美文网首页
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