Android2009. 7. 24. 18:27

이글은, 구글 안드로이드 상의 Java 애플리케이션에서 native c/c++ 모듈을 Call 할 수 있는 가에
대한 것이다. 결론적으로 말하면 쉽지 않을 것 같지만 불가능 하지는 않을 듯 하다.
직접 JNI를 통해 결과를 검증해 보지는 않았지만, 대략적인 방법들과 문제점들을 정리해 보고자
한다.
 
 
1. JNI란 무엇인가? - JNI example 중심으로
 
This section presents the ReadFile example program. This example shows how you can use 
the Java Native Interface (JNI) to invoke a native method that makes C function calls to map a 
file into memory. 

About the Example 

You can call code written in any programming language from a program written in the Java 
language by declaring a native Java method, loading the library that contains the native code, 
and then calling the native method. The ReadFile source code below does exactly this. 
However, successfully running the program requires a few additional steps beyond compiling 
the Java language source file. After you compile, but before you run the example, you have to 
generate a header file. The native code implements the function defintions contained in the 
generated header file and implements the business logic as well. The following sections walk 
through all the steps.
import java.util.*;
class ReadFile {
 //Native method declaration
   native byte[] loadFile(String name);
 //Load the library
   static {
     System.loadLibrary("nativelib");
   }
 
   public static void main(String args[]) {
      byte buf[];
 //Create class instance
      ReadFile mappedFile=new ReadFile();
 //Call native method to load ReadFile.java
      buf=mappedFile.loadFile("ReadFile.java");
 //Print contents of ReadFile.java
      for(int i=0;i<buf.length;i++) {
          System.out.print((char)buf[i]);
      }
   }
}
Native Method Declaration 

The native declaration provides the bridge to run the native function in the Java virtual machine.
In this example, the loadFile function maps onto a C function called Java_ReadFile_loadFile.
The function implementation accepts a String that represents a file name and returns the 
contents of that file in the byte array.
native byte[] loadFile(String name); Load the Library
The library containing the native code implementation is loaded by a call to System.loadLibrary().
Placing this call in a static initializer ensures this library is only loaded once per class.
The library can be loaded outside of the static block if your application requires it. You might 
need to configure your environment so the loadLibrary method can find your native code library.
static { 
     System.loadLibrary("nativelib"); 
}
Compile the Program
 
To compile the program, just run the javac compiler command as you normally would: 
javac ReadFile.java
Next, you need to generate a header file with the native method declaration and implement the 
native method to call the C functions for loading and reading a file. 

Generate the Header File 

To generate a a header file, run the javah command on the ReadFile class. In this example, 
the generated header file is named ReadFile.h. It provides a method signature that you have 
to use when you implement the loadfile native function.
javah -jni ReadFile
Note: When running javah on your own classes, be sure to use the fully-qualified class name.
Method Signature 

The ReadFile.h header file defines the interface to map the Java language method to the native
C function. It uses a method signature to map the arguments and return value of the Java
language mappedfile.loadFile method to the loadFile native method in the nativelib library.
Here is the loadFile native method mapping (method signature):
/* * Class: ReadFile * Method: loadFile * Signature: (Ljava/lang/String;)[B */ 
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile (JNIEnv *, jobject, jstring);
The method signature parameters function as follows:
JNIEnv *: A pointer to the JNI environment. This pointer is a handle to the current thread 
in the Java virtual machine, and contains mapping and other hosuekeeping information. 
jobject: A reference to the method that called this native code. If the calling method is 
static, this parameter would be type jclass instead of jobject. 
jstring: The parameter supplied to the native method. In this example, it is the name of 
the file to be read. 
 
Implement the Native Method
 
In this native C source file, the loadFile definition is a copy and paste of the C declaration 
contained in ReadFile.h. The definition is followed by the native method implementation. 
JNI provides a mapping for both C and C++ by default.
#include <jni.h>
#include <sys/types.h>
#include <sys/
ipc.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
JNIEXPORT jbyteArray JNICALL Java_ReadFile_loadFile
  (JNIEnv * env, jobject jobj, jstring name) {
    caddr_t m;
    jbyteArray jb;
    jboolean iscopy;
    struct stat finfo;
    const char *mfile = (*env)->GetStringUTFChars(
                env, name, &iscopy);
    int fd = open(mfile, O_RDONLY);
    if (fd == -1) {
      printf("Could not open %sn", mfile);
    }
    lstat(mfile, &finfo);
    m = mmap((caddr_t) 0, finfo.st_size,
                PROT_READ, MAP_PRIVATE, fd, 0);
    if (m == (caddr_t)-1) {
      printf("Could not mmap %sn", mfile);
      return(0);
    }
    jb=(*env)->NewByteArray(env, finfo.st_size);
    (*env)->SetByteArrayRegion(env, jb, 0, 
 finfo.st_size, (jbyte *)m);
    close(fd);
    (*env)->ReleaseStringUTFChars(env, name, mfile);
    return (jb);
}
 
