JNI实例在AndroidStudio快速搭建(基于非虫实验7.6.2)

HelloGcc项目地址 https://github.com/goodluckforme/HelloGcc/graphs/contributors

这边在直接进入主题进入之前 我们先思考以下几个问题:
1.什么是JNI
2.编译JNI步骤和方式
3.Cmake全面降低JNI开发门槛。
最后我将一一列举在编译过程中遇到的问题 和新的思考。
什么是JNI:
这是非虫老师在《android 软件安全与逆向分析》试验7.6.2中给出的案例,着重静态分析Android NDK,解释道JNI(JAVA Native Interface)是Androd NDK的一部分,JNI提供的接口函数,可以在原生的C/C++代码中与JAVA代码进行数据交互,JNI接口函数指针被放到JNINativeInterace与JNIInvokeInterface两个结构体中如下:

#if defined(__cplusplus)
    typedef _JNIENV JNIENV;
    typedef _JavaVM JavaVM;
#else
    typedef const struct JNINativeInterface* JNIENV;
    typedef const struct JNIInvokeInterface* JavaVM;

JNIENV的首地址解释成JNINativeInterface的首地址,而JNINativeInterface存放是JNI的接口地址,如下汇编代码中0x29/4=167 即NewStringUTF的偏移地址。每个方法栈4个字节。

LDR R3,[R3,#0x29c];即取出(*env)->NewStringUTF()的地址

2.编译JNI步骤和方式
介绍一下 Android.mk和Application.mk的作用:

Android.mk:工程的编译脚本,描述了编译原生程序支持的ARM硬件指令集,工程编译脚本,StL支持
Application.mk:工程编译脚本,描述原生程序的编译选项、头文件、源文件及依赖库。

方式:
1.使用eclipse的自编写Android.mk方式
2.使用AndroidStudio编写gradle的方式自动生成Android.mk.
3.使用AndroidStudio的Cmake方式,方便快捷。
步骤:
第一代方式:
1.编写Android.mk和Application.mk
Android.mk文件

LOCAL_PATH := $(call my-dir)  //固定写法 指的是当前目录
include $(CLEAR_VARS)        //固定写法 常量
#LOCAL_ARM_MODE := arm        //指定arm
LOCAL_MODULE := TestJniMethodsOne   //指定so库名字
#APP_BUILD_SCRIPT:=./Android.mk     //指定Android.mk   不需要
LOCAL_LDLIBS    := -llog            //指定链接库  就是java的import
LOCAL_SRC_FILES := com_xiaomakj_hellogcc_TestJniMethods.c   //指定编译的C/C++ 指定多个用"/" 空格 或tab键 分隔
include $(BUILD_SHARED_LIBRARY)  指定目标文件类型为so 

Application.mk

APP_ABI:all //指定编译指令集 一般为 "armeabi", "armeabi-v7a", "x86" 
其实也可以在build_gradle中设置 ndk { abiFilters "armeabi", "armeabi-v7a", "x86" }
手动生成so我们自己任然需要自己在Application.mk添加相关配置。
//APP_ABI := armeabi armeabi-v7a x86

2.配置AS的exteranl Tool脚本,javah 、ndk_build 、ndk_build clean
在.externalNativeBuild\ndkBuild\debug\armeabi\ndkBuild_build_command.txt 下就可以看到相关的命令行
这里写图片描述
Ctrl +Shift +S =>Tools => Exteranl Tool=> ‘+’添加
这里写图片描述
Javah

Programma:$JDKPath$\bin\javah       //javah路径
Parameters:-classpath  $Classpath$ -v -jni $FileClass$  //注意这个其实是class有效的 需要build生成class 在使用该命令 不然会报找不到错误
Working directory:$SourcepathEntry$\..\jni  //目标目录

Javah
ndk_build

Programma:D:\android-ndk-r10e\ndk-build.cmd
Parameters:-B                   //就是 ndk-build -B
Working directory:$ModuleFileDir$\src\main  

ndk_build
ndk_build clean

Programma:D:\android-ndk-r10e\ndk-build.cmd
Parameters:clean
Working directory:$ModuleFileDir$\src\main

ndk_build clean
3.编写JNI类的native方法
非虫老师的TestJniMethods

package com.xiaomakj.hellogcc;

public class TestJniMethods {
    public native void test();

    public native String nativeMethod();

    public native void newJniThreads(int i);

    public native Object allocNativeBuffer(long size);

    public native void freeNativeBuffer(Object obj);

    static {
        System.loadLibrary("TestJniMethodsOne");
    }
}

4.分别使用脚本生成 c头文件 和 so库文件
这里写图片描述

5.复制.h头文件 使用C/C++实现该方法

//声明
/* 请在jnimethods.c上点右键,在属性中将文件编码改为utf-8 */
JNIEXPORT void JNICALL Java_com_droider_jnimethods_TestJniMethods_test
  (JNIEnv *, jobject);
//实现
JNIEXPORT jstring nativeMethod(JNIEnv* env, jclass clazz){
    const char * chs = "你好!NativeMethod";
    return (*env)->NewStringUTF(env, chs);
}

生成so
这里写图片描述
如上 Working directory: ModuleFileDir M o d u l e F i l e D i r \src\main
最终会在app\src\main\libs下生成so
这也是为什么build_gradle中中需配置的原因

 sourceSets.main {
            jni.srcDirs = []//禁用as自动生成mk
            jniLibs.srcDir "src/main/libs"//这是ndk_build生成的so目录
        }

注意:
实现C方法的时候会遇到 JNI调用错误: No implementation found for native
这些完全是不懂C代码写法的小白才会犯的错误

方式二: 指定自定义Android.mk屏蔽AndroidStdio自动配置Android.mk
1.编写Android.mk和Application.mk文件
2.配置AS的build_gradle

//配置NDK_LIBS_OUT位jniLibs则不需要其他配置 否则配置
      sourceSets.main {
           jni.srcDirs = []//禁用as自动生成mk
           jniLibs.srcDir "src/main/libs"
       }

     方式三  自动生成 ndk_build 但是javah需要手动 将在build时自动生成so到jniLibs
    task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') {
        commandLine "D:\\android-ndk-r10e\\ndk-build.cmd",//配置ndk的路径
                'NDK_PROJECT_PATH=build/intermediates/ndk',//ndk默认的生成so的文件
                'NDK_LIBS_OUT=src/main/jniLibs',//配置的我们想要生成的so文件所在的位置
                'APP_BUILD_SCRIPT=src/main/jni/Android.mk',//指定项目以这个mk的方式
                'NDK_APPLOCATION_MK=src/main/jni/Application.mk'//指定项目以这个mk的方式
    }
   tasks.withType(JavaCompile) {
            //使用ndkBuild
       compileTask -> compileTask.dependsOn ndkBuild
   }

