程序员人生 网站导航

JNI 实战全面解析

栏目:互联网时间:2014-11-05 08:44:39
简介

项目决定移植1款C++开源项目到Android平台,开始对JNI深入研究。

JNI是甚么?

JNI(Java Native Interface)意为JAVA本地调用,它允许Java代码和其他语言写的代码进行交互,简单的说,1种在Java虚拟机控制下履行代码的标准机制。

NDK是甚么?

Android NDK(Native Development Kit )是1套工具集合,允许你用像C/C++语言那样实现利用程序的1部份。

为何要用NDK?

1、安全性,java是半解释型语言,很容易被反汇编后拿到源代码文件,我们可以在重要的交互功能使用C语言代替。
2、效力,C语言比起java来讲效力要高出很多。

JNI和NDK的区分?

从工具上说,NDK其实多了1个把.so和.apk打包的工具,而JNI开发并没有打包,只是把.so文件放到文件系统的特定位置。
从编译库说NDK开发C/C++只能能使用NDK自带的有限的头文件,而使用JNI则可使用文件系统中带的头文件。
从编写方式说,它们1样。

详解
1、JNI 元素

1、JNI组织结构


JNI函数表的组成绩像C++的虚函数表,虚拟机可以运行多张函数表。
JNI接口指针仅在当前线程中起作用,指针不能从1个线程进入另外一个线程,但可以在不同的线程中调用本地方法。


2、原始数据

Jobject  对象 援用类型


Java类型 本地类型(JNI) 描写
boolean(布尔型)jboolean 无符号8个比特
byte(字节型)jbyte 有符号8个比特
char(字符型)jchar 无符号16个比特
short(短整型)jshort 有符号16个比特
int(整型)jint 有符号32个比特
long(长整型)jlong 有符号64个比特
float(浮点型)jfloat 32个比特
double(双精度浮点型)jdouble 64个比特
void(空型)void N/A

函数操作

