'2011/11/23'에 해당되는 글 4건

  1. 2011.11.23 안드로이드 JNI 개발 준비 #2 2
  2. 2011.11.23 ubuntu 패스워드 변경
  3. 2011.11.23 android ndk r7 버그
  4. 2011.11.23 안드로이드 JNI 개발 준비 #2


1. 안드로이드 JNI 로그 남기기

이클립스의 LogCat 에서 jni가 출력되는 것을 보고 싶을 때 유용한다.
jni의 native단에서 printf로 출력되는 것은 adb shell로만 볼 수 있다.


Android.mk에 다음을 추가한다.
LOCAL_LDLIBS := -llog

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := test
LOCAL_SRC_FILES := test.c
LOCAL_LDLIBS := -llog

include $(BUILD_SHARED_LIBRARY)






C 코드에 다음을 추가한다.

#include <android/log.h>

__android_log_print(ANDROID_LOG_DEBUG, "Tag_Name", "Message...");

레벨은 많다.
ANDROID_LOG_UNKNOWN
ANDROID_LOG_DEFAULT
ANDROID_LOG_VERBOSE
ANDROID_LOG_DEBUG
ANDROID_LOG_INFO
ANDROID_LOG_WARN
ANDROID_LOG_ERROR
ANDROID_LOG_FATAL
ANDROID_LOG_SILENT




#include <jni.h>
#include <android/log.h>
#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jint JNICALL Java_com_google_AddJniActivity_addJNI
  (JNIEnv *env, jobject thiz, jint num1, jint num2)
{
        __android_log_print(ANDROID_LOG_ERROR, "GOOGLE", "******Message...");
         return num1+num2;
}

#ifdef __cplusplus
}
#endif




logcat에 에러가 남는다.




2. 두 개의 모듈을 컴파일 해보기


java  ==  jni-c  이런 상황에서 jni-c 파일이 사용하는 라이브러리가 따로 있는 경우를 의미한다.
즉 jni에서 명시적으로 가지고 쓰는 경우를 의미한다.


first.h

#ifndef FIRST_H
#define FIRST_H

extern int first(int  x, int  y);

#endif /* FIRST_H */




first.c

#include <android/log.h>

int  first(int  x, int  y)
{
 __android_log_print(ANDROID_LOG_DEBUG, "google", "** first.c, first function ");
     return x + y;
}






jni 코드 : test.c

#include <jni.h>
#include <android/log.h>
#include "first.h"

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_google_TwoLibs
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_google_TwoLibs_add
  (JNIEnv *env, jobject thiz, jint x, jint y) {
 __android_log_print(ANDROID_LOG_DEBUG, "google", "*******Message...");
   return first(x, y);
}

#ifdef __cplusplus
}
#endif





이런 경우의 android make 파일은 다음과 같이 지정한다.

Android.mk

LOCAL_PATH := $(call my-dir)

## 1st 다음은 libwolib-first 모듈를 만들자
include $(CLEAR_VARS)

LOCAL_MODULE    := libtwolib-first
LOCAL_SRC_FILES := first.c
LOCAL_LDLIBS := -llog

include $(BUILD_STATIC_LIBRARY)

## 2nd 다음은 libwolib-second 모듈을 만들자

include $(CLEAR_VARS)

LOCAL_MODULE    := libtwolib-second
LOCAL_SRC_FILES := second.c
LOCAL_LDLIBS := -llog

LOCAL_STATIC_LIBRARIES := libtwolib-first

include $(BUILD_SHARED_LIBRARY)



obj/local/armeabi 디렉토리 밑에 보면, first.a 파일과 secod.so 파일이 있는지 확인가능하다.
static lib는 linux archive를 의미한다.

$  ls -al
libtwolib-first.a
libtwolib-second.so
objs

libs/eabi 디렉토리는 static library를 포함하는 so 파일들이 만들어져 있다.
libs/eabi/libtwolib-second.so 파일을 이클립스 프로젝트에서 복사해서 사용한다.


3. module을 export 하기

여러 모듈(c, header) 파일이 있을 때, 여러개의 so파일과 함께 jni가 make 파일을 이용하여 프로그래밍을 할 수 있다.


foo.h

#ifndef FOO_H
#define FOO_H

extern int  foo(int x);

#endif /* FOO_H */




foo.c


#include "foo.h"
#include <android/log.h>

/* FOO should be defined to '2' when building foo.c */
#ifndef FOO
#error FOO is not defined here !
#endif

