파이어몽키에서 안드로이드 외부 라이브러리(jar 파일) 이용(Import jar)

2014.02.05 18:20

 안녕하세요. 험프리 김현수 입니다. 그간 많은 분들이 질문주셨던 내용인데요. 이제야 정리되어 소개해 드립니다.


 안드로이드에서 외부라이브러리 사용하려면 so, jar 파일을이용하는 2가지 방법이 있습니다. 이번에 소개할 내용은 jar 파일을 이용해 외부 라이브러리를 활용하는 내용입니다.

 간단하게 소개를 먼저하면, 필요한 jar 파일을 포함한 classes.dex 파일을 생성 후 배포 시 앞에서 생성한 classes.dex 파일을 배포하여 소스상에서 jar파일에 포함된 자바클래스를 활용하는 방식입니다.


아래의 순서대로 진행되어야 하며 순서에 맞춰 설명하겠습니다.


  1. 사용자 classes.dex 파일 생성
    • APK 구조와 jar 파일의 위치
    • java 소스파일 또는 jar 파일 준비
    • 빌드
      • (옵션) java 소스파일 컴파일
      • jar 파일 생성
      • jar 파일을 dex 파일로 변환
      • dex 파일 병합
  2. 배포파일 관리
  3. Java Interface 파일 생성
  4. 구현 및 확인

프로젝트 구성

  • JarBridge
    • [Java]
      • [output] - 빌드 시 자동생성
        • [dex] classes.dex - 빌드 후 생성
      • [src] kr\co\devgear\test\Foo.java
      • build.bat
    • JarBridgeDemo.dpr 등 - 프로젝트 및 폼 파일
    • JavaFoo.pas -Java Interface 정의 유닛

사용자 classes.dex 파일 생성

▶ APK 구조와 jar파일의 위치

apk 파일을 압축유틸리티로 열면 위와 같은 구조로 되어 있습니다. 그 중 jar 파일을 포함할 파일은 classes.dex 파일입니다. 여러분의 jar 파일은 아래의 빌드단계를 거처 classes.dex 파일로 변환됩니다.


dex 파일이란?

더보기

▶ Java 소스파일(또는 jar 파일) 준비

 저는 인터페이스 테스트를 위해 아래의 자바코드를 간단하게 작성했습니다. (워낙 간단하니 따로 설명드리지 않겠습니다.)

 해당 자바코드는 아래의 빌드 단계에서 jar파일을 생성합니다. 만약, 다른 jar 파일을 이용할 경우 아래의 코드는 작성하지 않아도 됩니다.

package kr.co.devgear.test;

import android.app.Activity;
import android.widget.Toast;

public class Foo
{
    public void printMsg(final Activity activity)
    {
         activity.runOnUiThread(new Runnable()
         {
               @Override
                public void run()
                {
                    Toast.makeText(activity.getApplicationContext(), "Hello world from a Java .jar library", Toast.LENGTH_SHORT).show();
                }
         });
    }

    public int getInteger(){
        return 99;
    }

    public String getString(){
        return "Foo string";
    }
}


