NDK开发——Android Studio实现JNI

    xiaoxiao2025-11-12  3

    曾经,作为一名Android应用开发者,掌握NDK的开发是很有必要的,虽然之前我并没涉足(惭愧~),随着移动应用软件的核心业务,如加密处理,游戏开发移植等操作的需求增大,NDK的开发显得尤为重要。

    现在,作为一命Android逆向分析工程师,分析NDK开发中涉及到的so库也是很有必要的,为了结合简单的NDK工程学习IDA PRO的逆向分析,懂得NDK的简单开发也是有一定的益处的。

    于是,我找了很多网上很多的资源学习,但在实现第一个NDK程序中却遇到了不少的问题。

    真的不得不说,纸上得来终觉浅,觉知此事要躬行~只有自己动手了,才会发现问题,才能解决问题,累积经验。

    其实,NDK开发第一个程序是非常简单的,但很多技术博客的步骤却省略了一些Exception的处理,害得我折腾了好一阵子,毕竟开发的环境和工具有一定的差异,照搬别人的不一定会行得通,得自己折腾折腾才能清楚。

    废话不多说,go go go!

    --------------------开始分割线---------------

    为了强调重点,一开始就说吧,这里的在Window上的NDK开发可能会遇到两个NDK的BUG,编译时会抛异常,下面会说到。

    首先,下载NDK

    官网:下载地址

    选择合适的平台版本下载即可

    新建工程

    除了基本的MainActivity.class外,新建一个JniUtils.class文件,并在里面声明带有native关键字的本地方法:

    JniUtils.class

    public class JniUtils { public native String hello(); }接着在MainActivity里调用该本地方法:

    MainActivity.class

    public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); JniUtils utils=new JniUtils(); Toast.makeText(this, utils.hello(), Toast.LENGTH_SHORT).show(); } }

    编译,生成编译文件,并对其生成.h格式头文件

    在菜单选择 Build->Make Project,编译之后会在app/build/intermediates目录下生成classes文件夹,内有编译后的classes文件。

    接着,Alt+F12打开AS的终端Terminal,此时的定位位置为工程的文件路径下。

    我们接下来需要通过终端命令,利用javah这个命令,将JniUtils类生成头文件。

    有两种方式,区别是命令的输入而已。

    第一种方式:

    利用cd命令,定位到build\intermediates\classes\debug下

    D:\workspace\JniTest>cd app/build/intermediates/classes/debug 接着,javah命令,生成头文件

    D:\workspace\JniTest\app\build\intermediates\classes\debug>javah -jni com.samuelzhan.jnitest.JniUtils 然后生成了一个头文件

    最后,在app/src/main目录下创建一个jni目录,并将头文件剪切,放到jni文件夹下。

    第二种方式:

    cd命令到app/src/main目录下:

    D:\workspace\JniTest>cd app/src/main 接着,输入javah +参数  的命令

    D:\workspace\JniTest\app\src\main>javah -d jni -classpath "D:\Android SDK\platforms\android-23\android.jar";D:\workspace\JniTest\app\build\intermediates\classes\debug com.samuelzhan.jnitest.JniUtils 这里直接生成头文件直接放到app/src/main/jni目录下

    但需要注意如下几点,尤其是2,3,4点,容易出错:

    1. jni为文件夹名;

    2. 需提供    SDK路径+android.jar   ,并用双引号括起来;(网上有方法貌似没有双引号也可以,但window上实测不可以,这里折腾了很久)

    3. 中间用 英文分号 ";" 隔开;

    4.  classes文件的路径;

    5. 包名+类名,用点隔开,不是用斜杠; 以上两种方法都是差不多的,都是为了利用classes文件生成头文件,并放到jni文件夹下:

    创建一个hello.c文件,实现头文件的方法

    hello.c

    #include "com_samuelzhan_jnitest_JniUtil.h" JNIEXPORT jstring JNICALL Java_com_samuelzhan_jnitest_JniUtil_hello (JNIEnv *env, jobject obj){ return (*env)->NewStringUTF(env,"hello world"); }

    有点像java实现接口一样,头文件里面只有方法的声明而已,没有方法体,还需要去实现它,头文件就类似于接口类。

    记得要添加#include  生成的头文件。

    顺便一提,这里的写法,C和C++是有点小区别的,上面是C的写法,具体区别自行百度。

    创建一个util.c文件,什么都不用写,留空,放到jni下即可(重点,NDK第一个BUG)

    这是NDK的一个BUG,如果不创建这个空c文件,在编译时会抛异常,返回什么鬼 非零出口值 non-zero exit value 2

    在gradle.properties文件配置android.useDeprecatedNdk=true(重点,NDK第二个BUG)

    如果不配置该处,也会抛异常

    在local.properties文件配置ndk路径,和sdk一样

    在app的build.gradle文件配置so库生成参数

    在android的defaultConfig下添加ndk的参数设置

    其中moduleName就是so库的名字,abiFilters就是架构文件夹名称

    若之前没配置好ndk路径,即上一步没做好,这步修改完build.gradle后会提示你Sync,Sync报错,提示你选择NDK路径

    JniUtils.class内加载库

    库的名字要和build.gradle一致

    public class JniUtils { static { System.loadLibrary("jniTest"); } public native String hello(); }

    最后,一切OK后,菜单Build里选择Make Project 或者 Rebuild Projec,重新编译生成so库

    so库生成在build/intermediates/ndk/debug/lib目录下

    导出APK,安装到手机上,运行

    Toast弹出hello world,调用了native方法hello( ), bingo~~~~

    -----------------------结束分割线----------------------

    上面的方法是我根据网上的方法做的,只是在中间强调了两个折磨了我很久的异常解决方法而已。

    由于不同的开发环境和不同的工具版本,NDK版本,可能导致步骤不一定奏效,实际还是需要亲自去弄过才知道。

    还是那句话,纸上得来终觉浅,绝知此事要躬行~~~~

    转载请注明原文地址: https://ju.6miu.com/read-1304098.html
    最新回复(0)