Android 源碼下利用jni編譯自己的項目(參考系統development/samples/SimpleJNI)

               記於正文前:環境是ubuntu10.10,android 源碼是2.0的,在第一次編譯源碼的時候遇到不少問題,第二次一次make通過。

               1)可能用到的文件或庫全部安裝(sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev ia32-libs x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev);

               2)建議用jdk1.5編譯android源碼,1.6也是可以編譯通過的,不過需要修改一個文件(這裏以2.0爲例,要修改文件/build/core/main.mk,將文件中的javac_version := $(shell javac -version 2>&1 | head -n 1 | grep '[ "]1\.5[\. "$$]') 全部改爲javac_version := $(shell javac -version 2>&1 | head -n 1 | grep '[ "]1\.6[\. "$$]'),總共兩處)


             Android 源碼下利用jni編譯自己的項目

              我的練習項目實現了一個簡單的四則運算,項目的目錄層次如下:

              AndroidManifest.xml  Android.mk  jni  res   src

                   資源文件簡簡單單,一個佈局文件,稍後會有demo的下載地址

                    主要記錄備忘的內容如下:

                     MainActivity.java

    public native int add(int x, int y);
    public native int substraction(int x, int y);
    public native float multiplication(int x, int y);
    public native float division(int x, int y);
    
    static{
    	System.loadLibrary("arithmetic");
    }

            生成lib的名稱爲libarithmetic.so.注意load的時候寫"arithmetic"

         jni 目錄下有兩個文件,一個是Android.mk,一個是c++源文件long.cpp

         jni/Android.mk如下:


LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE:= libarithmetic

LOCAL_SRC_FILES:= \
  long.cpp

LOCAL_SHARED_LIBRARIES := \
	libutils

LOCAL_STATIC_LIBRARIES :=

LOCAL_C_INCLUDES +=	\
	$(JNI_H_INCLUDE)

LOCAL_CFLAGS +=

LOCAL_PRELINK_MODULE := false

include $(BUILD_SHARED_LIBRARY)

註釋:

LOCAL_PATH(必須定義,而且要第一個定義),宏函數‘my-dir’, 由編譯系統提供,用於返回當前路徑(即包含Android.mk file文件的目錄);

include $( CLEAR_VARS),
CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE爲你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。這是必要的,因爲所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變量都是全局的;

LOCAL_MODULE_TAGS :=user eng tests optional

user: 指該模塊只在user版本下才編譯

eng: 指該模塊只在eng版本下才編譯

tests: 指該模塊只在tests版本下才編譯

optional:指該模塊在所有版本下都編譯


LOCAL_MODULE(必須定義),標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。Note:編譯系統會自動產生合適的前綴和後綴,例如:arithmetic編譯成功後將生成libarithmetic.so庫文件

LOCAL_SRC_FILES 變量必須包含將要編譯打包進模塊中源代碼文件。不用在這裏列出頭文件和包含文件。

LOCAL_SHARED_LIBRARIES中加入所需要鏈接的動態庫(*.so)的名稱

  LOCAL_STATIC_LIBRARIES加入所需要鏈接的靜態庫(*.a)的名稱

LOCAL_CFLAG可選的編譯器選項,用法之一是定義宏,例如LOCAL_CFLAGS    := -Werror作用是編譯警告也作爲錯誤信息

LOCAL_PRELINK_MODULE:=false,不作prelink處理,默認是要prelink操作的,有可能造成地址空間衝突(這地方目前還不明白)              long.cpp源代碼如下:

#define LOG_TAG "LongTest2 long.cpp"
#include <utils/Log.h>
#include <stdio.h>
#include "jni.h"

jint add(JNIEnv *env, jobject thiz, jint x, jint y){
	return x + y;
}

jint substraction(JNIEnv *env, jobject thiz, jint x, jint y){

	return x - y;
}

jfloat multiplication(JNIEnv *env, jobject thiz, jint x, jint y){

	return (float)x * (float)y;
}

jfloat division(JNIEnv *env, jobject thiz, jint x, jint y){
	return (float)x/(float)y;
}