# android.mk 파일에서 설정값에 parameter값을 넣어서 동작되는 지를 확인하는 코드

#if FOO != 2
#error FOO is incorrectly defined here !
#endif

#define  LOG_TAG    "libfoo"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

int  foo(int  x)
{
    LOGI("foo(%d) called !", x);
    return x+1;
}





bar.h


#ifndef BAR_H
#define BAR_H

/* FOO should be defined to '1' here with the magic of LOCAL_EXPORT_CFLAGS */
#ifndef FOO
#error FOO should be defined here !
#endif

#if FOO != 1
#error FOO is not correctly defined here !
#endif

extern int  bar(int  x);

#endif /* BAR_H */





bar.c

#include "bar.h"
#include <android/log.h>

#define LOG_TAG  "libbar"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

int bar(int x)
{
 LOGI("bar(%d) called!!", x);
 return foo(x)-1;
}




zoo.c


#include "bar.h"
#include <android/log.h>

int something(void)
{
 __android_log_print(ANDROID_LOG_INFO, "libzoo", "something() called!!");
 return bar(42);
}




test.c

#include <jni.h>

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jint JNICALL Java_com_google_ModuleExports_foo
  (JNIEnv *env, jobject thiz, jint val)
{
 return foo(val);
}

JNIEXPORT jint JNICALL Java_com_google_ModuleExports_bar
  (JNIEnv *env, jobject thiz, jint val)
{
 return bar(val);
}

JNIEXPORT jint JNICALL Java_com_google_ModuleExports_zoo
  (JNIEnv *env, jobject thiz, jint val)
{
 return something();
}

#ifdef __cplusplus
}
#endif




java파일

class ActivityTest .... {

    public native int  foo(int val);
    public native int  bar(int val);
    public native int  zoo(int val);
   
    static {
        System.loadLibrary("bar");
        System.loadLibrary("zoo");
    }

}


Android.mk

LOCAL_PATH := $(call my-dir)

# FOO 값을 2로 만들어서  foo.c을 컴파일하고, FOO값을 1로 셋팅한후 archive 파일을 만든다.
include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_CFLAGS := -DFOO=2
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/foo
LOCAL_EXPORT_CFLAGS := -DFOO=1
LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)

# bar.c 를 컴파일하고 static 파일이었던 foo.a 를 모아 bar.so 파일로 만든다.
include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar/bar.c
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/bar
LOCAL_STATIC_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)

# zoo 파일을 jni파일인 test.c 파일과 함께 컴파일하고, zoo.so 파일로 만든다.
include $(CLEAR_VARS)
LOCAL_MODULE := zoo
LOCAL_SRC_FILES := test.c zoo/zoo.c
LOCAL_SHARED_LIBRARIES := bar
include $(BUILD_SHARED_LIBRARY)



../../ndk-build 결과는 다음과 같은 so 파일이 생성된다.






 

Posted by '김용환'
,

원래 root 패스워드를 알 수 없어서 . 변경한다. sudo 쓰기 진짜 귀찮다...

$ sudo passwd root
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully

Posted by '김용환'
,

가장 따뜻한 11월 에 나온 ndk 7이다.

Linux 32/64-bit (x86) android-ndk-r7-linux-x86.tar.bz2


android nkd 7을 다운받아서 ndk 해보는데. 잘 안된다.  구글 검색해도 안나와서.. 좀 열받는다.

ndk 빌드하면 이렇게 아래로 나오는데..

/work/android-ndk-r7/samples/san-angeles# ../../ndk-build
/work/android-ndk-r7/prebuilt/linux-x86/bin/awk: 1: ELF : not found
/work/android-ndk-r7/prebuilt/linux-x86/bin/awk: 4: Syntax error: word unexpected (expecting ")")
Android NDK: Host 'awk' tool is outdated. Please define HOST_AWK to point to Gawk or Nawk !   
/work/android-ndk-r7/build/core/init.mk:258: *** Android NDK: Aborting.    .  Stop.



추적을 해보니. 어라. awk 문법에 맞는 것이 동작이 안된다.