▶ 빌드

 준비된 자바 소스파일을 jar로 만들어 새로운 classes.dex에 포함하는 작업을 위해 아래의 배치파일을 사용합니다. (빌드파일은 Brian long(http://blog.blong.com)이 공개한 빌드파일을 참고해서 수정 되었습니다. 많은 도움이 되었습니다.)


빌드파일은 아래의 내용을 진행합니다.

  1. Compiling the Java source file - 자바소스를 컴파일
  2. Creating jar containing the new classes - jar 파일을 만들고
  3. Converting from jar to dex - jar 파일을 dex 파일로 변환 후
  4. Merging dex files - 3에서 생성된 dex 파일을 Embarcadero의 dex파일과 병합
만약, 보유한 jar파일을 이용하는 경우 3, 4 단계만 진행하도록 배치파일을 수정 후 사용바랍니다.

@echo off

setlocal

if x%ANDROID% == x set ANDROID="C:\Users\Public\Documents\RAD Studio\12.0\PlatformSDKs\adt-bundle-windows-x86-20130522\sdk"

set SRC_PATH=src\kr\co\devgear\test\Foo.java
set LAST_DOMAIN=kr
set ANDROID_PLATFORM=%ANDROID%\platforms\android-17
set DX_PATH=%ANDROID%\build-tools\android-4.2.2
set DX_LIB=%DX_PATH%\lib
set EMBO_DEX="C:\Program Files (x86)\Embarcadero\RAD Studio\12.0\lib\android\debug\classes.dex"
set PROJ_DIR=%CD%
set VERBOSE=0

echo.
echo Compiling the Java source file
echo.
mkdir output\classes 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=-verbose
javac %VERBOSE_FLAG% -Xlint:all -classpath %ANDROID_PLATFORM%\android.jar -d output\classes -source 1.6 -target 1.6 %SRC_PATH%


echo.
echo Creating jar containing the new classes
echo.
mkdir output\jar 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=v
jar c%VERBOSE_FLAG%f output\jar\test_classes.jar -C output\classes %LAST_DOMAIN%


echo.
echo Converting from jar to dex...
echo.
mkdir output\dex 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=--verbose
call %DX_PATH%\dx --dex %VERBOSE_FLAG% --output=%PROJ_DIR%\output\dex\test_classes.dex --positions=lines %PROJ_DIR%\output\jar\test_classes.jar

echo.
echo Merging dex files
echo.
java -cp %DX_LIB%\dx.jar com.android.dx.merge.DexMerger  %PROJ_DIR%\output\dex\classes.dex %PROJ_DIR%\output\dex\test_classes.dex %EMBO_DEX%

echo Tidying up
echo.
del output\dex\test_classes.dex
del output\jar\test_classes.jar
rmdir output\jar

echo.
echo Now we have the end result, which is output\dex\classes.dex

:Exit

pause

endlocal

배치파일의 경로는 RAD Studio XE5를 기준으로 설정되어 있어, 기본 경로로 설치하셨을 경우 아래의 2가지 변수외에는 그대로 사용하실 수 있습니다.

  • SRC_PATH : java 소스파일의 상대경로로 수정
  • LAST_DOMAIN : 도멘인의 마지막을 입력(e.g. kr, com, net 등)


위의 배치파일을 실행하면 output\dex\classes.dex 파일이 생성됩니다. 아래의 배포파일 관리에서 해당 파일이 사용됩니다.

배포파일 관리

안드로이드 모바일 프로젝트를 생성하면 배포파일 목록에 엠바카데로에서 제공하는 classes.dex이 등록되는데, 우리는 jar파일이 포함된 classes.dex를 배포해야 합니다.



위의 그림과 같이 기본 지정된 classes.dex파일을 선택해제 하시고, Add files 버튼을 이용해 위에서 만든 classes.dex 파일을 추가하고 아래와 같이 속성을 변경합니다.

classes.dex

  • Platform : Android
  • Remote Path : classes\

Java Interface 파일생성

위의 자바파일(Foo.java)을 참고해 아래의 JavaFoo 유닛을 만들었습니다.

자바파일 구조

  • package kr.co.devgear.test;
  • public class Foo
    • public void printMsg(final Activity activity)
    • public int getInteger()
    • public String getString()


unit JavaFoo;

interface

uses
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.APP;

type
  JFoo = interface;

  JFooClass = interface(JObjectClass)
   ['{94F03A5A-B62B-401E-BE8D-2A96B77FA542}']
   function init: JFoo;
  end;

  [JavaSignature('kr/co/devgear/test/Foo')]
  JFoo = interface(JObject)
    ['{66CA87AA-1A6B-4039-A4E0-EB771BC2F4A5}']
    procedure printMsg(const AActivyty: JActivity);
    function getInteger: Integer; cdecl;
    function getString: JString; cdecl;
  end;

  TJFoo = class(TJavaGenericImport<JFooClass, JFoo>) end;

implementation

end.

위의 과정이 어렵다면 Android2DelphiImport tool(유료: 0.25 bitcoin)을 이용할 수 있습니다.

위의 링크의 설명은 어떤 jar파일도 pas파일로 변환해준다는 내용이 있습니다.
(비트코인으로 결재를 하네요^^)


파이어몽키 - 안드로이드 SDK 전체 랩핑(Wrapping) 파일

  • http://blog.hjf.pe.kr/185

구현 및 확인

아래와 같이 JavaFoo 파일을 uses에 넣고 버튼을 누르면 JFoo 생성 후 자바 메소드를 호출하면 응답을 받을 수 있습니다.

uses
  JavaFoo,
  Androidapi.JNI.JavaTypes,
  FMX.Helpers.Android;

{$R *.fmx}

procedure TForm1.Button2Click(Sender: TObject);
var
  Foo: JFoo;
begin
  Foo := TJFoo.JavaClass.init;

  Edit1.Text := IntToStr(Foo.getInteger);
  Edit2.Text := JStringToString(Foo.getString);

  Foo.printMsg(SharedActivity);
end;

아래는 넥서스 7에서 실행한 화면입니다.


참고



관련 글


저작자 표시 비영리 동일 조건 변경 허락
신고

험프리.김현수 파이어몽키 , ,

  1. Blog Icon
    김동익

    JavaFoo unit에 있는 두 개의 인터페이스 ID값은 어떻게 생성이 된 건가요? 생성규칙이 있는 건가요?

  2. 임의의 GUID 값을 넣은것이구요.
    델파이 상에서는 Ctrl + Shift + G를 누르면 임의의 GUID 값을 생성 할 수 있습니다.

  3. Blog Icon
    차칸

    안녕하세요. xe5에서 jar파일을 쓰는법을 검색하다가 여기까지 왔는데요. 질문해도 괜찬나요?
    글대로 따라해서 작동은 됬는데 기존에 가지고있는 jar파일에서 변수타입이 hashtable<string,string> 일경우
    xe5에서 인터페이스에 함수를 만들때 어떻게 해줘야하나요?
    예를들면 java함수 String이면 xe5에서는 JString으로 가능한데 hashtable은 검색하다 해결방법을 못찾아서 문의드립니다.

  4. API를 사용하실 경우 반드시 모든 것을 안드로이드 샘플과 똑같이 하실 필요는 없습니다. 자바클래스 선언만 델파이로 컨버팅 하신 후에 HashTable같은 목록은 델파이의 Collection을 사용하셔도 좋구요 전통적인 TList를 사용하셔도 됩니다.(http://docwiki.embarcadero.com/CodeExamples/XE5/en/Generics_Collections_TList_(Delphi))

    꼭 HashTable을 사용하셔야 한다면, HashTable을 먼저 컨버팅하시고 그 컨버팅한 객체를 사용하실 수 있습니다.(http://developer.android.com/reference/java/util/Hashtable.html)

    JString 등은 이미 델파이에서 컨버팅한 자료형입니다.

  5. Blog Icon
    차돌이

    좋은 자료 감사합니다. 위의 자료를 참고하여 classes.dex 파일을 수정한후 Deployment 에서 변경된 파일로 변경후 실제 안드로이드 폰에 올릴경우 아래와 같은 에러메시지가 발생합니다. 혹시 관련해서 아시는 방법이 있으신가요? install_failed_uid_changed

  6. 정확한 답변은 아니겠지만 의심가는것이 있어 답변 드려봅니다.

    혹시 다른 버전의 델파이에서 동일한 프로젝트 명으로 배포된 apk가 있지 않으신가요?
    (혹시 모르니)프로젝트 명 변경 후 시도해 보세요.

    또는 JFooClass, JFoo의 GUID(['{94F03A5A-B62B-401E-BE8D-2A96B77FA542}'])를 재 생성(Ctrl + Alt + G) 해서 시도해 보세요.

  7. Blog Icon
    차돌이

    일단은 프로젝트 명을 변경하여 디바이스에 올라가는것을 확인하였습니다. 감사합니다.

  8. Blog Icon
    홍박

    먼저 좋은자료 ㄳ드립니다.
    "배포파일 관리 " 메뉴는 어디에 있는가요? 아무리찾아도 안보이는군요 사용 IDE 는 XE5 입니다. 답변ㄳ드립니다.

  9. Project > Deployment 메뉴를 선택하시면 됩니다.

  10. Blog Icon
    홍박

    ㄳ합니다~ ^^

  11. Blog Icon
    홍박

    작업하다가 궁금한점이 있어서 질문드립니다.
    PROGRAMFILES~ Embarcadero\RAD Studio\12.0\lib\android\debug\classes.dex 이 경로에 있는 파일이
    완전히 새로운파일로 교체되는것인가요?
    . 혹시 교체가 된다면 다음부터 1,2,3,4 과정없이 선언만으로 다른프로젝트에 사용 가능한것인지?
    알고싶습니다 무더위가 기승을 부리네요 수고하세요~

  12. 결과적으로 배포되는 classes.dex 파일은 엠바카데로에서 기본으로 제공하는 classes.dex를 포함하도록 배치파일에서 구성됩니다.

    만약 동일한 외부 jar파일을 사용하실것이라면 동일한 classes.dex파일을 다양한 앱에서 사용해도 됩니다.

  13. Blog Icon
    홍박

    친절한 답변 감사합니다 다른경로에 xe 를 설치해서 배치파일을 그대로쓰면 설치된게 아작날까바 아직 실행은 못해봤습니다 아직 자바초보라 자바컴파일 하는것도 많이 어렵네요 자주 들러겠습니다 수고하세요

  14. 네. 감사합니다.
    다양한 시도해 보시고 문제가 되거나 공유할 만한 거리가 있으면
    댓글로 공유 부탁드려요^^

  15. Blog Icon
    나들이

    저어 도움을 부탁드립니다.
    예제를 따라 해보니 잘됩니다. 근데 이렇게 한 후 seattle 에서 안드로이드 서비스를 추가하려는데 잘되지가 않더군요. ㅜㅜ
    시애틀에 안드로이드 서비스를 추가하니까 class 가 세개가 자동으로 classas.dex에 들어가는 것 같은데 뭘 어떻게 해야할지 모르겠습니다. 한 수 부탁드립니다.