static const char *classPathName = "com/inspur/test2/MainActivity";

static JNINativeMethod methods[]= {
	
	{"add", "(II)I", (void*)add},
	{"substraction", "(II)I", (void*)substraction},
	{"multiplication", "(II)F", (void*)multiplication},
	{"division", "(II)F", (void*)division},
};


typedef union{
	JNIEnv* env;
	void* venv;
}UnionJNIEnvToVoid;



static int registerNativeMethods(JNIEnv* env, const char* className,
	JNINativeMethod* gMethods, int numMethods){

	jclass clazz;
	clazz = env->FindClass(className);

	if (clazz == NULL)
		return JNI_FALSE;
	if (env->RegisterNatives(clazz, gMethods, numMethods)<0)
		return JNI_FALSE;
	return JNI_TRUE;

}


static int registerNatives(JNIEnv *env){

	if (!registerNativeMethods(env, classPathName,
		methods, sizeof(methods)/sizeof(methods[0])))
	{
		return JNI_FALSE;
	}

	return JNI_TRUE;

}



jint JNI_OnLoad(JavaVM* vm, void* reserved){

	UnionJNIEnvToVoid uenv;
	uenv.venv = NULL;
	jint result = -1;
	JNIEnv *env = NULL;

	if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK){
		goto bail;
	}
	
	env = uenv.env;

	env = uenv.env;

	if (registerNatives(env) != JNI_TRUE){

		goto bail;
	}

	result = JNI_VERSION_1_4;

bail:
	return result;
}
除了利用     編寫native   JAVA類,通過javah生成.h文件,根據.h文件編寫.c/cpp文件 方法外(名字像老太太的裹腳步,又臭又長,而且不靈活),Android還可以通過引用JNI_Onload方式實現。jint JNI_onLoad(JavaVM* vm, void* reverced),改方法在so文件被加載時調用。

JNI_OnLoad()有兩個重要的作用:

  指定JNI版本:告訴VM該組件使用那一個JNI版本(若未提供JNI_OnLoad()函數,VM會默認該使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,則必須由JNI_OnLoad()函數返回常量JNI_VERSION_1_4(該常量定義在jni.h中) 來告知VM。

  初始化設定,當VM執行到System.loadLibrary()函數時,會立即先呼叫JNI_OnLoad()方法,因此在該方法中進行各種資源的初始化操作最爲恰當。

  JNI_OnUnload()的作用與JNI_OnLoad()對應,當VM釋放JNI組件時會呼叫它,因此在該方法中進行善後清理,資源釋放的動作最爲合適。

更多更詳細內容請參考使用RegisterNatives方法傳遞和使用Java自定義類 




項目根目錄下Android.mk文件:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional 

LOCAL_SRC_FILES := $(call all-java-files-under, src)


LOCAL_JNI_SHARED_LIBRARIES := libarithmetic


LOCAL_PACKAGE_NAME := LongTest

LOCAL_SHARED_LIBRARIES := \
		libutils\
		liblog

include $(BUILD_PACKAGE)
include $(LOCAL_PATH)/jni/Android.mk 

# Also build all of the sub-targets under this one: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))

LOCAL_PACKAGE_NAME:項目名稱,即最終生成apk的名字

LOCAL_JNI_SHARED_LIBRARIES := libxxx就是把so文件放到apk文件裏的libs/armeabi裏

執行BUILD_PACKAGE。它的定義也是在config.mk中定義如下:BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk

$(call all-java-files-under, src)編譯的源代碼文件列表添加src目錄下所有的java 源文件

$(call all-makefiles-under, $(LOCAL_PATH))編譯器會在編譯完當前目錄下的文件後再深入子目錄編譯



如果make過android源碼,可以在項目根目錄下執行mm命令進行編譯。前提是執行過source androidSRC/build/envsetup.sh

或者直接把source androidSRC/build/envsetup.sh添加到~/.bashrc中,會更加方便


示例代碼下載

發佈了24 篇原創文章 · 獲贊 53 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章