Android動態註冊jni

來源:互聯網
上載者:User

標籤:包含   ring   ted   程式崩潰   efi   share   eset   不同的   研究   

最近整理了以前關於jni的代碼,這裡梳理下,供以後參考。

JNI簡介 

JNI是Java Native Interface的縮寫,它提供了若干的介面實現了Java和其他語言的通訊(主要是c、c++)。從Java1.1開始,JNI標準成為java平台的一部分,它允許Java代碼和其他語言寫的代碼進行互動。jni是Android中java和c++之間串連的橋樑,jni是jvm提供的一種與native方法對接的方式。

JNI的副作用

一旦使用JNI,JAVA程式就喪失了JAVA平台的兩個優點:

1、程式不再跨平台。要想跨平台,必須在不同的系統內容下重新編譯本地語言部分。

2、程式不再是絕對安全的,本地代碼的不當使用可能導致整個程式崩潰。一個通用規則是,你應該讓本地方法集中在少數幾個類當中。這樣就降低了JAVA和C之間的耦合性。

JNI使用情境

當你開始著手準備一個使用JNI的項目時,請確認是否還有替代方案。應用程式使用JNI會帶來一些副作用。下面給出幾個方案,可以避免使用JNI的時候,達到與本地代碼進行互動的效果:

1、JAVA程式和本地程式使用TCP/IP或者IPC進行互動。

2、當用JAVA程式串連本機資料庫時,使用JDBC提供的API。

3、JAVA程式可以使用分布式對象技術,如JAVA IDL API。

這些方案的共同點是,JAVA和C處於不同的線程,或者不同的機器上。這樣,當本地程式崩潰時,不會影響到JAVA程式。

下面這些場合中,同一進程內JNI的使用無法避免:

1、程式當中用到了JAVA API不提供的特殊系統內容才會有的特徵。而跨進程操作又不現實。

2、你可能想訪問一些己有的本地庫,但又不想付出跨進程調用時的代價,如效率,記憶體,資料傳遞方面。

3、JAVA程式當中的一部分代碼對效率要求非常高,如演算法計算,圖形渲染等。

總之,只有當你必須在同一進程中調用本地代碼時,再使用JNI。

JNI註冊方法

註冊方法有兩種:靜態註冊和動態註冊

靜態註冊

1,在Java檔案中定義native方法。

2,在cmd命令列模式中切換目錄到定義native方法class檔案(或者java檔案)存放位置。

3,用javah 和javac命令產生包含native方法的.h標頭檔。

4,實現native方法,用ndk-build編譯產生.so庫。

靜態註冊方法步驟比較繁瑣,在項目中我比較偏向動態註冊方法。

動態註冊JNI

首先建立一個Android項目,勾選上include c++ support,MainActivity.java中內容:

public class MainActivity extends AppCompatActivity {    // Used to load the ‘native-lib‘ library on application startup.    static {        System.loadLibrary("JNITest");    }    public String TAG = "MainActivity";    public JNITest jniTest;    TextView mTextView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        TextView tv = (TextView) findViewById(R.id.sample_text);        mTextView = (TextView)findViewById(R.id.textView);        jniTest = new JNITest();        int sum = jniTest.addInt(4,3);        // Example of a call to a native method         mTextView.setText(jniTest.getString()+"  "+sum);        Log.d(TAG,"set text after....");    }}

再建立一個java檔案定義所需要的native方法,這裡定義了兩個方法

package com.example.szq.testjni;public class JNITest {    public JNITest(){    }    public native String getString();    public native int addInt(int a,int b);}  

在src/main目錄下建立jni檔案夾,並建立JNITest.c和Android.mk兩個檔案

#include <stdio.h>#include <stdlib.h>#include <jni.h>#include <assert.h>#define JNIREG_CLASS "com/example/szq/testjni/JNITest" //定義native方法的java檔案//實現jstring jni_getstr(JNIEnv* jniEnv,jobject ob){    return (*jniEnv)->NewStringUTF(jniEnv,"動態註冊JNI test");}jint jni_add(JNIEnv* jniEnv,jobject ob, jint a,jint b){    return a+b;}static JNINativeMethod gMethods[] = {        {"getString", "()Ljava/lang/String;", (void*)jni_getstr},        {"addInt", "(II)I", (void*)jni_add},};static int registerNativeMethods(JNIEnv* env        , const char* className        , JNINativeMethod* gMethods, int numMethods) {    jclass clazz;    clazz = (*env)->FindClass(env, className);    if (clazz == NULL) {        return JNI_FALSE;    }    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {        return JNI_FALSE;    }    return JNI_TRUE;}/** 為所有類註冊本地方法*/static int registerNatives(JNIEnv* env) {    int re = registerNativeMethods(env, JNIREG_CLASS,gMethods,            sizeof(gMethods)/sizeof(gMethods[0]));    return re;}/** System.loadLibrary("lib")時會調用* 如果成功返回JNI版本, 失敗返回-1*/JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {    JNIEnv* env = NULL;    jint result = -1;    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {        return -1;    }    assert(env != NULL);    if (!registerNatives(env)) {//註冊        return -1;    }    //成功    result = JNI_VERSION_1_6;    return result;}

配置Android.mk

LOCAL_PATH := $(call my-dir)  include $(CLEAR_VARS)  LOCAL_MODULE := JNITestLOCAL_SRC_FILES := JNITest.cLOCAL_LDFLAGS += -lloginclude $(BUILD_SHARED_LIBRARY)

在命令列模式下切換到jni目錄,運行ndk-build會產生.so庫(前提是ndk環境先配置好),將.so檔案copy到src/main/libs中,構建項目,就能運行出下面的結果:

註:產生的.so檔案一定要在與jni同一層的libs檔案夾中

NDK自動編譯配置

配置build.gradle,因為在構建項目時,編譯器會自動載入gradle檔案,所以在gradle中加入編譯的任務(task)就能編譯jni中的c檔案了,配置如下:

apply plugin: ‘com.android.application‘android {    compileSdkVersion 26    defaultConfig {        applicationId "com.example.szq.testjni"        minSdkVersion 18        targetSdkVersion 26        versionCode 1        versionName "1.0"        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"        ndk{            moduleName "JNITest"            ldLibs "log", "z", "m"            abiFilters "armeabi", "armeabi-v7a", "x86"            //用於指定應用應該使用哪個標準庫,此處添加c++庫支援            stl "stlport_static"        //  支援stl            cFlags "-fexceptions"        // 支援exception        }        tasks.withType(JavaCompile) {            compileTask -> compileTask.dependsOn ‘ndkBuild‘, ‘copyJniLibs‘        }        sourceSets.main{            jniLibs.srcDirs = [‘libs‘]        }        externalNativeBuild {            cmake {                cppFlags ""            }        }    }    buildTypes {        release {            minifyEnabled false            proguardFiles getDefaultProguardFile(‘proguard-android.txt‘), ‘proguard-rules.pro‘        }    }    externalNativeBuild {        cmake {            path "CMakeLists.txt"        }    }}task ndkBuild(type: Exec) {//    def ndkDir = project.plugins.findPlugin(‘com.android.application‘).sdkHandler.getNdkFolder()    def ndkDir = project.android.ndkDirectory    commandLine "$ndkDir\\ndk-build.cmd", ‘-C‘, ‘src/main/jni‘,            "NDK_OUT=$buildDir/ndk/obj",            "NDK_APP_DST_DIR=$buildDir/ndk/libs/\$(TARGET_ARCH_ABI)"}task copyJniLibs(type: Copy) {   from fileTree(dir: file(buildDir.absolutePath + ‘/ndk/libs‘), include: ‘**/*.so‘)    into file(‘src/main/jniLibs‘)}dependencies {    implementation fileTree(dir: ‘libs‘, include: [‘*.jar‘])    implementation ‘com.android.support:appcompat-v7:26.1.0‘    implementation ‘com.android.support.constraint:constraint-layout:1.0.2‘    testImplementation ‘junit:junit:4.12‘    androidTestImplementation ‘com.android.support.test:runner:1.0.1‘    androidTestImplementation ‘com.android.support.test.espresso:espresso-core:3.0.1‘}

這樣直接構建項目,構建完成就能運行程式了。

在Android studio 3.0版本中添加了更加方便的CMake來編譯jni,設定檔是CMakeLists.txt,CMake會在以後的項目中經常用到,有興趣的可以一起研究下

Android動態註冊jni

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.