函数 Java 数组类型 本地类型 说明
GetBooleanArrayElementsjbooleanArrayjbooleanReleaseBooleanArrayElements 释放
GetByteArrayElementsjbyteArrayjbyteReleaseByteArrayElements 释放
GetCharArrayElementsjcharArrayjcharReleaseShortArrayElements 释放
GetShortArrayElementsjshortArrayjshortReleaseBooleanArrayElements 释放
GetIntArrayElementsjintArrayjintReleaseIntArrayElements 释放
GetLongArrayElementsjlongArrayjlongReleaseLongArrayElements 释放
GetFloatArrayElementsjfloatArrayjfloatReleaseFloatArrayElements 释放
GetDoubleArrayElementsjdoubleArrayjdoubleReleaseDoubleArrayElements 释放
GetObjectArrayElement自定义对象object 
SetObjectArrayElement自定义对象object 
GetArrayLength  获得数组大小
New<Type>Array  创建1个指定长度的原始数据类型的数组
GetPrimitiveArrayCritical  得到指向原始数据类型内容的指针,该方法可能使垃圾回收不能履行,该方法可能返回数组的拷贝,因此必须释放此资源。
ReleasePrimitiveArrayCritical  释放指向原始数据类型内容的指针,该方法可能使垃圾回收不能履行,该方法可能返回数组的拷贝,因此必须释放此资源。
NewStringUTF  jstring类型的方法转换
GetStringUTFChars  jstring类型的方法转换
DefineClass   从原始类数据的缓冲区中加载类
FindClass   该函数用于加载本地定义的类。它将搜索由CLASSPATH 环境变量为具有指定名称的类所指定的目录和 zip文件
GetObjectClass   通过对象获得这个类。该函数比较简单,唯1注意的是对象不能为NULL,否则获得的class肯定返回也为NULL
GetSuperclass   获得父类或说超类 。 如果 clazz 代表类class而非类 object,则该函数返回由 clazz 所指定的类的超类。 如果 clazz指定类 object 或代表某个接口,则该函数返回NULL
IsAssignableFrom   肯定 clazz1 的对象是不是可安全地强迫转换为clazz2
Throw  抛出 java.lang.Throwable 对象
ThrowNew   利用指定类的消息(由 message 指定)构造异常对象并抛出该异常
ExceptionOccurred   肯定是不是某个异常正被抛出。在平台相干代码调用 ExceptionClear() 或 Java 代码处理该异常前,异常将始终保持抛出状态
ExceptionDescribe   将异常及堆栈的回溯输出到系统毛病报告信道(例如 stderr)。该例程可便利调试操作
ExceptionClear   清除当前抛出的任何异常。如果当前无异常,则此例程不产生任何效果
FatalError   抛出致命毛病并且不希望虚拟机进行修复。该函数无返回值
NewGlobalRef   创建 obj 参数所援用对象的新全局援用。obj 参数既可以是全局援用,也能够是局部援用。全局援用通过调用DeleteGlobalRef() 来显式撤销。
DeleteGlobalRef   删除 globalRef 所指向的全局援用
DeleteLocalRef   删除 localRef所指向的局部援用
AllocObject   分配新 Java 对象而不调用该对象的任何构造函数。返回该对象的援用。clazz 参数务必不要援用数组类。
getObjectClass  返回对象的类
IsSameObject  测试两个援用是不是援用同1 Java 对象
NewString   利用 Unicode 字符数组构造新的 java.lang.String 对象
GetStringLength   返回 Java 字符串的长度(Unicode 字符数)
GetStringChars   返回指向字符串的 Unicode 字符数组的指针。该指针在调用 ReleaseStringchars() 前1直有效
ReleaseStringChars   通知虚拟机平台相干代码无需再访问 chars。参数chars 是1个指针,可通过 GetStringChars() 从 string 取得
NewStringUTF   利用 UTF⑻ 字符数组构造新 java.lang.String 对象
GetStringUTFLength   以字节为单位返回字符串的 UTF⑻ 长度
GetStringUTFChars   返回指向字符串的 UTF⑻ 字符数组的指针。该数组在被ReleaseStringUTFChars() 释放前将1直有效
ReleaseStringUTFChars   通知虚拟机平台相干代码无需再访问 utf。utf 参数是1个指针,可利用 GetStringUTFChars() 取得
NewObjectArray   构造新的数组,它将保存类 elementClass 中的对象。所有元素初始值均设为 initialElement
Set<PrimitiveType>ArrayRegion  将基本类型数组的某1区域从缓冲区中复制回来的1组函数
GetFieldID   返回类的实例(非静态)域的属性 ID。该域由其名称及签名指定。访问器函数的
Get<type>Field 及 Set<type>Field系列使用域 ID 检索对象域。GetFieldID() 不能用于获得数组的长度域。应使用GetArrayLength()。
Get<type>Field  该访问器例程系列返回对象的实例(非静态)域的值。要访问的域由通过调用GetFieldID() 而得到的域 ID 指定。 
Set<type>Field  该访问器例程系列设置对象的实例(非静态)属性的值。要访问的属性由通过调用
SetFieldID() 而得到的属性 ID指定。
GetStaticFieldID 

GetStatic<type>Field

SetStatic<type>Field
  同上,只不过是静态属性。
GetMethodID  返回类或接口实例(非静态)方法的方法 ID。方法可在某个 clazz 的超类中定义,也可从 clazz 继承。该方法由其名称和签名决定。 GetMethodID() 可以使未初始化的类初始化。要取得构造函数的方法 ID,应将<init> 作为方法名,同时将void (V) 作为返回类型。
CallVoidMethod   
CallObjectMethod   
CallBooleanMethod    
CallByteMethod   
CallCharMethod   
CallShortMethod   
CallIntMethod   
CallLongMethod   
CallFloatMethod   
CallDoubleMethod   
GetStaticMethodID   调用静态方法
Call<type>Method   
RegisterNatives   向 clazz 参数指定的类注册本地方法。methods 参数将指定 JNINativeMethod 结构的数组,其中包括本地方法的名称、签名和函数指针。nMethods 参数将指定数组中的本地方法数。
UnregisterNatives   取消注册类的本地方法。类将返回到链接或注册了本地方法函数前的状态。该函数不应在常规平台相干代码中使用。相反,它可以为某些程序提供1种重新加载和重新链接本地库的途径。    
    

