码迷,mamicode.com
首页 > 移动开发 > 详细

Android动态注册jni

时间:2018-01-24 16:57:33      阅读:246      评论:0      收藏:0      [点我收藏+]

标签:包含   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 := JNITest
LOCAL_SRC_FILES := JNITest.c
LOCAL_LDFLAGS += -llog
include $(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

标签:包含   ring   ted   程序崩溃   efi   share   eset   不同的   研究   

原文地址:https://www.cnblogs.com/cocoabird/p/8341963.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!