美文网首页
Android NDK编译配置

Android NDK编译配置

作者: 一个小草人 | 来源:发表于2019-12-24 14:51 被阅读0次

    使用NDK编译代码主要有两种方法:

    • 基于Make的ndk-build
    • CMake

    基于Make的ndk-build

    • 准备Android.mk
      Android.mk文件位于项目app/src/main/jni目录中,用于向编译系统描述源文件和共享库。
    //此变量表示源文件在开发树中的位置。在这行代码中,编译系统提供的宏函数 my-dir 将返回当前目录(Android.mk 文件本身所在的目录)的路径。
    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    //LOCAL_MODULE 变量存储您要编译的模块的名称。请在应用的每个模块中使用一次此变量。每个模块名称必须唯一,且不含任何空格;LOCAL_SRC_FILES := hello-jni.c,会列举源文件,以空格分隔多个文件
    LOCAL_SRC_FILES := Test.c
    LOCAL_MODULE    := Hello
    #支持日志
    LOCAL_LDLIBS    := -llog
    //帮助系统将所有内容连接到一起:
    include $(BUILD_SHARED_LIBRARY)
    
    • Module的右键菜单中选择Link C++ Project with Gradle


      image.png
    • 选择ndk-build,然后选择Android.mk


      image.png

    CMake

    • 准备CMakeLists.txt,放到app目录下
    # For more information about using CMake with Android Studio, read the
    # documentation: https://d.android.com/studio/projects/add-native-code.html
    
    # Sets the minimum version of CMake required to build the native library.
    
    cmake_minimum_required(VERSION 3.4.1)
    
    # Creates and names a library, sets it as either STATIC
    # or SHARED, and provides the relative paths to its source code.
    # You can define multiple libraries, and CMake builds them for you.
    # Gradle automatically packages shared libraries with your APK.
    
    add_library( # Sets the name of the library.
                 Hello
    
                 # Sets the library as a shared library.
                 SHARED
    
                 # Provides a relative path to your source file(s).
                 src/main/jni/Test.c )
    
    # Searches for a specified prebuilt library and stores the path as a
    # variable. Because CMake includes system libraries in the search path by
    # default, you only need to specify the name of the public NDK library
    # you want to add. CMake verifies that the library exists before
    # completing its build.
    
    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 )
    
    # Specifies libraries CMake should link to your target library. You
    # can link multiple libraries, such as libraries you define in this
    # build script, prebuilt third-party libraries, or system libraries.
    
    target_link_libraries( # Specifies the target library.
                           Hello
    
                           # Links the target library to the log library
                           # included in the NDK.
                           ${log-lib} )
    
    • 选择cmake,然后选择CMakeLists.txt


      image.png

    构建环境通过上面两种方式任意一种配置完成即可,ndk-build编译方式有代码自动补全功能,然后在切换为cmake编译方式,cmake也就具有代码自动补全功能。然后进行下面的步骤:

    • 新建JNI.java类,Java调用C方法
    public class JNI {
        /**
         * 定义native方法
         * 调用C代码对应的代码
         *
         * @return
         */
    
        public native int add(int x, int y);
    
        public native String sayHello(String s);
    
        public native int[] increaseArrayEles(int[] intArray);
    
        public native int checkPwd(String pwd);
    
    }
    
    • 自动生成c方法
      在app/src/main/java目录下执行命令:javah com.qsc.ndk.demo.JNI会在java目录下生成com_qsc_ndk_demo_JNI.h,把该文件剪切到app/src/main/java/jni目录下
    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_qsc_ndk_demo_JNI */
    
    #ifndef _Included_com_qsc_ndk_demo_JNI
    #define _Included_com_qsc_ndk_demo_JNI
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     com_qsc_ndk_demo_JNI
     * Method:    add
     * Signature: (II)I
     */
    JNIEXPORT jint JNICALL Java_com_qsc_ndk_demo_JNI_add
      (JNIEnv *, jobject, jint, jint);
    
    /*
     * Class:     com_qsc_ndk_demo_JNI
     * Method:    sayHello
     * Signature: (Ljava/lang/String;)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_com_qsc_ndk_demo_JNI_sayHello
      (JNIEnv *, jobject, jstring);
    
    /*
     * Class:     com_qsc_ndk_demo_JNI
     * Method:    increaseArrayEles
     * Signature: ([I)[I
     */
    JNIEXPORT jintArray JNICALL Java_com_qsc_ndk_demo_JNI_increaseArrayEles
      (JNIEnv *, jobject, jintArray);
    
    /*
     * Class:     com_qsc_ndk_demo_JNI
     * Method:    checkPwd
     * Signature: (Ljava/lang/String;)I
     */
    JNIEXPORT jint JNICALL Java_com_qsc_ndk_demo_JNI_checkPwd
      (JNIEnv *, jobject, jstring);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    
    • 在main/jni目录下新建c文件,如Test.c,从上一步骤中生成的文件中把方法拷贝过来重写
    #include<stdio.h>
    #include<stdlib.h>
    #include<jni.h>
    #include <string.h>
    
    
    jint JNICALL Java_com_qsc_ndk_demo_JNI_add
            (JNIEnv *env, jobject jobj, jint a, jint b) {
        int result = a + b;
        return result;
    }
    
    /**
     * 将一个jstring转换成一个c语言的char* 类型.
       其实可以用char *fromJava = (*env)->GetStringUTFChars(env, str, 0)代替
     */
    char *_JString2CStr(JNIEnv *env, jstring jstr) {
        char *rtn = NULL;
        jclass clsstring = (*env)->FindClass(env, "java/lang/String");
        jstring strencode = (*env)->NewStringUTF(env, "GB2312");
        jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
        jbyteArray barr = (jbyteArray) (*env)->CallObjectMethod(env, jstr, mid,
                                                                strencode); // String .getByte("GB2312");
        jsize alen = (*env)->GetArrayLength(env, barr);
        jbyte *ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
        if (alen > 0) {
            rtn = (char *) malloc(alen + 1); //"\0"
            memcpy(rtn, ba, alen);
            rtn[alen] = 0;
        }
        (*env)->ReleaseByteArrayElements(env, barr, ba, 0);
        return rtn;
    }
    
    jstring JNICALL Java_com_qsc_ndk_demo_JNI_sayHello
            (JNIEnv *env, jobject jobj, jstring str) {
        //将jstring转为char*
        char *fromJava = (*env)->GetStringUTFChars(env, str, 0);
        char *fromC = "And I'm from C";
        strcat(fromJava, fromC);
        return (*env)->NewStringUTF(env, fromJava);
    }
    
    jintArray JNICALL Java_com_qsc_ndk_demo_JNI_increaseArrayEles
            (JNIEnv *env, jobject jobj, jintArray array) {
        jsize size = (*env)->GetArrayLength(env, array);
        jint *jarray = (*env)->GetIntArrayElements(env, array, JNI_FALSE);
        int i;
        for (i = 0; i < size; i++) {
            *(jarray + i) += 10;
        }
        return array;
    }
    
    jint JNICALL Java_com_qsc_ndk_demo_JNI_checkPwd
            (JNIEnv *env, jobject jobj, jstring str) {
        char *origin = "123456";
        char *user = _JString2CStr(env, str);
        int code = strcmp(origin, user);
        if (code == 0) {
            return 200;
        } else {
            return 400;
        }
    }
    

    C回调Java方法

    public class JNI {
        {
            System.loadLibrary("C2Java");
        }
        public native void callbackAdd();
    
        public native void callbackHelloFromJava();
    
        public native void callbackPrintString();
    
        public native void callbackSayHello();
    
        public int add(int x,int y){
            Log.e("TAG","add() x="+x+" y="+y);
            return x+y;
        }
        public void helloFromJava(){
            Log.e("TAG","helloFromJava()");
        }
        public void printString(String s){
            Log.e("TAG","C中输入的:"+s);
        }
        public static void sayHello(String s){
            Log.e("TAG","我是静态方法==="+s);
        }
    
        public native void unInstallListener(String packageName,int sdkVersion);
    }
    

    C方法实现

    #include <stdio.h>
    #include <stdlib.h>
    #include <android/log.h>
    #include <unistd.h>
    #include <pthread.h>
    #include "jni.h"
    
    #define LOG_TAG "qsc"
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    JNIEXPORT void JNICALL Java_com_qsc_ndk_c2java_JNI_callbackAdd
            (JNIEnv *env, jobject jobj){
        jclass jclazz=(*env)->FindClass(env,"com/qsc/ndk/c2java/JNI");
        jmethodID methodId=(*env)->GetMethodID(env,jclazz,"add","(II)I");
        jobject obj=(*env)->AllocObject(env,jclazz);
        jint result=(*env)->CallIntMethod(env,obj,methodId,6,7);
        LOGE("result==%d\n",result);
    }
    void JNICALL Java_com_qsc_ndk_c2java_JNI_callbackHelloFromJava
            (JNIEnv * env, jobject jobj){
        jclass jclazz=(*env)->FindClass(env,"com/qsc/ndk/c2java/JNI");
        jmethodID methodId=(*env)->GetMethodID(env,jclazz,"helloFromJava","()V");
        jobject obj=(*env)->AllocObject(env,jclazz);
        (*env)->CallVoidMethod(env,obj,methodId);
    }
    
    void JNICALL Java_com_qsc_ndk_c2java_JNI_callbackPrintString
            (JNIEnv * env, jobject jobj){
        jclass jclazz=(*env)->FindClass(env,"com/qsc/ndk/c2java/JNI");
        jmethodID methodId=(*env)->GetMethodID(env,jclazz,"printString","(Ljava/lang/String;)V");
        jobject obj=(*env)->AllocObject(env,jclazz);
        jstring str=(*env)->NewStringUTF(env,"cccccc");
        (*env)->CallVoidMethod(env,obj,methodId,str);
    }
    
    void JNICALL Java_com_qsc_ndk_c2java_JNI_callbackSayHello
            (JNIEnv * env, jobject jobj){
        jclass jclazz=(*env)->FindClass(env,"com/qsc/ndk/c2java/JNI");
        jmethodID methodId=(*env)->GetStaticMethodID(env,jclazz,"sayHello","(Ljava/lang/String;)V");
    //    jobject obj=(*env)->AllocObject(env,jclazz);
        jstring str=(*env)->NewStringUTF(env,"hello from c");
        (*env)->CallStaticVoidMethod(env,jclazz,methodId,str);
    }
    void *thr_fn(void *arg)
    {
        LOGE("new thread: ");
    //        int code=fork();
    //    LOGE("code==%d---%d\n",getpid(),code);
        return NULL;
    }
    JNIEXPORT void JNICALL
    Java_com_qsc_ndk_c2java_JNI_unInstallListener(JNIEnv *env, jobject instance, jstring packageName_, jint sdkVersion) {
        /**
         * >0:父进程的id
         * =0:子进程的id
         * <0:出错了
         */
        pthread_t ntid;
        int code=pthread_create(&ntid,NULL,thr_fn, NULL);
        LOGE("error==%d\n",code);
    
        if(code==0){
            int flag=1;
            while(flag){
                sleep(5);
                const char *packageName = (*env)->GetStringUTFChars(env, packageName_, 0);
                LOGE("packageName==%s",packageName);
                FILE* file=fopen(packageName,"r");
                if(file==NULL){
                    execlp("am","am","start","--user","0","-a","android.intent.action.VIEW",
                    "-d","https://www.baidu.com",NULL);
                    flag=0;
                    (*env)->ReleaseStringUTFChars(env, packageName_, packageName);
                }
            }
        }else if(code==1){
    
        }else{
    
        }
        // TODO
    
    
    }
    

    在上面的C通过反射调用Java的过程中,其中GetMethodID方法需要传入方法签名参数,那方法签名如何获取呢?

    查看方法签名的方法

    NDKDemo\ccalljava\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes该目录右键Show In Explore,然后打开
    命令窗口:javap -s com.qsc.ndk.c2java.JNI

    $ javap -s com.qsc.ndk.c2java.JNI
    Compiled from "JNI.java"
    public class com.qsc.ndk.c2java.JNI {
      public com.qsc.ndk.c2java.JNI();
        descriptor: ()V
    
      public native void callbackAdd();
        descriptor: ()V
    
      public native void callbackHelloFromJava();
        descriptor: ()V
    
      public native void callbackPrintString();
        descriptor: ()V
    
      public native void callbackSayHello();
        descriptor: ()V
    
      public int add(int, int);
        descriptor: (II)I
    
      public void helloFromJava();
        descriptor: ()V
    
      public void printString(java.lang.String);
        descriptor: (Ljava/lang/String;)V
    
      public static void sayHello(java.lang.String);
        descriptor: (Ljava/lang/String;)V
    }
    
    

    相关文章

      网友评论

          本文标题:Android NDK编译配置

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