static library, dynamic library
라이브러리란 무엇인가? 우선 라이브러리는 나의 혹은 타인의 결과물을 패키징해서 쓰고 싶을때 사용하는 것이다.
상황이 많기에 이에 따른 라이브러리의 종류도 많고 그에 따른 확장자도 많다. 이를 천천히 설명해 보겠다.
크게는 static library 와 dynamic library 로 나뉜다.
TODO) 자세하게 들어가면 Compiler개념 JVM 개념과 opcode operand 개념 등 다룰게 많기에 간략하게 설명을 해보겠다.
어떤 코드가 실행되기 전에 컴퓨터가 알아듣게 만들려면 '컴파일' 이라는 단계가 필요한
static library 는 '미리 컴파일'을 해두는 놈이고
dynamic library 는 '실행시 그때그때 컴파일'을 하는' 놈이다.
자 어떤 느낌이 드는가.
static 은 처음이 무겁겠지만 빌드 이후로 (참고로 빌드는 컴파일을 포함하는 코드 실행을 위한 사전작업이라고 생각하면 된다) 실행이 편하다.
dynamic 은 빌드가 간편하겠지만 각각의 실행시에 static 보다는 시간이 더 든다.
사용되는 확장자는 다음과 같다.
.a 는 static library. .so 는 dynamic library
.lib 은 static library. .dll은 dynamic library.
Cmake, 자동 빌드 툴
외부 native 를 빌드하기 위해서 cmake 라는 확장성 높은 라이브러리 build tool 을 사용했습니다
java 로 짜여진 android 에서 c 로 짜여진 qemu과 통신하기 위해서 c 코드를 라이브러리로 말아서 사용할 필요성이 생겼고, 따라서 사용하게 되었습니다.
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
build 시 자동으로 요청한 내용을 빌드할 수 있게 해주도록 build.gradle 에 포함을 시켰습니다
cmake 는 cmakelists.txt 라는 상위 폴더가 지정한 대로 요청한 라이브러리를 말아줍니다
cmake_minimum_required(VERSION 3.4.1)
if (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "armeabi-v7a")
set(OUT_DIR ${CMAKE_SOURCE_DIR}/../../보안)
elseif (${CMAKE_ANDROID_ARCH_ABI} STREQUAL "arm64-v8a")
set(OUT_DIR ${CMAKE_SOURCE_DIR}/../../보안)
endif()
function(add_prebuilt_static_lib name)
add_library(${name} STATIC IMPORTED)
set_target_properties(${name} PROPERTIES IMPORTED_LOCATION ${OUT_DIR}/lib/${name}.a)
endfunction()
add_prebuilt_static_lib(libcrypto)
add_prebuilt_static_lib(libssl)
add_prebuilt_static_lib(libjpeg)
add_prebuilt_static_lib(libopus)
add_prebuilt_static_lib(libavutil)
add_prebuilt_static_lib(libavcodec)
add_prebuilt_static_lib(libswscale)
add_prebuilt_static_lib(libswresample)
add_prebuilt_static_lib(libvncclient)
add_library(aav_client SHARED src/main/cpp/aav_client.c)
target_include_directories(aav_client PRIVATE ${OUT_DIR}/include)
target_link_libraries(aav_client libvncclient libopus libjpeg libavcodec libswresample libswscale libavutil
libssl libcrypto z jnigraphics log m
-Wl,-version-script -Wl,${CMAKE_SOURCE_DIR}/src/main/cpp/aav_client.ver)
우리가 오픈소스로 사용하는 여러 라이브러리를 add_prebuild_static_lib 으로 넣어주고
우리가 생성한 c 함수가 포함된 모듈을 add_library 로 포함시켜서
.so 파일로 최종적으로 말아줍니다
#!/bin/bash
./gradlew assembleRelease
cp ./app/build/intermediates/cmake/release/obj/armeabi-v7a/libaav_client.so ...
copy directory 는 보안 문제로 인해 자세히 적지 않겠습니다
해당 .sh 파일을 실행하면 release 로 cmake 가 build 되고 생성된 파일을 원하는 디렉토리에 옮겨서 사용하도록 합니다
android, aar 파일로 리소스 효율화
어플 프로젝트가 커질 경우 res 파일에 담긴 이미지, 스트링 등이 너무 많아지는 문제가 생깁니다
이를 .aar 로 말아서 /app/libs 안에 .aar 로 말아서 넣는다면 크기도 작아지고 더 효율적으로 접근하고 관리할 수 있게 됩니다.
최상위 디렉토리에 {프로젝트이름}_resource 이름의 폴더를 추가해주고 /src/main/res 안에 말고싶은 drawable… 등의 폴더를 추가해줍니다
gradle 에 추가된 {프로젝트 명}_resource 로 빌드를 하면 build 폴더 안에 리소스가 말린 .aar 폴더가 나오게 됩니다
이를 app/libs 안에 추가한 후 dependency 에 추가해서 사용하면 됩니다
등록한 aar 을 메인 프로젝트에서 사용하기 위해서는 build.gradle dependencies 에 다음을 추가해줘야 합니다
implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
자바 소켓통신 서버
구현은 마치 레고를 조립하는 것과 같습니다.
소켓 통신을 하기 위해 제일 기본적인 서버 - 클라이언트 구조 테스트코드부터 작성을 시작해 제품에 이식하는 과정을 진행했습니다
자바 소켓통신 서버의 테스트 코드를 작성해 보았습니다
package com.example.test_socket_server;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final byte[][] msg = {new byte[256]};
TextView textView = findViewById(R.id.text_receive);
Button start_server = findViewById(R.id.start_server);
Button msg_send = findViewById(R.id.msg_send);
Button msg_receive = findViewById(R.id.msg_receive);
Button stop_server = findViewById(R.id.stop_server);
ServerSocket serverSocket[] = {null};
Socket socket[] = {null, null};
InputStream IS[] = {null};
JSONObject json = new JSONObject();
try {
json.put("Command", "CreateNewUser");
json.put("User", "user");
} catch (JSONException e) {
json = null;
}
start_server.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(() -> {
try {
serverSocket[0] = new ServerSocket(8002);
System.out.println("socket, bind, listen");
socket[0] = serverSocket[0].accept();
System.out.println("accept");
IS[0] = socket[0].getInputStream();
} catch (IOException e) {}
}).start();
}
});
JSONObject finalJson = json;
msg_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String sendMsg = finalJson.toString();
new Thread(() -> {
try {
String serverIp = "127.0.0.1";
socket[1] = new Socket(serverIp, 8002);
byte[] arrayStream = sendMsg.getBytes("utf-8");
OutputStream OS = socket[1].getOutputStream();
OS.write(arrayStream);
} catch (IOException e) {}
}).start();
}
});
msg_receive.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(() -> {
String output = null;
try {
output = new String(msg[0], 0, IS[0].read(msg[0]), "UTF-8");
} catch (IOException e) { }
textView.setText(output);
}).start();
}
});
stop_server.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
IS[0].close();
serverSocket[0].close();
socket[0].close();
socket[1].close();
System.out.println("socket closed");
textView.setText("receive_text");
} catch (IOException e) {}
}
});
}
}
버튼은 크게 4개가 존재합니다 start_server, send_msg, receive_msg, stop_server
start_server 는 socket생성, port와 bind, listen으로 대기 상태까지 진행을 해 클라이언트 요청을 받을 준비를 합니다. 그리고 연결이 된 경우 반환된 socket으로 inputstream 까지 만들어줍니다.
send_msg 는 클라이언트와 연결된 다른 소켓으로 outputstream 을 만든 후 .write 를 합니다
receive_msg 는 start_server 에서 만든 inputstream 으로 .read 를 합니다
stop_server 는 연결된 stream 과 socket 을 닫아줍니다. socket 이 닫히면 stream 은 알아서 닫힙니다.
receiver thread 가 따로 구현되어있지 않아서 이를 버튼으로 구현하는 것이 생각보다 까다로웠습니다.
receiver 하나가 hashmap 에 계속 input 값을 받아주고 또 sender 하나가 그 받은 input 값을 보내주는 식으로 asynchronous I/O 를 진행하게 될 예정입니다
android-sdk 로 adb 로 에뮬레이터 접근
adb 로 안드로이드가 돌아가는 가상환경 virtual machine 에 직접 접근할 수 있다.
adb에 대해 정리한 글을 읽어보면 이해가 쉬울 것이다.
https://yeon-lee.tistory.com/115
[Android Studio] ADB, SDK
ADB란 핸드폰도 하나의 컴퓨터입니다. 앱을 짜려면 내 코드와 디렉토리들, 그리고 코드가 실시간으로 실행되는 모습을 한번에 볼 수 있는 '개발 환경' 이 구성되어 있어야 합니다. 안드로이드는 a
yeon-lee.tistory.com
여기서 흥미로운 점은 adb 안에서 adb 를 들어갈 수도 있다는 것이다!
안드로이드 안에서 가상화를 해야하는 즉 에뮬레이터를 돌려야 하는 제품의 특성 상 그런것도 가능한 것 같다..
'CS' 카테고리의 다른 글
[CS] HTTPS란 (0) | 2023.10.12 |
---|---|
[CS] machine code 와 byte code 의 차이 (0) | 2023.09.15 |
[CS] call back 이란, 동기식과 비동기식 (0) | 2023.03.14 |
[CS] 데스크톱 가상화란 (0) | 2023.03.07 |
[네트워크] 포트(port) 란 (2) | 2023.03.07 |