You can approach calling an existing C function instead of implementing one, in one of two ways: 
Map the name generated by JNI to the existing C function name. The Language Issues 
section shows how to map between Xbase database functions and Java language code 
Use the shared stubs code available from the JNI page on the java.sun.com web site.

Compile the Dynamic or Shared Object Library 

The library needs to be compiled as a dynamic or shared object library so it can be loaded at 
runtime. Static or archive libraries are compiled into an executable and cannot be loaded at 
runtime. The shared object or dynamic library for the loadFile example is compiled as follows:
Gnu C/Linux:
gcc  -o libnativelib.so -shared -Wl,-soname,libnative.so  
     -I/export/home/jdk1.2/include 
     -I/export/home/jdk1.2/include/linux nativelib.c  
     -static -lc
Gnu C++/Linux with Xbase
g++ -o libdbmaplib.so -shared -Wl,-soname,libdbmap.so  
    -I/export/home/jdk1.2/include 
    -I/export/home/jdk1.2/include/linux 
    dbmaplib.cc -static -lc -lxbase

Win32/WinNT/Win2000
cl -Ic:/jdk1.2/include 
   -Ic:/jdk1.2/include/win32 
   -LD nativelib.c -Felibnative.dll

Run the Example
 
To run the example, the Java virtual machine needs to be able to find the native library. To do this, 
set the library path to the current directory as follows:

Unix or Linux: LD_LIBRARY_PATH=`pwd` export LD_LIBRARY_PATH
Windows NT/2000/95: set PATH=%path%;.

With the library path properly specified for your platform, invoke the program as you normally would 
with the interpreter command:
 java ReadFile
 
 
2. 구글 안드로이드 상에서 JNI 지원 API

아래의 두가지 함수에 대한 안드로이드 상에서의 특별한 제약이 없다면 JNI 을 지원하기 위한 최소한의
조건은 만족된 듯하다
The core functional component is the use of the 'native' keyword and the bindings to the 
java.lang.System.loadLibary() API, which, does seem to be documented within the android reference:
 
java.lang.System
java.lang 
public final class
java.lang.System
java.lang.Object  
       java.lang.System 

Class System provides a standard place for programs to find system related information. 
All System API is static.
static  void  load(String pathName) : Loads the specified file as a dynamic library. 
static  void  loadLibrary(String libName) : Loads and links the library specified by the argument.

3. Dynamic & Shard Object Library 만들기
 
the question here is how to build a native .so (shared library), but we can refer to the JNI example 
above for instructions. we have also been informed, through this list that you can build native code 
on the device, but without UI bindings:
 
이 부분 중, 가장 어렵게 생각되는 부분은 Cross Compiler를 통해 shard object library를 만들 수 있느냐 일듯.
현재 안드로이드의 /system/lib 아래에는 안드로이드 Library들이 .so 형태로 존재함.
Posted by 삼스
Android/App개발2009. 5. 12. 10:46
1. ARM GNU/Linux Target Platform 용 Cross-compiler 설치
아래의 사이트에 접속하여 Cross-compiler 를 설치한다.
http://www.codesourcery.com/gnu_toolchains/arm/download.html

2. 간단한 helloandroid.c 를 작성한다.

#include <stdio.h>
 
int main(int argc, char** argv)
{
        printf("Hello Android !n");
        return 0;
}
3. Cross Compile을 통해 ARM GNU/Linux 용 Application을 만든다.
> arm-none-linux-gnueabi-gcc -static helloandroid.c -o helloandroid
(주의: -static option을 필요 사용할 것.)
4. Android Device에 해당 애플리케이션을 Upload 한 후, 실행한다. > adb push helloandroid data/helloandroid
> adb shell
# chmod 755 data/helloandroid
# data/helloandroid
Hello Android ! <- 실행 결과
#
Posted by 삼스
Android/App개발2009. 4. 23. 11:21
http://www.kandroid.org/board/board.php?board=androidsource&command=body&no=17

안드로이드 소스코드내의 아래의 위치에 존재하는 문서를 참고하시면 안드로이드 Native 라이브러리 및 
해당 라이브러리를 static, 또는 dynamic하게 linking하는 Native 애플리케이션을 제작하는 법에 대한 설명이
존재합니다. 
 
~/mydroid/development/pdk/ndk/README
 
