jni开发入门

jni开发流程

这里使用Cmake方式,简单方便(需要AS2.2以上)
1.准备环境 下载NDK、CMake、LLDB打开AS Settings->Android SDK->SDK tools,点击右下角Show Package Details下载最新的NDK、Cmake、LLDB

  1. 在项目main目录下新建cpp文件夹用于存放C/C++代码(文件夹名字可以随意取)
  2. 在项目目录下创建CMakeLists目录下创建CMakeLists.txt(文件名不可改),在CMakeLists中输入以下内容内容:
    cmake_minimum_required(VERSION 3.4.1)
    add_library( # Specifies the name of the library.
            #设置生成库的名字
            test-lib
    
            # Sets the library as a shared library.可以暂时不用管,就写SHARED
            SHARED
    
            # 设置c++代码路径(C++代码相对于CMakeList的路径),有多个C++文件则添加多个
            src/main/cpp/test.cpp
            src/main/cpp/test1.cpp
            )
    # 从系统库中查找依赖库
    find_library( # Sets the name of the path variable.
            # 设置依赖库的名字,下面链接库的时候会用到
            log-lib
    
            # Specifies the name of the NDK library that
            # you want CMake to locate.
    
            # 查找log依赖库
            # {sdk-path}/ndk-bundle/sysroot/usr/include/android/log.h
            log)
    
    # 配置库的依赖关系(链接关系)
    target_link_libraries( # Specifies the target library.
            # 目标库
            test-lib
    
            # Links the target library to the log library
            # included in the NDK.
            # 依赖库,可以是多个
            ${log-lib})
  1. 修改项目目录下gradle.build文件,在android{}和defaultConfig{}里面分别增加externalNativeBuild:
    defaultConfig {
        applicationId "anative.test.example.com.testnative"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        externalNativeBuild {
            cmake {
                cppFlags ''
                abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
            }
        }
    }

    externalNativeBuild {
        cmake {
            path file('CMakeLists.txt')
        }
    }
  1. 在java代码中新增native接口
    package anative.test.example.com.testnative.jni;
    
    public class Test {
        public native String getNativeString();
        public native void showToast(Context context);
        public native void getSignature(Context context);
    }

  1. 这个时候这些native接口会报红,我们把鼠标放在函数上点击键盘alt+enter,选择create function xxx就会
    在cpp文件中自动生成相对应的native方法(也有可能AS在main下创建一个jni文件夹然后在里面创建一个.c文件,可以把这些copy到自己创建的文件中可以把这个系统
    创建的文件添加到CMakeLists中去):
    #include <jni.h>
    
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_anative_test_example_com_testnative_cpp_Test_getNativeString(JNIEnv *env, jobject instance) {
        return env->NewStringUTF(returnValue);
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_anative_test_example_com_testnative_cpp_Test_showToast(JNIEnv *env, jobject instance,
                                                                jobject context) {
    
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_anative_test_example_com_testnative_cpp_Test_getSignature(JNIEnv *env, jobject instance, jobject context) {
    
        // TODO
    
        return env->NewStringUTF(returnValue);
    }
  1. 用C/C++实现这些函数(c和c++的实现有点区别,在这里我全部使用c++实现,所以我创建的jni文件以.cpp结尾)
    #include <jni.h>
    
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_anative_test_example_com_testnative_cpp_Test_getNativeString(JNIEnv *env, jobject instance) {
        return env->NewStringUTF("this is from jni");
    }
    
    extern "C"
    JNIEXPORT void JNICALL
    Java_anative_test_example_com_testnative_cpp_Test_showToast(JNIEnv *env, jobject instance,
                                                                jobject context) {
    
        jclass toastClass = env->FindClass("android/widget/Toast");
        //获取Toast.LENGTH_SHORT静态对象
        jfieldID toastLengthId = env->GetStaticFieldID(toastClass, "LENGTH_SHORT", "I");
        jint toastLength = env->GetStaticIntField(toastClass, toastLengthId);
        //调用makeText返回toast对象
        jmethodID makeTextId = env->GetStaticMethodID(toastClass, "makeText",
                                                      "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
        jstring message = env->NewStringUTF("use jni to show toast");
        jobject makeText = env->CallStaticObjectMethod(toastClass, makeTextId, context, message,
                                                       toastLength);
        //调用toast.show()方法
        jclass showClass = env->GetObjectClass(makeText);
        jmethodID showId = env->GetMethodID(showClass, "show", "()V");
        env->CallVoidMethod(makeText, showId);
    
    }
    
    extern "C"
    JNIEXPORT jstring JNICALL
    Java_anative_test_example_com_testnative_cpp_Test_getSignature(JNIEnv *env, jobject instance,
                                                                   jobject context) {
    
        jclass cContext = env->GetObjectClass(context);
        //获取context.getPackageName方法id
        jmethodID packageNameId = env->GetMethodID(cContext, "getPackageName", "()Ljava/lang/String;");
        //获取packageName
        jstring packageName = static_cast<jstring>(env->CallObjectMethod(context, packageNameId));
        //获取PackageManager.GET_SIGNATURES
        jclass cPackageManager = env->FindClass("android/content/pm/PackageManager");
        jfieldID getSignaturesId = env->GetStaticFieldID(cPackageManager, "GET_SIGNATURES", "I");
        jint getSignatures = env->GetStaticIntField(cPackageManager, getSignaturesId);
        //调用getPackageManager方法获取PackageManager对象
        jmethodID getPackageManagerId = env->GetMethodID(
                cContext, "getPackageManager", "()Landroid/content/pm/PackageManager;");
        jobject packageManager = env->CallObjectMethod(context, getPackageManagerId);
        //调用getPackageInfo方法返回packageInfo对象
        jmethodID getPackageInfoId = env->GetMethodID(
                env->GetObjectClass(packageManager), "getPackageInfo",
                "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
        jobject packageInfo = env->CallObjectMethod(packageManager, getPackageInfoId, packageName,
                                                    getSignatures);
        //以上就完成获取PackageInfo对象的获取,PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES);
    
        //获取signatures成员
        jfieldID signaturesId = env->GetFieldID(
                env->GetObjectClass(packageInfo), "signatures", "[Landroid/content/pm/Signature;");
        //从包信息获得签名数组 Signature[] signatures = packageInfo.signatures;
        jobjectArray singnatures = (jobjectArray) env->GetObjectField(packageInfo, signaturesId);
        //得到应用签名signatures[0]
        jobject signature = env->GetObjectArrayElement(singnatures, 0);
        //将signature对象转化为string
        jmethodID toCharStringId = env->GetMethodID(
                env->GetObjectClass(signature), "toCharsString", "()Ljava/lang/String;");
        jstring result = (jstring) env->CallObjectMethod(signature, toCharStringId);
    
        return result;
    }

  1. 然后build工程,我们在build/intermediates/cmake/obj下面看到我们生成的.so库了
  2. 在代码里面加载我们的so库之后就可以使用这些native方法了
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            System.loadLibrary("test-lib");
    
            final Test test = new Test();
            findViewById(R.id.test1).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e("test", test.getNativeString());
                }
            });
            findViewById(R.id.test2).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    test.showToast(MainActivity.this);
                }
            });
            findViewById(R.id.test3).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.e("test", test.getSignature(MainActivity.this));
                }
            });
        }
    }
  1. 点击运行就ok(AS会自动把.so库放到apk里面,当然我们也可以把.so库拿出来手动放到工程里面)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。