編輯:關於Android編程
在一些Android應用的開發中,需要通過JNI和Android NDK工具實現JAVA和C/C++之間的相互調用。
Java Native Interface (JNI)標准是java平台的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI是本地編程接口,它使得在Java虛擬機(VM)內部運行的Java代碼能夠與用其它編程語言(如C、C++和匯編語言)編寫的應用程序和庫進行交互操作。
由於Android的應用層的類都是以Java寫的,這些Java類編譯為Dex型式的Bytecode之後,必須靠Dalvik虛擬機(VM: Virtual Machine)來執行。在執行Java類的過程中,如果Java類需要與C組件溝通時,VM就會去載入C組件,然後讓Java的函數順利地調用到C組件的函數。此時,VM扮演著橋梁的角色,讓Java與C組件能通過標准的JNI介面而相互溝通。
在實際應用中這兩者之間的調用關系可以歸納為以下四種方式:
1. 在應用的JAVA代碼中調用NDK中C/C++實現的函數。
2. 在NDK開發中的C/C++代碼調用應用中JAVA類的靜態函數。
3. 在NDK開發中的C/C++代碼調用應用中JAVA類當前傳入NDK中的實例的函數。
4. 在NDK開發中的C/C++代碼調用應用中JAVA類新建實例的函數。
下面我們就怎樣在Eclipse中實現JNI編碼和四種調用方式加以闡述。
一、在Eclipse中建立一個包含JNI開發的工程。
在這裡我們不直接導入NDK中的hello-jni來說明JNI的使用方法。而是新建立一個工程,來說明怎樣建立一個包含JNI的工程。
第一步:建立一個Andriod工程JniDemo,在該工程的根目錄下建立一個叫jni的目錄,在jni目錄下建立一個叫Android.mk的文件,(當然你也可以從其他地方,比如ndk樣例代碼hello-jni中將裡面的Android.mk復制過來修改)。Android.mk裡面的內容如下所示
LOCAL_PATH :=$(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE:= demo-jni
LOCAL_SRC_FILES := demo-jni.c
include$(BUILD_SHARED_LIBRARY)
關於這幾句話的含義,在這裡不再贅述。網上搜下,就可以很明白。
然後在jni目錄下生成demo-jni.c文件。實現的接口的內容。
現在選中工程中的jni目錄,點擊鼠標右鍵,選Refresh,jni目錄中的文件就顯示在工程的jni目錄下了。
第二步:設置jni的編譯環境。選中工程中的根目錄JniDemo,點擊鼠標右鍵,選Properties。彈出對話框,選中列表中的Builders。如圖一所示:
圖一:JniDemo特性設置對話框
點擊對話框右端的new按鈕,彈出“Choose configuration type”對話框,如圖二,選擇Program,點擊對話框下面的OK按鈕。
圖二:選擇配置類型
現在我們打開了”Edit Configuration”對話框,在Name對應的文本框中輸入名字JniBuilder(當然也可是你喜歡的其他名字).在Main選項下,在Location中輸入cygwin系統中bash.exe的絕對路徑。我這裡是c:\cygwin\bin\bash.exe(c:\cygwin\為我的系統中cygwin的安裝目錄,這裡要根據你的電腦中cygwin的安裝目錄來確定),在Working Directory中輸入c:\cygwin\bin\.在Arguments中輸入--login -c "cd /cygdrive/d/study/JniDemo && /cygdrive/d/android-ndk-r6b/ndk-build"。這裡/cygdrive/d/study/JniDemo為工程根目錄,/cygdrive/d/android-ndk-r6b為NDK的安裝目錄。這兩個目錄參數根據你的工程目錄和ndk的安裝目錄而定。注意的是驅動器要采用cygwin的方式。(比如:Windows系統下的D:對應/cygdrive/d,其余類推)。設置結果如圖三所示,然後點擊OK按鈕即可。
圖三:編輯JNI配置參數
二、演示四種調用方式
演示界面如圖四所示,四個按鈕分別測試四種調用方式。
圖四:演示界面圖
分別點擊按鈕Test1, Test2, Tes3, Test四的測試結果如圖五、六、七、八所示。
圖五:點擊Test1的測試結果
圖六:點擊Test2的測試結果
圖七:點擊Test3的測試結果
圖八:點擊Test4的測試結果
Test1演示在應用中調用NDK中C/C++實現的函數。JAVA代碼和C代碼分別為:
JAVA代碼:
[java]view plaincopy
Buttonbtn01=(Button)findViewById(R.id.Button01);
btn01.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
TextViewtv=(TextView)findViewById(R.id.tv01);
tv.setText(stringFromJNI());
showMessage(JniDemoActivity.this,"JNItest1",stringFromJNI());
}
});
C代碼:
view plaincopy
JstringJava_study_jnidemo_JniDemoActivity_stringFromJNI(JNIEnv*env,jobjectthiz)
{
return(*env)->NewStringUTF(env,"JniDemo,HellofromJNI!");
}
lTest2靜態調用。JAVA代碼和C代碼分別為:
JAVA代碼:
[java]view plaincopy
//測試C/C++中對JAVA函數的靜態回調
Buttonbtn02=(Button)findViewById(R.id.Button02);
btn02.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
intret=jniStaticShowMessage(JniDemoActivity.this,"JNItest2","teststaticcallbackMessage");
TextViewtv=(TextView)findViewById(R.id.tv01);
if(ret==0)
{
tv.setText("testJNIstaticcallbacksuccessed");
}
else
{
tv.setText("testJNIstaticcallbackfialed");
}
}
});
C代碼:
view plaincopy
JintJava_study_jnidemo_JniDemoActivity_jniStaticShowMessage(JNIEnv*env, view plaincopy
jobjectthiz,jobjectctx,jstringstrTitle,jstringstrMessage)
{
jclasscls=(*env)->FindClass(env,"study/jnidemo/JniDemoActivity");
//jclasscls=(*env)->GetObjectClass(env,thiz);
if(cls!=NULL)
{
jmethodIDid=(*env)->GetStaticMethodID(env,cls,"staticShowMessage",
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
if(id!=NULL)
{
return(*env)->CallStaticIntMethod(env,cls,id,ctx,strTitle,strMessage);
}
}
return1;
}
lTest3當前實例調用:JAVA代碼和C代碼分別為:
JAVA代碼:
[java]view plaincopy
Buttonbtn03=(Button)findViewById(R.id.Button03);
btn03.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
strTest="[messagehaschangednow]";
intret=jniShowMessage(JniDemoActivity.this,"JNItest3","testcallbackincurrentinstance");
TextViewtv=(TextView)findViewById(R.id.tv01);
if(ret==0)
{
tv.setText("testJNIcallbacksuccessed");
}
else
{
tv.setText("testJNIcallbackfialed");
}
}
});
C代碼:
view plaincopy
JintJava_study_jnidemo_JniDemoActivity_jniShowMessage(JNIEnv*env,jobjectthiz,
jobjectctx,jstringstrTitle,jstringstrMessage)
{
jclasscls=(*env)->GetObjectClass(env,thiz);
if(cls!=NULL)
{
jstringstr;
jmethodIDstrTest_id=(*env)->GetMethodID(env,cls,"getTestString","()Ljava/lang/String;");
if(strTest_id!=NULL)
{
str=(*env)->CallObjectMethod(env,thiz,strTest_id);
}
jmethodIDshowMessage_id=(*env)->GetMethodID(env,cls,"showMessage",
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
if(showMessage_id!=NULL)
{
return(*env)->CallIntMethod(env,thiz,showMessage_id,ctx,
strTitle,combine_jstring(env,strMessage,str));
}
}
return1;
}
lTest4新建實例調用:JAVA代碼和C代碼分別為:
JAVA代碼:
[java]view plaincopy
Buttonbtn04=(Button)findViewById(R.id.Button04);
btn04.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
strTest="[messagehaschangednow]";
intret=jniInstanceShowMessage(JniDemoActivity.this,
JNItest4","testcallbackinnewinstance");
TextViewtv=(TextView)findViewById(R.id.tv01);
if(ret==0)
{
tv.setText("testJNInewinstancesuccessed");
}
else
{
tv.setText("testJNInewinstancefialed");
}
}
});
C代碼:
view plaincopy
jintJava_study_jnidemo_JniDemoActivity_jniInstanceShowMessage(JNIEnv*env,jobjectthiz,
jobjectctx,jstringstrTitle,jstringstrMessage)
{
jclasscls=(*env)->FindClass(env,"study/jnidemo/JniDemoActivity");
if(cls!=NULL)
{
//getinstance
jmethodIDconstuctor_id=(*env)->GetMethodID(env,cls,"","()V");
if(constuctor_id!=NULL)
{
jobjectobj=(*env)->NewObject(env,cls,constuctor_id);
if(obj!=NULL)
{
jstringstr;
jmethodIDstrTest_id=(*env)->GetMethodID(env,cls,"getTestString","()Ljava/lang/String;");
if(strTest_id!=NULL)
{
str=(*env)->CallObjectMethod(env,obj,strTest_id);
}
jmethodIDshowMessage_id=(*env)->GetMethodID(env,cls,"showMessage",
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
if(showMessage_id!=NULL)
{
return(*env)->CallIntMethod(env,obj,showMessage_id,ctx,strTitle,combine_jstring(env,strMessage,str));
}
}
}
}
return1;
}
Test1和Test2都是常規的調用,在這裡不做解釋了。現在我們看看Test3和Test4的區別,在Test3中,strTest=" [message has changed now]"在相應的代碼中都做了賦值。但是在Test4中並沒有改變,還是初始值。這是因為Test創建了一個新實例,和應用的JAVA代碼中所賦值的實例並不是同一個。因此才出現了不同的結果。
附完整的JAVA和C代碼
JAVAD代碼:JniDemoActivity.java
[java]view plaincopy
packagestudy.jnidemo;
importandroid.app.Activity;
importandroid.app.AlertDialog;
importandroid.os.Bundle;
importandroid.widget.Button;
importandroid.view.View;
importandroid.widget.TextView;
importandroid.content.Context;
importandroid.content.DialogInterface;
publicclassJniDemoActivityextendsActivity{
publicStringstrTest="[initialmessage]";
/**Calledwhentheactivityisfirstcreated.*/
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findAndModifyButton();
}
publicStringgetTestString()
{
returnstrTest;
}
//測試JAVA的NDK調用
publicnativeStringstringFromJNI();
//測試C/C++中對JAVA函數的靜態回調
publicnativestaticintjniStaticShowMessage(Contextctx,StringstrTitle,StringstrMessage);
//測試實例中C/C++中對JAVA類的函數的調用
publicnativeintjniShowMessage(Contextctx,StringstrTitle,StringstrMessage);
//測試創建新實例C/C++對JAVA類的函數的調用
publicnativeintjniInstanceShowMessage(Contextctx,StringstrTitle,StringstrMessage);
static{
System.loadLibrary("demo-jni");
}
staticintstaticShowMessage(Contextctx,StringstrTitle,StringstrMessage)
{
AlertDialog.Builderbuilder=newAlertDialog.Builder(ctx);
builder.setTitle(strTitle);
builder.setMessage(strMessage);
builder.setPositiveButton("確定",
newDialogInterface.OnClickListener(){
publicvoidonClick(DialogInterfacedialog,intwhichButton){
}
});
builder.show();
return0;
}
publicintshowMessage(Contextctx,StringstrTitle,StringstrMessage)
{
returnstaticShowMessage(ctx,strTitle,strMessage);
}
privatevoidfindAndModifyButton()
{
//測試JAVA的NDK調用
Buttonbtn01=(Button)findViewById(R.id.Button01);
btn01.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
TextViewtv=(TextView)findViewById(R.id.tv01);
tv.setText(stringFromJNI());
showMessage(JniDemoActivity.this,"JNItest1",stringFromJNI());
}
});
//測試C/C++中對JAVA函數的靜態回調
Buttonbtn02=(Button)findViewById(R.id.Button02);
btn02.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
intret=jniStaticShowMessage(JniDemoActivity.this,"JNItest2","teststaticcallbackMessage");
TextViewtv=(TextView)findViewById(R.id.tv01);
if(ret==0)
{
tv.setText("testJNIstaticcallbacksuccessed");
}
else
{
tv.setText("testJNIstaticcallbackfialed");
}
}
});
//測試實例中C/C++中對JAVA類的函數的調用
Buttonbtn03=(Button)findViewById(R.id.Button03);
btn03.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
strTest="[messagehaschangednow]";
intret=jniShowMessage(JniDemoActivity.this,"JNItest3","testcallbackincurrentinstance");
TextViewtv=(TextView)findViewById(R.id.tv01);
if(ret==0)
{
tv.setText("testJNIcallbacksuccessed");
}
else
{
tv.setText("testJNIcallbackfialed");
}
}
});
//測試創建新實例C/C++對JAVA類的函數的調用
Buttonbtn04=(Button)findViewById(R.id.Button04);
btn04.setOnClickListener(newButton.OnClickListener()
{
publicvoidonClick(Viewv)
{
strTest="[messagehaschangednow]";
intret=jniInstanceShowMessage(JniDemoActivity.this,"JNItest4","testcallbackinnewinstance");
TextViewtv=(TextView)findViewById(R.id.tv01);
if(ret==0)
{
tv.setText("testJNInewinstancesuccessed");
}
else
{
tv.setText("testJNInewinstancefialed");
}
}
});
}
}
C代碼demo-jni.cpp
view plaincopy
#include
#include
//加載此動態庫時系統自動首先加載
jintJNI_OnLoad(JavaVM*vm,void*reserved)
{
JNIEnv*env;
if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_4)!=JNI_OK)
{
return-1;
}
returnJNI_VERSION_1_4;
}
jstring
Java_study_jnidemo_JniDemoActivity_stringFromJNI(JNIEnv*env,jobjectthiz)
{
return(*env)->NewStringUTF(env,"JniDemo,HellofromJNI!");
}
jstring
combine_jstring(JNIEnv*env,jstringstr1,jstringstr2)
{
jbooleanb_ret;
constchar*s1=(*env)->GetStringUTFChars(env,str1,&b_ret);
constchar*s2=(*env)->GetStringUTFChars(env,str2,&b_ret);
intn1=strlen(s1);
intn2=strlen(s2);
char*new_str=(char*)malloc(n1+n2+1);
memset(new_str,0,n1+n2+1);
strcat(new_str,s1);
strcat(new_str,s2);
jstringret_str=(*env)->NewStringUTF(env,(constchar*)new_str);
free(new_str);
returnret_str;
}
jint
Java_study_jnidemo_JniDemoActivity_jniStaticShowMessage(JNIEnv*env,jobjectthiz,
jobjectctx,jstringstrTitle,jstringstrMessage)
{
jclasscls=(*env)->FindClass(env,"study/jnidemo/JniDemoActivity");
//jclasscls=(*env)->GetObjectClass(env,thiz);
if(cls!=NULL)
{
jmethodIDid=(*env)->GetStaticMethodID(env,cls,
"staticShowMessage",
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
if(id!=NULL)
{
return(*env)->CallStaticIntMethod(env,cls,id,ctx,strTitle,strMessage);
}
}
return1;
}
//在當前已有的JAVA實例中調用
jint
Java_study_jnidemo_JniDemoActivity_jniShowMessage(JNIEnv*env,jobjectthiz,
jobjectctx,jstringstrTitle,jstringstrMessage)
{
jclasscls=(*env)->GetObjectClass(env,thiz);
if(cls!=NULL)
{
jstringstr;
jmethodIDstrTest_id=(*env)->GetMethodID(env,cls,"getTestString",
"()Ljava/lang/String;");
if(strTest_id!=NULL)
{
str=(*env)->CallObjectMethod(env,thiz,strTest_id);
}
jmethodIDshowMessage_id=(*env)->GetMethodID(env,cls,"showMessage",
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
if(showMessage_id!=NULL)
{
return(*env)->CallIntMethod(env,thiz,showMessage_id,ctx,
strTitle,combine_jstring(env,strMessage,str));
}
}
return1;
}
//在新建JAVA實例中調用
jint
Java_study_jnidemo_JniDemoActivity_jniInstanceShowMessage(JNIEnv*env,jobjectthiz,
jobjectctx,jstringstrTitle,jstringstrMessage)
{
jclasscls=(*env)->FindClass(env,"study/jnidemo/JniDemoActivity");
if(cls!=NULL)
{
//getinstance
jmethodIDconstuctor_id=(*env)->GetMethodID(env,cls,"","()V");
if(constuctor_id!=NULL)
{
jobjectobj=(*env)->NewObject(env,cls,constuctor_id);
if(obj!=NULL)
{
jstringstr;
jmethodIDstrTest_id=(*env)->GetMethodID(env,cls,"getTestString",
"()Ljava/lang/String;");
if(strTest_id!=NULL)
{
str=(*env)->CallObjectMethod(env,obj,strTest_id);
}
jmethodIDshowMessage_id=(*env)->GetMethodID(env,cls,"showMessage",
"(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I");
if(showMessage_id!=NULL)
{
return(*env)->CallIntMethod(env,obj,showMessage_id,ctx,
strTitle,combine_jstring(env,strMessage,str));
}
}
}
}
return1;
}
<?xml version=1.0 encoding=utf-8?> <LinearLayout xmlns:android=http:
前言Promoted Actions是指一種操作按鈕,它不是放在actionbar中,而是直接在可見的UI布局中(當然這裡的UI指的是setContentView所管轄的
一、前言今天我們開啟Android系統篇的文章了,其實一直想弄,只是之前一直沒有太多深入的了解,最近又把這塊拿出來好好看了一下,所以想從新梳理一下,來看看Android中
Android程序是怎麼從源碼變成可以安裝使用的apk的流程官方版詳細版上面就是一個關於構建過程的一個典型的流程圖。輸出生成的apk在app/build/outputs/