<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ### 注意 - 由于從Android studio2.2版本開始,AS集成了CMake插件,使用AS開發支持C++的項目,更加方便。一切相關的配置如Android.mk,Application.mk這些配置都交給了CMake來處理,所以后面的jni開發的某些操作,在新的CMake操作下,就不需要了。 ### 交叉編譯 - 交叉編譯:在一個平臺上去編譯另一個平臺上可以執行的本地代碼 - CPU平臺 arm、 X86 - 操作系統平臺 windows linux mac os - 原理 模擬不同平臺的特性去編譯代碼 ### JNI開發工具 ### - ndk native develop kit android提供的開發工具包 - ndk 目錄(基于r9d版本),隨著NDK版本的升級,功能的不斷完善,該目錄的各個子目錄也不相同 - docs---> 幫助文檔 - platforms---> 好多平臺版本文件夾,選擇時選擇項目支持的最小版本號對應的文件夾 - include 文件夾---> jni開發中常用的.h頭文件 - lib文件夾---> google打包好的提供給開發者使用的.so文件 - samples---> google官方提供的樣例工程,可以參考進行開發 - android-ndk-r9d\build\tools---> linux系統下的批處理文件,在交叉編譯時會自動調用 - ndk-build---> 交叉編譯的命令 - ndk-r15c的目錄結構如下圖 ![NDKr15c目錄](https://box.kancloud.cn/95b2c4a78d384d0c0f4b5127314df196_624x442.png =624x442) - cdt eclipse開發的插件用于高亮C的代碼(只是適用于eclipse,不過現在主流的是AS,eclipse幾乎快要淘汰) ### jni hello world #### jni開發步驟(以eclipse為開發工具) 1. 寫java代碼 聲明本地方法,用到native關鍵字,java中本地方法不用實現 2. 在項目根目錄下創建jni文件夾 3. 在jni文件夾下創建.c文件 - 本地函數的命名規則:Java_包名_類名_本地方法名 - JNIENV* env JNIEnv是結構體JNINativeInterface(接口函數指針表)的一級指針 - 結構體JNINativeInterface定義了大量的函數指針,這些函數指針在jni開發中很常用。(C語言中結構體中不能定義函數,可以定義函數指針) - env是結構體JNINativeInterface的二級指針 - (*env)-> 中“->“是間接引用函數符 指向結構體成員運算符,類似于結構成員運算符".";都是用來訪問結構體成員的, *env是一級指針 - (*env)-> 調用結構體中的函數指針 - 第二個參數jobject 在C中表示的java的對象即thiz就是調用這個本地函數的java對象,在本列子中就是MainActivity的實例 4. 導入<jni.h> 5. 創建Android.mk makefile告訴編譯器.c的源文件在什么地方,要生成的編譯對象的名字是什么? **Android.mk** ``` LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= hello//編譯完后生成的名字,指定了生成的動態鏈接庫的名字 LOCAL_SRC_FILES := hello.c//C的源文件,有多個.c文件用空格隔開,指定了C的源文件在哪 include $(BUILD_SHARED_LIBRARY) ``` 6. 進入到項目所在的根目錄,在DOS窗口,調用ndk-build編譯C代碼生成動態鏈接庫.so文件,文件的位置在lib-->armeabi-->.so 7. 在java代碼中加載動態鏈接庫System.loadlibrary("動態鏈接庫的名字");Android.mk中LOCAL_MODULE所指定的名字 8. 運行部署到模擬器或者真機上 以上是java調用C的步驟 **MainActivity**代碼如下 package com.wsc.jnihelloworld; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View view) { System.loadLibrary("hello"); String result =helloFromC(); Toast.makeText(this, result, Toast.LENGTH_SHORT).show(); } //聲明 本地方法 使用native關鍵字本地方法不用實現 public native String helloFromC(); } **hello.c**代碼如下 #include <stdlib.h> #include <stdio.h> #include <jni.h> /*jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )*/ //JNIEnv* env是結構體JNINativeInterface的二級指針 //JNIEnv是結構體JNINativeInterface(接口函數指針表)的一級指針 //接口函數指針表,該表定義了大量的函數,可以把C的數據類型轉換成java可以理解的數據類型,也可以把java的數據類型轉換成C可以理解的數據類型 //結構體JNINativeInterface定義了大量的函數指針,這些函數指針在jni開發中很常用 // (*env)-> 中“->“是間接引用函數符 指向結構體成員運算符,類似于結構成員運算符".";都是用來訪問結構體成員的, *env是一級指針 //jobject 在C中表示的java的對象即thiz就是調用這個本地函數的java對象,在本列子中就是MainActivity的實例 //上面的這2個參數env和thiz是規定死的,這兩個參數是JNI規則規定好的,如果java中還有其他參數,那么就在這2個參數后面延續,本列中沒有參數,所以后面就沒有 //c本地函數名命規則 Java_包名_類名_本地方法名 //jstring (*NewStringUTF)(JNIEnv*, const char*);//可以將char轉換成jstring類型 jstring Java_com_wsc_jnihelloworld_MainActivity_helloFromC(JNIEnv* env,jobject thiz){ char* cstr="hello from c!"; return (*env)->NewStringUTF(env,cstr); } Android.mk文件如下 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE:= hello LOCAL_SRC_FILES := hello.c include $(BUILD_SHARED_LIBRARY) ![](http://i.imgur.com/5A31JMD.png) ![](http://i.imgur.com/zHPulRp.png) ### JNI簡便開發流程 (以eclipse 為開發工具)### * ①寫java代碼,native聲明本地方法 * ②添加本地支持,右鍵單擊項目-->android tools-->add native support * 如果發現finish不能點擊需要給工作空間配置ndk目錄的位置 * window-->preference-->左側選擇android-->ndk 把ndk解壓的目錄指定出來 * ③如果寫的是.c文件,先修改一下生成的.cpp文件的擴展名,不要忘了相應修改Android.mk文件中LOCAL_SRC_FILES := hello.c * ④javah生成頭文件,在生成的頭文件中拷貝c的函數名.c的文件 * ⑤CDT解決CDT插件報錯 * 右鍵單擊項目選擇properties--->c/c++ general-->paths and symbolc--->include選項卡下,點擊ADD-->file system,選擇ndk目錄下,platforms文件夾對應平臺下(項目支持的最小版本)usr目錄下arch-arm-->include確定后會解決代碼提示和報錯的問題 * ⑥編寫C函數,如果需要單獨編譯一下C代碼,就在C/C++視圖工具欄處找到小錘子,錘一下,如果想直接運行到模擬器上,就不用小錘子了,控制臺會自動錘一下 * ⑦java代碼中不要忘了System.loadlibrary(),加載.so文件 **注意:Application.mk這個文件還是應該手動復制粘貼到jni目錄中,當APP運行到X86模擬器上時,在Application.mk中指定** APP_ABI := armeabi x86 APP_PLATFORM := android-14 編譯時會產生一個obj目錄如圖所示 ![](http://i.imgur.com/gpX3QYH.png) 運行時jni目錄和obj目錄是不起作用,只是編譯時起作用,打包時不會打到APK中 ![](http://i.imgur.com/VFDLssV.png) ### java傳遞int類型數據給C ### 實現:點擊按鈕,調用java的代碼,讓java傳遞一些參數給C,C處理后返還給java。 #### java 與 c之間的數據傳遞 #### public native int add(int x, int y); public native String sayHelloInC(String s); public native int[] arrElementsIncrease(int[] intArray); #### 在c代碼中使用logcat#### * Android.mk文件增加以下內容 LOCAL_LDLIBS += -llog//加載動態鏈接庫,第一個l是load的縮寫,log是so文件中文件名中除lib外的名字如liblog.so中的log * C代碼中增加以下內容 #include <android/log.h> #define LOG_TAG "System.out" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) * C代碼中使用logcat, 例: LOGI("info\n"); LOGD("debug\n"); * define C的宏定義,起別名,#define LOG_TAG "System.out",給"System.out"起別名為LOG_TAG * `#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)` * 給__android_log_print函數起別名,寫死了前兩個參數,第一個參數優先級,第二個TAG * `__VA_ARGS__`可變參數的固定寫法 * LOGI(...)在調用的時候,用法和printf()一樣。 > **打印LOG日志比較消耗性能,實際開發中應該注釋掉** #### C中調用java #### C中回調java中的方法,需要用到反射的知識,C的代碼中反射,拿到java類的字節碼,拿到字節碼就可以找到這里面的函數,創建對象,然后通過反射的方式調用方法 獲取方法簽名(java的方法可以重載,在一個類里面可以重載同一個方法,如何確定方法的唯一性,可以通過方法簽名,可以通過javap -s) * ①找到字節碼對象 * jclass (*FindClass)(JNIEnv*, const char*); * 第二個參數是要回調的java方法所在的(反射調用的)類的路徑即com.wsc.callbackjava.JNI * ②通過字節碼對象找到方法對象 * jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); * 第二個參數字節碼對象;第三個參數 要反射調用的java方法名;第三個參數是方法名,要回調的方法名,第四個參數是方法簽名(通過javap -s 包名+類名) * ③ 通過字節碼對象創建java對象(可選),如果本地方法和要回調的java方法在同一個類里面可以直接用jni傳過來的java對象,調用創建的method * jobject obj = (*env)->AllocObject(env, claz); * 當回調的方法和本地的方法不在一個類,需要通過剛創建的字節碼對象手動創建一個java對象 * 再通過這個對象來回調java方法 * 注意:如果創建的是一個activity對象,回調的方法還包含上下文,這個方法行不通,會報空指針異常 * ④通過對象調用方法 * void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); * 第二個參數調用java方法的對象;第三個參數要調用的jmethodID對象; 第四個參數是可選的參數 調用方法時接收的參數 ### C Java JNI三者相互之間數據類型的轉換 - Java中任何一種類型,在Jni中都有一種對應的類型,比如:String--->jstring,但是Java中String是雙字節的,在C++中不是雙字節的,這就涉及到一個字符串轉換,編碼的轉換 #### Java和C++字符串轉換 - string轉換為jstring ~~~ jstring c2j(JNIEnv* env, string cstr) { return env->NewStringUTF(cstr.c_str()); } ~~~ - jstring轉換為string ~~~ string j2c(JNIEnv* env, jstring jstr) { string ret; jclass stringClass = env->FindClass("java/lang/String"); jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); // 把參數用到的字符串轉化成java的字符 jstring arg = c2j(env, "utf-8"); jbyteArray jbytes = (jbyteArray)env->CallObjectMethod(jstr, getBytes, arg); // 從jbytes中,提取UTF8格式的內容 jsize byteLen = env->GetArrayLength(jbytes); jbyte* JBuffer = env->GetByteArrayElements(jbytes, JNI_FALSE); // 將內容拷貝到C++內存中 if(byteLen > 0) { char* buf = (char*)JBuffer; std::copy(buf, buf+byteLen, back_inserter(ret)); } // 釋放 env->ReleaseByteArrayElements(jbytes, JBuffer, 0); return ret; } ~~~ - jstring轉換成c語言的char* 類型 ~~~ /** * 把一個jstring轉換成一個c語言的char* 類型. */ char* _JString2CStr(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = (*env)->FindClass(env, "java/lang/String"); jstring strencode = (*env)->NewStringUTF(env, "GB2312"); jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes","(Ljava/lang/String;)[B"); jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,strencode); // String .getByte("GB2312"); jsize alen = (*env)->GetArrayLength(env, barr); jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE); if (alen > 0) { rtn = (char*) malloc(alen + 1); //"\0" memcpy(rtn, ba, alen); rtn[alen] = 0; } (*env)->ReleaseByteArrayElements(env, barr, ba, 0); return rtn; } ~~~ ### JNI開發 ### **JNI開發,應用運行時和“.c”的源碼沒有任何關系,而且打包時“.c”源文件也不會打進去,“.c”源文件只是在編譯階段起作用,真正運行時調用時,起作用的是“.so”這個動態鏈接庫**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看