위의 문서를 참조하여, 아래와 같은 절차를 거치시면 다양한 native lib. 및 application을 제작 테스트 해 볼 수
있습니다.

 
1. 가장 먼저 해야할 일은 다음과 아래의 URL에 있는 문서에서의 다음의 두 절차, 
    즉 6번까지의 작업과 8번의 goldfish 부분에 대한 build를 마무리 해야 함.
 
   http://www.kandroid.org/board/board.php?board=androidsource&command=body&no=4
 
  
중략...
 
   6. 안드로이드 빌드 하기
       $ make 
 
   8. Kernel 별도로 빌드하기
        - goldfish
 
        $ cd ~/mydroid/kernel
        $ make goldfish_defconfig ARCH=arm
        $ make ARCH=arm CROSS_COMPILE=../prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi- 


 
2. ~mydroid/development/pdk/ndk/config/config.mk 를 다음과 같이 수정한다.
 
<android_full_src_path> 는 각자의 host pc에 설치된 android full source의 root directory 를 지정하면 됩니다.
 
CC         := ~/mydroid/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-gcc
AR         := ~/mydroid/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/arm-eabi-ar
INC        := -I<android_full_src_path>/bionic/libc/arch-arm/include 
-I<android_full_src_path>/bionic/libc/include 
-I<android_full_src_path>/kernel/include 
-I<android_full_src_path>/bionic/libm/include 
-I<android_full_src_path>/bionic/libm/include/arm 
-I<android_full_src_path>/bionic/libstdc++/include
LINK       := -nostdlib -Bdynamic 
     -Wl,-T,<android_full_src_path>/build/core/armelf.x 
     -Wl,-dynamic-linker,/system/bin/linker 
     -Wl,-z,nocopyreloc 
     -L<android_full_src_path>/out/target/product/generic/obj/lib 
     -Wl,-rpath-link=<android_full_src_path>/out/target/product/generic/obj/lib 
     <android_full_src_path>/out/target/product/generic/obj/lib/crtbegin_dynamic.o
POSTLINK := <android_full_src_path>/out/target/product/generic/obj/lib/crtend_android.o
%.o: %.cpp
        $(CC) $(CFLAGS) -fno-exceptions -fno-rtti $(INC) -o $@ -c $<
%.o: %.c
        $(CC) $(CFLAGS) $(INC) -o $@ -c $<

 
 
3. ~mydroid/development/pdk/ndk/sample/Makefile.lib 를 다음과 같이 수정한다.
 

NDK_BASE   := ..
include $(NDK_BASE)/config/config.mk
SOURCES    := hellolibrary.c
OBJECTS    := $(SOURCES:.c=.o)
LIBS       := -lc -lm
ALIB       := <android_full_src_path>/prebuilt/linux-x86/toolchain/arm-eabi-4.2.1/bin/../lib/gcc/arm-eabi/4.2.1/interwork/libgcc.a
all: sharedlib staticlib
# Using shared and static suffixes as these are going in the same directory;
# typically you would not do this as you would make only one version,
# but if we don't we'll screw up the linking because of linker defaults.
staticlib: $(OBJECTS)
        $(AR) -cr libhello-static.a $(OBJECTS)
sharedlib: hellolibrary-shared.o
        $(CC) -nostdlib -Wl,-soname,libhello-shared.so -Wl,-shared,-Bsymbolic -L<android_full_src_path>/out/target/product/generic/obj/lib $^ $(LIBS) -o libhello-shared.so -Wl,--no-undefined $(ALIB)
hellolibrary-shared.o: hellolibrary.c
        $(CC) -c -fpic $(INC) -o $@ $^
clean:
        rm -rf *.o libhello-static.a libhello-shared.so

 
4. native library 및 application 제작 및 테스트
 
먼저 ~/mydroid/development/pdk/ndk/로 cd 하신후 부터 아래의 절차를 수행하면 됩니다.
하지만, 아래의 과정을 수행하는 과정에서 shared library를 테스트 하기 위해서는
아래의 예제에 있는 libhello-shared.so 가 반드시 /system/lib 아래에 설치되어야 합니다.
하지만, 현재의 emulator는 /system partition이 ro 모드로 되어 있기 때문에 설치가 불가능합니다.
/system partition을 rw 모드로 바꾸는 작업이 필요한데..번거로울까봐 이곳에서는 가장 간단한 방법
(아래의 0번)
제시하도록 하겠습니다.
 