方式三:As自己生成Android.mk 文件在 so库也会自动生成到intermediates/ndkbuild/下 所以 配置sourceSets.main.jniLibs.srcDir “src/main/libs”貌似没有意义。
1.编写Android.mk和Application.mk文件
2.配置AS的build_gradle

  ndk {
            moduleName "TestJniMethodsOne"//使用link C++ Gradle Project 之后 貌似没用了 abiFilters有用 这里的ndk会覆盖Android.mk的效果
            moduleName "TestJniMethodsTwo"//这玩意不加也能生成多个So库
            abiFilters "armeabi", "armeabi-v7a", "x86"//会覆盖Android.mk的效果
        }
   externalNativeBuild {
       ndkBuild {
           path 'src/main/jni/Android.mk'//指向Android.mk
           //rebuild project 将自动在build的ndkbuild文件下生成so文件
       }
    }

这些是利用AS自动生成Android.mk的gradle配置方法.
https://blog.csdn.net/zjclugger/article/details/51305968

方式四:Cmake 这个方式比较新 也最简单 直接点灯
https://blog.csdn.net/b2259909/article/details/58591898

思考一:如何生成多个SO?
https://blog.csdn.net/dreamintheworld/article/details/49848333
https://blog.csdn.net/zjclugger/article/details/51305968 (推荐)
一共介绍了两种方法 推荐第二种
如下只需要复制一份即可
LOCAL_SRC_FILES := jniOne/com_xiaomakj_hellogcc_TestJniMethods.c
如果分包存放 需添加对应路径

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
#LOCAL_ARM_MODE := arm
LOCAL_MODULE := TestJniMethodsOne
#APP_BUILD_SCRIPT:=./Android.mk
LOCAL_LDLIBS    := -llog
LOCAL_SRC_FILES := jniOne/com_xiaomakj_hellogcc_TestJniMethods.c
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
#LOCAL_ARM_MODE := arm
LOCAL_MODULE := TestJniMethodsTwo
#APP_BUILD_SCRIPT:=./Android.mk
LOCAL_LDLIBS    := -llog
LOCAL_SRC_FILES := jniTwo/com_xiaomakj_hellogcc_MuchJNIShow.cpp
include $(BUILD_SHARED_LIBRARY)

思考二:AS3.0中除了使用CMake外如何开启提示?
如下我已经做了许多尝试并没什么卵用

android.useDeprecatedNdk=true  //过时
ndk.dir=D\:\\android-ndk-r10e   
sdk.dir=D\:\\android_sdk

Link C++ Project with Gradle 这算是方法三的一种快捷方式
只是多了行,对JNI提示并没什么卵用

 externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }

link C++ Gradle
我的想法是 AS3.0之后抛弃了useDeprecatedNdk ndk_build的方式也没有获得提示,将AS降级试试,再不行就是见鬼了。 着实难受 还是拥抱Cmake吧。不过这个坑也是JNI初学者必须经历的。

思考三:C/C++代码太复杂 我们该如何去学习?
这些都是谷歌推出的最新的NDK案例 全部使用Cmake编写
https://github.com/googlesamples/android-ndk/tree/master/other-builds/ndkbuild

推荐使用第三种方式gradle配置如下

 defaultConfig {
        ···
        ndk {
            moduleName "TestJniMethodsOne"//使用link C++ Gradle Project 之后 貌似没用了 abiFilters有用 这里的ndk会覆盖Android.mk的效果
            moduleName "TestJniMethodsTwo"//这玩意不加也能生成多个So库
            abiFilters "armeabi", "armeabi-v7a", "x86"
        }
        sourceSets.main {
            jni.srcDirs = []//禁用as自动生成mk
            jniLibs.srcDir "src/main/libs"
        }
    }
    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }

最后介绍一篇C/C++的windosw环境下利用MakeFile的编译方法:
https://www.cnblogs.com/bingghost/p/5721423.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值