域描写符

Java 语言
Zboolean
Bbyte
Cchar
Sshort
Iint
Jlong
Ffloat
Ddouble
  

援用类型则为 L + 该类型类描写符 + 。

数组,其为 :  [ + 其类型的域描写符 + 。

多维数组则是 n个[ +该类型的域描写符 , N代表的是几维数组。

String类型的域描写符为 Ljava/lang/String; [ + 其类型的域描写符 + ; int[ ] 其描写符为[I float[ ] 其描写符为[F String[ ] 其描写符为[Ljava/lang/String; Object[ ]类型的域描写符为[Ljava/lang/Object; int [ ][ ] 其描写符为[[I float[ ][ ] 其描写符为[[F

 将参数类型的域描写符依照申明顺序放入1对括号中后跟返回值类型的域描写符,规则以下: (参数的域描写符的叠加)返回类型描写符。对,没有返回值的,用V(表示void型)表示。
举例以下:

Java层方法 JNI函数签名 String test ( ) Ljava/lang/String; int f (int i, Object object) (ILjava/lang/Object;)I void set (byte[ ] bytes) ([B)V

JNIEnv与JavaVM 

JNIEnv 概念 : 是1个线程相干的结构体, 该结构体代表了 Java 在本线程的运行环境 ; 

JNIEnv 与 JavaVM : 注意辨别这两个概念; 
-- JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有1个;
-- JNIEnv : JavaVM 在线程中的代表, 每一个线程都有1个, JNI 中可能有很多个 JNIEnv;

JNIEnv 作用 : 
-- 调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可使用 JNIEnv 调用 Java 中的代码;
-- 操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象;


JNIEnv 体系结构 

线程相干 : JNIEnv 是线程相干的, 即 在 每一个线程中 都有1个 JNIEnv 指针, 每一个JNIEnv 都是线程专有的, 其它线程不能使用本线程中的 JNIEnv, 线程 A 不能调用 线程 B 的 JNIEnv;

JNIEnv 不能跨线程 : 
-- 当前线程有效 : JNIEnv 只在当前线程有效, JNIEnv 不能在 线程之间进行传递, 在同1个线程中, 屡次调用 JNI层方法, 传入的 JNIEnv 是相同的;
-- 本地方法匹配多JNIEnv : 在 Java 层定义的本地方法, 可以在不同的线程调用, 因此 可以接受不同的 JNIEnv;

JNIEnv 结构 : 由上面的代码可以得出, JNIEnv 是1个指针,  指向1个线程相干的结构, 线程相干结构指向 JNI 函数指针 数组, 这个数组中寄存了大量的 JNI 函数指针, 这些指针指向了具体的 JNI 函数; 


注意:JNIEnv只在当前线程中有效。本地方法不能将JNIEnv从1个线程传递到另外一个线程中。相同的 Java 线程中对本地方法屡次调用时,传递给该本地方法的JNIEnv是相同的。但是,1个本地方法可被不同的 Java 线程所调用,因此可以接受不同的 JNIEnv。



UTF⑻编码

JNI使用改进的UTF⑻字符串来表示不同的字符类型。Java使用UTF⑴6编码。UTF⑻编码主要使用于C语言,由于它的编码用u000表示为0xc0,而不是通常的0×00。非空ASCII字符改进后的字符串编码中可以用1个字节表示。

毛病

JNI不会检查NullPointerException、IllegalArgumentException这样的毛病,缘由是:致使性能降落。

在绝大多数C的库函数中,很难避免毛病产生。
JNI允许用户使用Java异常处理。大部份JNI方法会返回毛病代码但本身其实不会报出异常。因此,很有必要在代码本身进行处理,将异常抛给Java。在JNI内部,首先会检查调用函数返回的毛病代码,以后会调用ExpectOccurred()返回1个毛病对象。
jthrowable ExceptionOccurred(JNIEnv *env); 例如:1些操作数组的JNI函数不会报错,因此可以调用ArrayIndexOutofBoundsException或ArrayStoreExpection方法报告异常。
3、JNI函数实战

1、*.so的入口函数

JNI_OnLoad()与JNI_OnUnload()
当Android的VM(Virtual Machine)履行到System.loadLibrary()函数时,首先会去履行C组件里的JNI_OnLoad()函数。它的用处有2:
(1)告知VM此C组件使用那1个JNI版本。如果你的*.so档没有提供JNI_OnLoad()函数,VM会默许该*.so档是使用最老的JNI 1.1版本。由于新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer,就必须藉由JNI_OnLoad()函数来告知VM。
(2)由于VM履行到System.loadLibrary()函数时,就会立即先呼唤JNI_OnLoad(),所以C组件的开发者可以藉由JNI_OnLoad()来进行C组件内的早期值之设定(Initialization) 。


2、返回值

jstring str = env->newStringUTF("HelloJNI"); //直接使用该JNI构造1个jstring对象返回 return str ;
jobjectArray ret = 0; jsize len = 5; jstring str; string value("hello");   ret = (jobjectArray)(env->NewObjectArray(len, env->FindClass("java/lang/String"), 0)); for(int i = 0; i < len; i++) {     str = env->NewStringUTF(value..c_str());     env->SetObjectArrayElement(ret, i, str); } return ret; 返回数组
jclass m_cls = env->FindClass("com/ldq/ScanResult"); jmethodID m_mid = env->GetMethodID(m_cls,"<init>","()V"); jfieldID m_fid_1 = env->GetFieldID(m_cls,"ssid","Ljava/lang/String;"); jfieldID m_fid_2 = env->GetFieldID(m_cls,"mac","Ljava/lang/String;"); jfieldID m_fid_3 = env->GetFieldID(m_cls,"level","I"); jobject m_obj = env->NewObject(m_cls,m_mid); env->SetObjectField(m_obj,m_fid_1,env->NewStringUTF("AP1")); env->SetObjectField(m_obj,m_fid_2,env->NewStringUTF("00⑴1⑵2⑶3⑷4⑸5")); env->SetIntField(m_obj,m_fid_3,⑸0); return m_obj; 返回自定义对象

jclass list_cls = env->FindClass("Ljava/util/ArrayList;");//取得ArrayList类援用 if(listcls == NULL) { cout << "listcls is null " ; } jmethodID list_costruct = env->GetMethodID(list_cls , "<init>","()V"); //取得得构造函数Id jobject list_obj = env->NewObject(list_cls , list_costruct); //创建1个Arraylist集合对象 //或得Arraylist类中的 add()方法ID,其方法原型为: boolean add(Object object) ; jmethodID list_add = env->GetMethodID(list_cls,"add","(Ljava/lang/Object;)Z"); jclass stu_cls = env->FindClass("Lcom/feixun/jni/Student;");//取得Student类援用 //取得该类型的构造函数 函数名为 <init> 返回类型必须为 void 即 V jmethodID stu_costruct = env->GetMethodID(stu_cls , "<init>", "(ILjava/lang/String;)V"); for(int i = 0 ; i < 3 ; i++) { jstring str = env->NewStringUTF("Native"); //通过调用该对象的构造函数来new 1个 Student实例 jobject stu_obj = env->NewObject(stucls , stu_costruct , 10,str); //构造1个对象 env->CallBooleanMethod(list_obj , list_add , stu_obj); //履行Arraylist类实例的add方法,添加1个stu对象 } return list_obj ; 返回对象集合

3、操作Java层的类

//取得jfieldID 和 该字段的初始值 jfieldID nameFieldId ; jclass cls = env->GetObjectClass(obj); //取得Java层该对象实例的类援用,即HelloJNI类援用 nameFieldId = env->GetFieldID(cls , "name" , "Ljava/lang/String;"); //取得属性句柄 if(nameFieldId == NULL) { cout << " 没有得到name 的句柄Id ;" ; } jstring javaNameStr = (jstring)env->GetObjectField(obj ,nameFieldId); // 取得该属性的值 const char * c_javaName = env->GetStringUTFChars(javaNameStr , NULL); //转换为 char *类型 string str_name = c_javaName ; cout << "the name from java is " << str_name << endl ; //输出显示 env->ReleaseStringUTFChars(javaNameStr , c_javaName); //释放局部援用 //构造1个jString对象 char * c_ptr_name = "I come from Native" ; jstring cName = env->NewStringUTF(c_ptr_name); //构造1个jstring对象 env->SetObjectField(obj , nameFieldId , cName); // 设置该字段的值
4、回调Java层方法

jstring str = NULL; jclass clz = env->FindClass("cc/androidos/jni/JniTest"); //获得clz的构造函数并生成1个对象 jmethodID ctor = env->GetMethodID(clz, "<init>", "()V"); jobject obj = env->NewObject(clz, ctor); // 如果是数组类型,则在类型前加[,如整形数组int[] intArray,则对应类型为[I,整形数组String[] strArray对应为[Ljava/lang/String; jmethodID mid = env->GetMethodID(clz, "sayHelloFromJava", "(Ljava/lang/String;II[I)I"); if (mid) { LOGI("mid is get"); jstring str1 = env->NewStringUTF("I am Native"); jint index1 = 10; jint index2 = 12; //env->CallVoidMethod(obj, mid, str1, index1, index2); // 数组类型转换 testIntArray能不能不申请内存空间 jintArray testIntArray = env->NewIntArray(10); jint *test = new jint[10]; for(int i = 0; i < 10; ++i) { *(test+i) = i + 100; } env->SetIntArrayRegion(testIntArray, 0, 10, test); jint javaIndex = env->CallIntMethod(obj, mid, str1, index1, index2, testIntArray); LOGI("javaIndex = %d", javaIndex); delete[] test; test = NULL; }

static void event_callback(int eventId,const char* description) { //主进程回调可以,线程中回调失败。 if (gEventHandle == NULL) return; JNIEnv *env; bool isAttached = false; if (myVm->GetEnv((void**) &env, JNI_VERSION_1_2) < 0) { //获得当前的JNIEnv if (myVm->AttachCurrentThread(&env, NULL) < 0) return; isAttached = true; } jclass cls = env->GetObjectClass(gEventHandle); //获得类对象 if (!cls) { LOGE("EventHandler: failed to get class reference"); return; } jmethodID methodID = env->GetStaticMethodID(cls, "callbackStatic", "(ILjava/lang/String;)V"); //静态方法或成员方法 if (methodID) { jstring content = env->NewStringUTF(description); env->CallVoidMethod(gEventHandle, methodID,eventId, content); env->ReleaseStringUTFChars(content,description); } else { LOGE("EventHandler: failed to get the callback method"); } if (isAttached) myVm->DetachCurrentThread(); }

线程中回调
把c/c++中所有线程的创建,由pthread_create函数替换为由Java层的创建线程的函数AndroidRuntime::createJavaThread。
static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg) { return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg); } static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { //异常检测和排除 if (env->ExceptionCheck()) { LOGE("An exception was thrown by callback '%s'.", methodName); LOGE_EX(env); env->ExceptionClear(); } } static void receive_callback(unsigned char *buf, int len) //回调 { int i; JNIEnv* env = AndroidRuntime::getJNIEnv(); jcharArray array = env->NewCharArray(len); jchar *pArray ; if(array == NULL){ LOGE("receive_callback: NewCharArray error."); return; } pArray = (jchar*)calloc(len, sizeof(jchar)); if(pArray == NULL){ LOGE("receive_callback: calloc error."); return; } //copy buffer to jchar array for(i = 0; i < len; i++) { *(pArray + i) = *(buf + i); } //copy buffer to jcharArray env->SetCharArrayRegion(array,0,len,pArray); //invoke java callback method env->CallVoidMethod(mCallbacksObj, method_receive,array,len); //release resource env->DeleteLocalRef(array); free(pArray); pArray = NULL; checkAndClearExceptionFromCallback(env, __FUNCTION__); } public void Receive(char buffer[],int length){ //java层函数 String msg = new String(buffer); msg = "received from jni callback" + msg; Log.d("Test", msg); }

jclass cls = env->GetObjectClass(obj);//取得Java类实例 jmethodID callbackID = env->GetMethodID(cls , "callback" , "(Ljava/lang/String;)V") ;//或得该回调方法句柄 if(callbackID == NULL) { cout << "getMethodId is failed " << endl ; } jstring native_desc = env->NewStringUTF(" I am Native"); env->CallVoidMethod(obj , callbackID , native_desc); //回调该方法,并且


5、传对象到JNI调用

jclass stu_cls = env->GetObjectClass(obj_stu); //或得Student类援用 if(stu_cls == NULL) { cout << "GetObjectClass failed " ; } //下面这些函数操作,我们都见过的。O(∩_∩)O~ jfieldID ageFieldID = env->GetFieldID(stucls,"age","I"); //取得得Student类的属性id jfieldID nameFieldID = env->GetFieldID(stucls,"name","Ljava/lang/String;"); // 取得属性ID jint age = env->GetIntField(objstu , ageFieldID); //取得属性值 jstring name = (jstring)env->GetObjectField(objstu , nameFieldID);//取得属性值 const char * c_name = env->GetStringUTFChars(name ,NULL);//转换成 char * string str_name = c_name ; env->ReleaseStringUTFChars(name,c_name); //释放援用 cout << " at Native age is :" << age << " # name is " << str_name << endl ;
6、与C++互转

jbytearray转c++byte数组

jbyte * arrayBody = env->GetByteArrayElements(data,0); jsize theArrayLengthJ = env->GetArrayLength(data); BYTE * starter = (BYTE *)arrayBody;


jbyteArray 转 c++中的BYTE[] 
jbyte * olddata = (jbyte*)env->GetByteArrayElements(strIn, 0); jsize oldsize = env->GetArrayLength(strIn); BYTE* bytearr = (BYTE*)olddata; int len = (int)oldsize;


C++中的BYTE[]转jbyteArray 
jbyte *by = (jbyte*)pData; jbyteArray jarray = env->NewByteArray(nOutSize); env->SetByteArrayRegin(jarray, 0, nOutSize, by);


jbyteArray 转 char * 
char* data = (char*)env->GetByteArrayElements(strIn, 0);


char* 转jstring
jstring WindowsTojstring(JNIEnv* env, char* str_tmp) { jstring rtn=0; int slen = (int)strlen(str_tmp); unsigned short* buffer=0; if(slen == 0) { rtn = env->NewStringUTF(str_tmp); } else { int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, NULL, 0); buffer = (unsigned short*)malloc(length*2+1); if(MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str_tmp, slen, (LPWSTR)buffer, length) > 0) { rtn = env->NewString((jchar*)buffer, length); } } if(buffer) { free(buffer); } return rtn; }


char* jstring互转
JNIEXPORT jstring JNICALL Java_com_explorer_jni_SambaTreeNative_getDetailsBy (JNIEnv *env, jobject jobj, jstring pc_server, jstring server_user, jstring server_passwd) { const char *pc = env->GetStringUTFChars(pc_server, NULL); const char *user = env->GetStringUTFChars(server_user, NULL); const char *passwd = env->GetStringUTFChars(server_passwd, NULL); const char *details = smbtree::getPara(pc, user, passwd); jstring jDetails = env->NewStringUTF(details); return jDetails; }
4、Android.mk、Application.mk
1、Android.mk

Android.mk文件是GNU Makefile的1小部份,它用来对Android程序进行编译,Android.mk中的变量都是全局的,解析进程会被定义。

1个Android.mk文件可以编译多个模块,模块包括:APK程序、JAVA库、CC++利用程序、CC++静态库、CC++同享库。

简单实例以下:

LOCAL_PATH := $(call my-dir) #表示是当前文件的路径 include $(CLEAR_VARS) #指定让GNU MAKEFILE该脚本为你清除许多 LOCAL_XXX 变量 LOCAL_MODULE:= helloworld #标识你在 Android.mk 文件中描写的每一个模块 MY_SOURCES := foo.c #自定义变量 ifneq ($(MY_CONFIG_BAR),) MY_SOURCES += bar.c endif LOCAL_SRC_FILES += $(MY_SOURCES) #包括将要编译打包进模块中的 C 或 C++源代码文件 include $(BUILD_SHARED_LIBRARY) #根据LOCAL_XXX系列变量中的值,来编译生成同享库(动态链接库)


GNU Make系统变量

变量 描写
CLEAR_VARS指向1个编译脚本,几近所有未定义的 LOCAL_XXX 变量都在"Module-description"节中列出。必须在开始1个新模块之前包括这个脚本:include$(CLEAR_VARS),用于重置除LOCAL_PATH变量外的,所有LOCAL_XXX系列变量。
BUILD_SHARED_LIBRARY指向编译脚本,根据所有的在 LOCAL_XXX 变量把列出的源代码文件编译成1个同享库。
BUILD_STATIC_LIBRARY1个 BUILD_SHARED_LIBRARY 变量用于编译1个静态库。静态库不会复制到的APK包中,但是能够用于编译同享库。
TARGET_ARCH目标 CPU平台的名字,  和 android 开放源码中指定的那样。如果是arm,表示要生成 ARM 兼容的指令,与 CPU架构的修订版无关。
TARGET_PLATFORMAndroid.mk 解析的时候,目标 Android 平台的名字.详情可参考/development/ndk/docs/stable- apis.txt.
TARGET_ARCH_ABI支持目标平台
TARGET_ABI目标平台和 ABI 的组合,它事实上被定义成$(TARGET_PLATFORM)-$(TARGET_ARCH_ABI)  ,在想要在真实的装备中针对1个特别的目标系统进行测试时,会有用。在默许的情况下,它会是'android⑶-arm'。
  

模块描写变量

变量 描写
LOCAL_PATH这个变量用于给出当前文件的路径。必须在 Android.mk 的开头定义,可以这样使用:LOCAL_PATH := $(call my-dir)  这个变量不会被$(CLEAR_VARS)清除,因此每
个 Android.mk 只需要定义1次(即便在1个文件中定义了几个模块的情况下)。
LOCAL_MODULE这是模块的名字,它必须是唯1的,而且不能包括空格。必须在包括任1的$(BUILD_XXXX)脚本之前定义它。模块的名字决定了生成文件的名字。例如,如果1个1个同享库模块的名字是,那末生成文件的名字就是 lib.so。但是,在的 NDK 生成文件中(或 Android.mk 或 Application.mk),应当只触及(援用)有正常名字的其他模块。
LOCAL_SRC_FILES这是要编译的源代码文件列表。只要列出要传递给编译器的文件,由于编译系统自动计算依赖。注意源代码文件名称都是相对 LOCAL_PATH的,你可使用路径部份。
LOCAL_CPP_EXTENSION这是1个可选变量, 用来指定C++代码文件的扩大名,默许是'.cpp',但是可以改变它。
LOCAL_C_INCLUDES可选变量,表示头文件的搜索路径。
LOCAL_CFLAGS可选的编译器选项,在编译 C 代码文件的时候使用。
LOCAL_CXXFLAGS与 LOCAL_CFLAGS同理,针对 C++源文件。
LOCAL_CPPFLAGS与 LOCAL_CFLAGS同理,但是对 C 和 C++ source files都适用。
LOCAL_STATIC_LIBRARIES表示该模块需要使用哪些静态库,以便在编译时进行链接。
LOCAL_SHARED_LIBRARIES表示模块在运行时要依赖的同享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。注意:它不会附加列出的模块到编译图,也就是依然需要在Application.mk 中把它们添加到程序要求的模块中。
LOCAL_LDLIBS编译模块时要使用的附加的链接器选项。这对使用‘-l’前缀传递指定库的名字是有用的。
LOCAL_ALLOW_UNDEFINED_SYMBOLS默许情况下, 在试图编译1个同享库时,任何未定义的援用将致使1个“未定义的符号”毛病。
LOCAL_ARM_MODE默许情况下, arm目标2进制会以 thumb 的情势生成(16 位),你可以通过设置这个变量为 arm如果你希望你的 module 是以 32 位指令的情势。
LOCAL_MODULE_PATH 和 LOCAL_UNSTRIPPED_PATH在 Android.mk 文件中, 还可以用LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH指定最后的目标安装路径.
不同的文件系统路径用以下的宏进行选择:
  TARGET_ROOT_OUT:表示根文件系统。
   TARGET_OUT:表示 system文件系统。
   TARGET_OUT_DATA:表示 data文件系统。
用法如:LOCAL_MODULE_PATH :=$(TARGET_ROOT_OUT) 
至于LOCAL_MODULE_PATH 和LOCAL_UNSTRIPPED_PATH的区分,暂时还不清楚。

GNU Make 功能宏

变量 描写
my-dir返回当前 Android.mk 所在的目录的路径,相对 NDK 编译系统的顶层。
all-subdir-makefiles返回1个位于当前'my-dir'路径的子目录中的所有Android.mk的列表。
this-makefile返回当前Makefile 的路径(即这个函数调用的地方)
parent-makefile返回调用树中父 Makefile 路径。即包括当前Makefile的Makefile 路径。
grand-parent-makefile返回调用树中父Makefile的父Makefile的路径
  

范例:


2、

编译1个简单的APK

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java subdirectory LOCAL_SRC_FILES := $(call all-subdir-java-files) # Name of the APK to build LOCAL_PACKAGE_NAME := LocalPackage # Tell it to build an APK include $(BUILD_PACKAGE)

编译1个依赖静态.jar文件的APK 

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # List of static libraries to include in the package LOCAL_STATIC_JAVA_LIBRARIES := static-library # Build all java files in the java subdirectory LOCAL_SRC_FILES := $(call all-subdir-java-files) # Name of the APK to build LOCAL_PACKAGE_NAME := LocalPackage # Tell it to build an APK include $(BUILD_PACKAGE) 注:LOCAL_STATIC_JAVA_LIBRARIES 后面应是你的APK程序所需要的JAVA库的JAR文件名。


编译1个需要platform key签名的APK

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java subdirectory LOCAL_SRC_FILES := $(call all-subdir-java-files) # Name of the APK to build LOCAL_PACKAGE_NAME := LocalPackage LOCAL_CERTIFICATE := platform # Tell it to build an APK include $(BUILD_PACKAGE) 注:LOCAL_CERTIFICATE 后面应当是签名文件的文件名

编译1个需要特殊vendor key签名的APK 

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java subdirectory LOCAL_SRC_FILES := $(call all-subdir-java-files) # Name of the APK to build LOCAL_PACKAGE_NAME := LocalPackage LOCAL_CERTIFICATE := vendor/example/certs/app # Tell it to build an APK include $(BUILD_PACKAGE)

装载1个普通的第3方APK

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Module name should match apk name to be installed. LOCAL_MODULE := LocalModuleName LOCAL_SRC_FILES := $(LOCAL_MODULE).apk LOCAL_MODULE_CLASS := APPS LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_CERTIFICATE := platform include $(BUILD_PREBUILT)

装载需要.so(动态库)的第3方apk

LOCAL_PATH := $(my-dir) include $(CLEAR_VARS) LOCAL_MODULE := baiduinput_android_v1.1_1000e LOCAL_SRC_FILES := $(LOCAL_MODULE).apk LOCAL_MODULE_CLASS := APPS LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX) LOCAL_CERTIFICATE := platform include $(BUILD_PREBUILT) ################################################################# ####### copy the library to /system/lib ######################### ################################################################# include $(CLEAR_VARS) LOCAL_MODULE := libinputcore.so LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES) LOCAL_SRC_FILES := lib/$(LOCAL_MODULE) OVERRIDE_BUILD_MODULE_PATH := $(TARGET_OUT_INTERMEDIATE_LIBRARIES) include $(BUILD_PREBUILT)

编译1个静态java库 

LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # Build all java files in the java subdirectory LOCAL_SRC_FILES := $(call all-subdir-java-files) # Any libraries that this library depends on LOCAL_JAVA_LIBRARIES := android.test.runner # The name of the jar file to create LOCAL_MODULE := sample # Build a static jar file. include $(BUILD_STATIC_JAVA_LIBRARY) 注:LOCAL_JAVA_LIBRARIES表示生成的java库的jar文件名。

编译C/C++利用程序模板

LOCAL_
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