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库拿出来手动放到工程里面)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354