編輯:關於Android編程
上一篇我們總結了jni中的數據類型的轉換,通過實戰體驗了如何轉換基本類型、字符串、以及數組,數組又包括了原始類型數組和對象數組。這一篇博客將繼續總結jni基礎知識,並通過實際體驗掌握jni編程。這篇博客將著重於本地代碼訪問java代碼的相關知識。
本地代碼訪問java代碼,主要是指訪問java的字段和方法。而字段和方法都分別有靜態與非靜態之分,我們將分別探討。
Java層的field和method,不管它是public,還是package、private和protected,從
JNI都可以訪問到,Java面向語言的封裝性不見了。
靜態字段和非靜態的字段訪問方式不同,jni規范提供了一系列帶static標示的訪問靜態字段的函數:
jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID); jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID); jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID); jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID); jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID); jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID); jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID); jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID) __NDK_FPABI__; jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID) __NDK_FPABI__; void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject); void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean); void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte); void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar); void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort); void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint); void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong); void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat) __NDK_FPABI__; void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble) __NDK_FPABI__;
訪問流程:
獲得java層的類:jclass cls = (*env)->GetObjectClass(env, obj); 獲得字段的ID:jfieldID fid = (*env)->GetStaticFieldID(env, cls, “s”, “Ljava/lang/String;”); 獲得字段的值:jstring jstr = (*env)->GetStaticObjectField(env, cls, fid); 設置字段的值:(*env)->SetStaticObjectField(env, cls, fid, jstr);按照以上的流程,參照上面訪問靜態字段的函數定義,寫如下測試代碼:
void native_accessJava(JNIEnv * env, jobject obj){ LOGE("lstr:native_accessJava"); //1. 獲得java層的類:jclass cls = (*env)->GetObjectClass(env, obj); jclass cls = (*env)->GetObjectClass(env, obj); //2. 獲得字段的ID:jfieldID fid = (*env)->GetStaticFieldID(env, cls, "s", "Ljava/lang/String;"); jfieldID fid = (*env)->GetStaticFieldID(env, cls, "s", "Ljava/lang/String;"); if (fid == NULL) { LOGE("get feild id error"); return; /* failed to find the field */ } //3. 獲得字段的值:jstring jstr = (*env)->GetStaticObjectField(env, cls, fid); jstring jstr = (*env)->GetStaticObjectField(env, cls, fid); LOGE("lstr:native_accessJava"); const char * lstr = (*env)->GetStringUTFChars(env,jstr,NULL); LOGE("lstr: %s",lstr); (*env)->ReleaseStringUTFChars(env,jstr,lstr); //4. 設置字段的值:(*env)->SetStaticObjectField(env, cls, fid, jstr); jstr = (*env)->NewStringUTF(env, "jni set"); if (jstr == NULL) { return; /* out of memory */ } (*env)->SetStaticObjectField(env, cls, fid, jstr); }
注冊方法的數組:
static JNINativeMethod gMethods[] = { {"sayHello", "([I)I", (void *)native_sayHello}, {"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry}, {"accessJava","()V",(void *)native_accessJava}, };
java中訪問的代碼:
public class MainActivity extends AppCompatActivity { TextView textView = null; static String s = "java str"; static { System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.text); accessJava(); textView.setText(s); } public native int sayHello(int []arr); public native String[] arrayTry(String [] arr); public native void accessJava(); }
在jni代碼所在目錄執行adk-build命令,把編譯生成的libhello.so文件拷貝到android工程的jniLibs目錄下,運行android程序即可看到現象。
有了訪問靜態字段的經歷,在去寫訪問實例字段的代碼就簡單多了,這裡總結下使用流程:
獲得java層的類:jclass cls = (*env)->GetObjectClass(env, obj); 獲得字段的ID:jfieldID fid = (*env)->GetFieldID(env, cls, “ss”, “Ljava/lang/String;”); 獲得字段的值:jstring jstr = (*env)->GetObjectField(env, obj, fid); 設置字段的值:(*env)->SetObjectField(env, obj, fid, jstr);在寫代碼之前,先看一下jni.h中定義的訪問實例字段的函數:
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*); jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID); jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID); jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID); jchar (*GetCharField)(JNIEnv*, jobject, jfieldID); jshort (*GetShortField)(JNIEnv*, jobject, jfieldID); jint (*GetIntField)(JNIEnv*, jobject, jfieldID); jlong (*GetLongField)(JNIEnv*, jobject, jfieldID); jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__; jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__; void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject); void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean); void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte); void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar); void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort); void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint); void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong); void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat) __NDK_FPABI__; void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble) __NDK_FPABI__;
可以看到訪問實例字段的函數和訪問靜態字段的函數在名字上就有區別,而且一定要注意的是,訪問實例字段函數的第三個參數是jobject,是一個對象,而訪問靜態字段的第三個參數是jclass,是一個類。
我們使用上面給出的函數和我們總結的使用流程寫如下代碼:
void native_accessinstanceJava(JNIEnv * env, jobject obj){ LOGE("lstr:native_accessinstanceJava"); //1. 獲得java層的類:jclass cls = (*env)->GetObjectClass(env, obj); jclass cls = (*env)->GetObjectClass(env, obj); //2. 獲得字段的ID:jfieldID fid = (*env)->GetFieldID(env, cls, "ss", "Ljava/lang/String;"); jfieldID fid = (*env)->GetFieldID(env, cls, "ss", "Ljava/lang/String;"); if (fid == NULL) { LOGE("get feild id error"); return; /* failed to find the field */ } //3. 獲得字段的值:jstring jstr = (*env)->GetObjectField(env, cls, fid); jstring jstr = (*env)->GetObjectField(env, obj, fid); const char * lstr = (*env)->GetStringUTFChars(env,jstr,NULL); LOGE("lstr: %s",lstr); (*env)->ReleaseStringUTFChars(env,jstr,lstr); //4. 設置字段的值:(*env)->SetObjectField(env, cls, fid, jstr); jstr = (*env)->NewStringUTF(env, "jni set"); if (jstr == NULL) { return; /* out of memory */ } (*env)->SetObjectField(env, obj, fid, jstr); }
注冊方法的數組:
static JNINativeMethod gMethods[] = { {"sayHello", "([I)I", (void *)native_sayHello}, {"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry}, {"accessJava","()V",(void *)native_accessJava}, {"accessinstanceJava","()V",(void *)native_accessinstanceJava}, };
JNI_OnLoad等方法請參考之前的博客。
java層調用很非常簡單,這裡就不貼了。
靜態方法的訪問總結為兩步:
? 首先通過GetStaticMethodID在給定類中查找方法
如:jmethodID mid = (*env)->GetStaticMethodID(env,cls,”changeStr”,”()V”);
? 通過CallStaticMethod調用
如:(*env)->CallStaticVoidMethod(env, cls, mid);
jni中定義的訪問靜態方法的函數有如下一些:
jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*); jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...); jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list); jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...); jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID, va_list); jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...); jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list); jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...); jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list); jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...); jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list); jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...); jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list); jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...); jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list); jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*); jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...) __NDK_FPABI__; jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list) __NDK_FPABI__; jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) __NDK_FPABI__; jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...) __NDK_FPABI__; jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list) __NDK_FPABI__; jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) __NDK_FPABI__; void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...); void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list); void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
結合上面總結的流程和jni.h中定義的函數,寫如下測試代碼:
代碼功能:調用java層的靜態方法,修改靜態字段的值,把修改後的字段的值使用TextView顯示出來。
void native_staticMethod(JNIEnv * env, jobject obj){ LOGE("native_staticMethod"); //1.獲得類中方法id jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetStaticMethodID(env,cls,"changeStr","()V"); if (mid == NULL) { LOGE("GetStaticMethodID error"); return; /* method not found */ } LOGE("GetStaticMethodID sucess"); //2.調用CallStaticMethod函數調用對應函數. (*env)->CallStaticVoidMethod(env, cls, mid); }
注冊方法的數組:
static JNINativeMethod gMethods[] = { {"sayHello", "([I)I", (void *)native_sayHello}, {"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry}, {"accessJava","()V",(void *)native_accessJava}, {"accessinstanceJava","()V",(void *)native_accessinstanceJava}, {"staticMethod","()V",(void *)native_staticMethod}, };
添加了staticMethod方法的注冊。
java層的調用:
public class MainActivity extends AppCompatActivity { TextView textView = null; static String s = "java str"; String ss = "instance str"; static { System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.text); staticMethod(); textView.setText(s); } public native int sayHello(int []arr); public native String[] arrayTry(String [] arr); public native void accessJava(); public native void accessinstanceJava(); public native void staticMethod(); public static void changeStr(){ s = "chang str"; } }
訪問實例方法與訪問靜態方法類似,要注意的主要是:實例方法是屬於對象jobject的,而靜態方法是屬於類的。
訪問普通的實例方法的步驟還是總結為兩步:
? 首先通過GetMethodID在給定類中查找方法
如:jmethodID mid = (*env)->GetStaticMethodID(env,cls,”changeStr”,”()V”);
? 通過CallMethod調用
如:(*env)->CallStaticVoidMethod(env, obj, mid);
jni.h中定義的訪問實例方法的相關函數有:
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list); jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...); jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list); jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...); jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list); jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...); jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list); jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...); jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list); jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...); jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list); jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...); jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list); jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__; jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__; jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__; jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__; jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__; jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__; void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list); void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*); jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*); jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass, jmethodID, ...) __NDK_FPABI__; jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list) __NDK_FPABI__; jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*) __NDK_FPABI__; jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass, jmethodID, ...) __NDK_FPABI__; jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list) __NDK_FPABI__; jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*) __NDK_FPABI__; void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass, jmethodID, ...); void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list); void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*);
我們看到了很多Nonvirtual方法,這是jni提供用來訪問被子類賦給的父類的方法的,使用步驟如下:
調用被子類覆蓋的父類方法: JNI支持用CallNonvirtualMethod滿足這類需求:
? GetMethodID獲得method ID
? 調用CallNonvirtualVoidMethod, CallNonvirtualBooleanMethod
上述,等價於如下Java語言的方式:
super.f();
CallNonvirtualVoidMethod可以調用構造函數
你可以像調用實例方法一樣,調用構造方法,只是此時構造函數的名稱叫做””.
綜合上面三個知識點,我們設計如下代碼時間這些知識:
1.在c函數中調用String類的構造函數新建一個字符串對象。
2.調用java的實例方法,傳入我們1中構建的字符串對象,修改TextView的內容。
代碼如下:
void native_instanceMethod(JNIEnv * env, jobject obj){ LOGE("native_instanceMethod"); //1.使用String類的構造函數構造String //1.1找到String類 jclass stringClass = (*env)->FindClass(env, "java/lang/String"); if (stringClass == NULL) { LOGE("FindClass error"); return; } //1.2找到String類的構造函數 jmethodID cid = (*env)->GetMethodID(env, stringClass, "", "([C)V"); if (cid == NULL) { LOGE("GetMethodID error"); return; /* exception thrown */ } //1.3創建字符數組 jint len = 10; jcharArray elemArr = (*env)->NewCharArray(env, len); if (elemArr == NULL) { LOGE("NewCharArray error"); return; /* exception thrown */ } jchar java_char[]={97,98,99,100,101,102,103,104,105,106};//abcdefghij //1.4設置字符數組 (*env)->SetCharArrayRegion(env, elemArr, 0, len, java_char); //1.5 創建一個字符串對象 jstring result = (*env)->NewObject(env, stringClass, cid, elemArr); //2.獲得類中方法id jclass cls = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env,cls,"changeTextView","(Ljava/lang/String;)V"); if (mid == NULL) { LOGE("GetMethodID error"); return; /* method not found */ } LOGE("GetMethodID sucess"); //2.調用Call Method函數調用對應函數. (*env)->CallVoidMethod(env, obj, mid,result); }
注冊方法的數組:
static JNINativeMethod gMethods[] = { {"sayHello", "([I)I", (void *)native_sayHello}, {"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry}, {"accessJava","()V",(void *)native_accessJava}, {"accessinstanceJava","()V",(void *)native_accessinstanceJava}, {"staticMethod","()V",(void *)native_staticMethod}, {"instanceMethod","()V",(void *)native_instanceMethod}, };
java層調用:
static JNINativeMethod gMethods[] = { {"sayHello", "([I)I", (void *)native_sayHello}, {"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry}, {"accessJava","()V",(void *)native_accessJava}, {"accessinstanceJava","()V",(void *)native_accessinstanceJava}, {"staticMethod","()V",(void *)native_staticMethod}, {"instanceMethod","()V",(void *)native_instanceMethod}, };
訪問實例方法的實驗到此結束。
每次獲得Field和Method IDS都比較耗時,如果我們需要多次獲取他們,那就應該把它們緩存起來,這樣以後用的時候就可以直接用了,從而節約了時間。
緩存的方式可以使用局部static字段緩存,也可以在類的初始化時,一次性緩存好全部的Field 和 Method IDs。
上述第一次使用緩存的方式,每次都有與NULL的判斷,並且可能有一個無害的競爭條件。
而初始化類時,同時初始化JNI層對該類成員的緩存,可以彌補上述缺憾。
首先比較Java/native和Java/Java
前者因下述原因可能會比後者慢:
? Java/native與Java/Java的調用約定不同. 所以,VM必須在調用前,對參數和調用
棧做特殊准備
? 常用的優化技術是內聯. 相比Java/Java調用,Java/native創建內聯方法很難
粗略估計:執行一個Java/native調用要比Java/Java調用慢2-3倍. 也可能有一些VM實
現,Java/native調用性能與Java/Java相當。(此種虛擬機,Java/native使用Java/Java
相同的調用約定)。
其次比較native/Java與Java/Java
native/Java調用效率可能與Java/Java有10倍的差距,因為VM一般不會做Callback的
優化。
最後關於字段訪問
對於field的訪問,將沒什麼不同,只是通過JNI訪問某對象結構中某個位置的值。
簡介RecyclerView是Google在android-supportv7包中推出的一個新的控件,該控件的主要作用是用於替代ListView、GridView,相比較
在很多地方我們都會用到縱向列表樣式的菜單,比如微信首頁的我、發現頁面,微博的首頁的我頁面,QQ的動態頁面等等等等,大多數的應用中都會存在這樣的頁面。我們怎樣實現這種頁面比
AndroidStudio 的SVN 安裝和使用方法與我以前用的其他IDE 都有很大差別,感覺特麻煩,網上相關資料很少,貌似現在 Git 比較流行,之前有用過 githu
適配:即當前應用在相同的手機上面顯示相同的效果。適配前需要首先確定當前手機所屬像素密度類型(如:xhdpi、hdpi、mdpi等),然後計算其像素密度,按一定比例給出界面