美文网首页
jni开发入门

jni开发入门

作者: yaozhukuang | 来源:发表于2019-05-15 16:06 被阅读0次

    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库拿出来手动放到工程里面)

    相关文章

      网友评论

          本文标题:jni开发入门

          本文链接:https://www.haomeiwen.com/subject/fvapaqtx.html