/work/android-ndk-r7/samples/san-angeles# ../../build/awk/check-awk.awk
../../build/awk/check-awk.awk: line 22: BEGIN: command not found
../../build/awk/check-awk.awk: line 26: syntax error near unexpected token `s1,"world"'
../../build/awk/check-awk.awk: line 26: `    if (! match(s1,"world")) {'


awk도 최신 버전인데..

# apt-get install awk
Reading package lists... Done
Building dependency tree      
Reading state information... Done
Package awk is a virtual package provided by:
  original-awk 2010-05-23-1
  mawk 1.3.3-15ubuntu2
  gawk 1:3.1.7.dfsg-5

 




mk 파일보면서 android -ndk에 awk가 이상이 있나 확인해보았다.

32비트 x86 버전에서 prebuild/linux-x86/bin/awk 파일은 64 비트용이었다... 아하~

/work/android-ndk-r7/prebuilt/linux-x86/bin# file awk
awk: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, stripped


awk 파일을 지우고 gawk를 링크를 걸어준다.

/work/android-ndk-r7/prebuilt/linux-x86/bin# mv awk awk.old
/work/android-ndk-r7/prebuilt/linux-x86/bin# ln -s /usr/bin/gawk awk

그리고, 샘플 디렉토리 가서 테스트해보니. 오예~ 빌드 완료.

/work/android-ndk-r7/samples/hello-jni# ../../ndk-build
Gdbserver      : [arm-linux-androideabi-4.4.3] libs/armeabi/gdbserver
Gdbsetup       : libs/armeabi/gdb.setup
Compile thumb  : hello-jni <= hello-jni.c
SharedLibrary  : libhello-jni.so
Install        : libhello-jni.so => libs/armeabi/libhello-jni.so
Posted by '김용환'
,


http://developer.android.com/sdk/index.html 에 밑에 보면,
ndk 를 다운받을 수 있다.


mac, window(cygwin 이용), linux 버전있는데, linux로 설치해본다.


ndk7이 4.0을 포함한 최신 버전이지만, ubuntu10. 11에서 awk 관련해서 버그가 있다. 32비트에서만 이슈가 있음. 이 부분에 대한 해결방법은 여기에 있다.

나는 ndk 6b를 사용했지만, 위의 7도 동일하게 사용할 수 있다.


리눅스(ubuntu)에 android-ndk-r6b.tar.gz을 설치한다.
tar jxvf android-ndk-r6b.tar.gz

설치는 완료했다.

이제 코드쪽을 본다.
이클립스에서 클래스 하나를 생성하고, jni 함수를 하나를 만들고, 동적 library를 읽도록 한다.

package com.google;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class HelloJni extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
  TextView tv = new TextView(this);
  tv.setText(stringFromJNI());
  setContentView(tv);
    }
    public native String stringFromJNI();
 static {
  System.loadLibrary("hello-jni");   
 }

}




이클립스 workspace의 프로젝트에서 컴파일된 디렉토리로 들어가서
class파일을 통해서 header 파일을 만든다.

프로젝트이름\bin> javah -classpath . com.google.JniHello

그러면, header 파일이 생성된 것을 확인할 수 있다.

> dir
com_google_JniHello.h

이 파일을 이용해서 c 소스를 만든다.

#include <string.h>
#include <jni.h>


jstring
Java_com_google_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}




이 파일을 linux에 설치한 ndk에 특정 위치에 둔다.

설치디렉토리/jni-test/knight/jni 폴더에 둔다.
그리고, make 파일인 Android.mk도 같이 집어 넣는다.

Android.mk 파일은 다음과 같다.

# Copyright (C) 2009 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)




 



설치디렉토리/jni-test/knight/ 디렉토리에서 빌드한다.

설치디렉토리/jni-test/knight # ../../ndk-build

컴파일이 잘되면 이렇게 so 파일이 나온다.
설치디렉토리/jni-test/knight/libs/armeabi/libhello-jni.so 파일이 생성된다.




so파일을 이클립스단으로 복사한다.
그 전에 먼저 jni 파일과 mk 파일을 복사한다.

eclipse 프로젝트의 jni 폴더를 생성하고, jni와 mk 파일을 복사한다.
그리고, so 파일을 libs에 복사한다.
디렉토리는 이렇게 나와야 한다.




이 파일에 대해서 에뮬에서 run하면 다음과 같은 결과가 나온다.




그리고, 보드에다가도 테스트해봤다.
ok 잘돈다.




소스는 아래와 같다.




ndk 를 보면, sample 디렉토리 밑에 많은 샘플(open gl 외 다양한 예제) 들이 있다.
참조해서 만들어보면 좋다.

san-angles 라는 샘플은 움직이는 예제를 보여준다. 터치해주면 잠깐 멈추고 그러니. 테스트하기에 재미있는 예제이다.



* 레퍼런스
 http://developer.android.com/sdk/ndk/index.html 

Posted by '김용환'
,