標籤:android style class c blog code
在某些情況下,java編程已經不能滿足我們的需要,比如一個複雜的演算法處理,這時候就需要用到jni技術;
- jni : java native interface
- jni 其實就是java和c/cpp之間進行通訊的一個介面規範,java可以調用c/cpp裡面的函數,同樣,c/cpp也可以調用java類的方法;
jni開發工具ndk的安裝:
在最新的ndk版本中,安裝ndk很簡單,只需要裝ndk的路徑配置到系統內容變數中即可;
在編譯的時候,進入工程根目錄;執行命令 ndk-build 即可完成編譯;
一、cpp與java
>>1.java檔案:TextOutput.java
package com.jnitest;import android.util.Log;public class TextOutput { private native String init(); static { System.loadLibrary("text"); } public String getString() { return init(); } public void sayHello() { Log.e("tag", "void sayHello"); } public String sayHello_() { Log.e("tag", "string sayHello_"); return "return string sayHello_"; } public static void sayHello__() { Log.e("tag", "static sayHello__"); }}
在該類中定義了一個本地方法init[native init],方法實現由接下來的cpp函數來實現;以及定義了其它幾個方法,由cpp檔案來調用;
>>2.cpp檔案:test.cpp
#include <jni.h>#include <android/log.h>#include <string.h>#ifndef _Included_com_jnitest_TextOutput#define _Included_com_jnitest_TextOutput#ifdef __cplusplusextern "C" {#endifJNIEXPORT jstring JNICALL Java_com_jnitest_TextOutput_init(JNIEnv *, jobject);#ifdef __cplusplus}JNIEXPORT jstring JNICALL Java_com_jnitest_TextOutput_init(JNIEnv * env, jobject obj) { jmethodID mid; // 方法標識id jclass cls = env->GetObjectClass(obj); // 類的對象執行個體 mid = env->GetMethodID(cls, "sayHello", "()V"); env->CallVoidMethod(obj, mid); jmethodID mid1; mid1 = env->GetMethodID(cls, "sayHello_", "()Ljava/lang/String;");// @1 jstring s1 = (jstring) env->CallObjectMethod(obj, mid1); // @2 jmethodID mid2; mid2 = env->GetStaticMethodID(cls, "sayHello__", "()V"); env->CallStaticVoidMethod(cls, mid2); return s1;}#endif#endif
先來看在cpp中定義的函數名:Java_com_jnitest_TextOutput_init
其實不難看出,java檔案與cpp檔案中函數名的配對定義方式為Java + 包名 + java類名 + 方法/函數名,中間用_分隔;其中兩個參數分別是:
- env:當前該線程的內容,包含線程裡面全部內容;
- obj:當前類的執行個體,指.java檔案的內容(在該例子中即是TextOutput類);
@1:擷取MethodId,該方法需要三個參數,分別jclass類的對象執行個體,函數名,函數簽名;其中函數簽名由兩部分構成(參數+函數傳回值);
查看簽名可以通過先進入到java檔案產生的class檔案目錄,執行命令:javap -s -p ClassName
產生如所示的函數簽名;
@2:執行該方法,在上面的例子中,將執行sayHello_方法
運行程式:
TextOutput to = new TextOutput();Log.e("tag", "" + to.getString());
二.c與java
在Activity中有一個int欄位,通過callback賦值
package com.example.hellojni;import android.app.Activity;import android.util.Log;import android.widget.TextView;import android.os.Bundle;public class HelloJni extends Activity { private static int si; private static void callback() { si = 123; } /** 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); Log.e("tag", "si=" + si); } public native String stringFromJNI(); static { System.loadLibrary("hello-jni"); }}
hello-jni.c
#include <string.h>#include <jni.h>/* * */jstring Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz) //env:當前該線程的內容,包含線程全部的東西;thiz:當前類的執行個體,指.java檔案的內容{ jint si; jfieldID fid; // 一個欄位,實際上對應java類裡面的一個欄位或屬性; jclass cls = (*env)->GetObjectClass(env, thiz); // 類的對象執行個體 jmethodID mid = (*env)->GetStaticMethodID(env, cls, "callback", "()V"); // 一個方法的id //(I)V (I)I if (mid == NULL) { return (*env)->NewStringUTF(env, "mid=null"); } (*env)->CallStaticVoidMethod(env, cls, mid); //調用callback方法 fid = (*env)->GetStaticFieldID(env, cls, "si", "I"); //取出si欄位 if (fid == NULL) { return (*env)->NewStringUTF(env, "fid=null"); } si = (*env)->GetStaticIntField(env, cls, fid); //取出欄位對應的值(fid欄位對應的值) return (*env)->NewStringUTF(env, "init success");}
運行上面的Activity,即可實現對si的賦值
3.加入連結庫
在程式開發過程中,會頻繁的用到調試,方式有很多種,下面要講的這一種是通過log列印資訊來列印程式運行時的一些狀態數值;
修改Android.mk檔案,添加一句代碼
include $(CLEAR_VARS)LOCAL_LDLIBS += -llog //LDLIBS:串連libs,後面跟的參數為需要連結的libs,-llog表示Android中的Log庫;include $(BUILD_SHARED_LIBRARY)
加入了log庫之後
即可以在c檔案中調用log列印輸出資訊:
__android_log_print(ANDROID_LOG_ERROR, "hello", "livingstone"); __android_log_print(ANDROID_LOG_DEBUG, "hello", "livingstone %d" ,23);