0번) emulator의 /system partition을 ro -> rw 로 바꾸는 방법. (필수)
 
    - 아래의 새로운 ramdisk.img 를 다운로드 한다.
       
 ramdisk.img(136.2KB)
   
    - 에뮬레이터에 존재하는 ramdisk.img를 backup한다.
      예) android-sdk-windows-1.0_r2 oolslibimages  아래 존재하는 
           ramdisk.img 를 ramdisk.img.orig 로 rename 한다.
    
    - 위에서 다운받은 새로운 ramdisk.img 를 위의 위치로 복사한다.
 
    - emulator를 실행한다.
        
 
1) 간단한 hello world 샘플 애플리케이션 제작
 
  cd ../sample
  make clean
  make
 
  adb push hello system/app
  adb shell
  # cd system/app
  # chmod 755 hello
  # ./hello
  Hello from the NDK; no user libraries.
  # exit
 
2) c++ binary hello_cpp.cpp 애플리케이션 제작 
 
  make -f Makefile.hello_cpp clean
  make -f Makefile.hello_cpp hello_cpp
 
  adb push hello_cpp system/app
  adb shell
  # cd system/app
  # chmod 755 hello_cpp
  # ./hello_cpp
  C++ example printing message: Hello world!
  # exit
 
4) shared library 제작 및 해당 library를 제작하는 사용하는 애플리케이션 제작
 
  make -f Makefile.lib clean
  make -f Makefile.lib sharedlib
  make -f Makefile.uselib clean
  make -f Makefile.uselib use_hellolibrary-so
 
  adb push libhello-shared.so system/lib
  adb push use_hellolibrary-so /system/app
  adb shell
  # cd system/app
  # chmod 755 use_hellolibrary-so
  # ./use_hellolibrary-so
  Library printing message: Hello from the NDK.
  # exit
 
5) static library 제작 및 해당 library를 static으로 링크한 애플리케이션 제작
 
  make -f Makefile.lib clean
  make -f Makefile.lib staticlib
  make -f Makefile.uselib clean
  make -f Makefile.uselib use_hellolibrary-a
 
  adb push use_hellolibrary-a system/app
  adb shell
  # cd system/app
  # chmod 755 use_hellolibrary-a
  # ./use_hellolibrary-a
  Library printing message: Hello from the NDK.
  # exit
Posted by 삼스
확장스크립트를 이용한 flash script를 이용한 native code의 통신방법에 대해 정리하겠다.
 
세가지의 연동관련 이슈가 있다.

1) Flash단에서 Native단으로 이벤트 전달
2) Flash단에서 Native단의 상태 얻기
3) Native단의 이벤트 발생시 Flash단에서 그 이벤트를 전달받기

각 이슈별로 Flash단과 Native단관점에서 하는 일은 아래와 같다.

1) Flash단에서 Native단으로 이벤트 전달

    Flash -> ext_fscommand2(arg1, arg2, arg3) 형태로 argument로 구분하여 이벤트를 전달할 수 있다.
    Native -> engine에서 제공하는 callback함수 int onFlashEngineStatus(int stat, void *arg1, void *arg2, void **arg3)로 처리 가능
int onFlashEngineStatus(
int stat,   // status
void *arg1,  // 구분자
void *arg2,  // arg개수
void **arg3) // arg리스트

2) Flash단에서 Native단의 상태 얻기

   Flash단 -> 기본적으로 이벤트 전달할 때와 동일하며 새번째 arg는 script에서 정의된 var값이 넘어간다. 
                   var var1;
                   ext_fscommand(arg1, arg2, "var1")이 호출되면 var1변수값이 변경된다.
   Native단 -> onFlashEngineStatus()에서 arg1과 arg2로 구분하여 해당 이벤트를 전달 받을 수 있으며 이 때 s3wi_setFlashVar()로 값을 채워넣을 수 있다.

3) Native단의 이벤트 발생시 Flash단에서 그 이벤트를 전달받기

   Flash단 -> Listener를 등록하여 Native단의 이벤트를 전달 받을 수 있다.
   messageEvent = new Object();
messageEvent.onEvent = function() {
   if (arguments[0] == "STAT_EVENT_BATTERY")
   {
                 //배터리 레벨표시 아이콘 프레임 변경, arguments[1] = 0~4 까지
                 battery_mc.gotoAndStop(arguments[1])
   }   
else
   {
                 debug_txt.text = "Unkown Event Error";
   }
};
ExtendedEvents.OnM35TPlatformEvent.addListener(messageEvent); // OnM35TPlatformEvent는 native에서 전송시 첫번째 파라메터


  Native단 -> 내부에서 flash단으로 이벤트를 보내야 하는 경우 s3wi_SendExtendedEvent()로 이벤트를 보낼 수 있다.
                  flash_SendExtendedEvent(hFlash, argc, argv))



Posted by 삼스