Android Ndk开发入门(实现静态注册,动态注册)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37344790/article/details/78258275

Ndk开发过程遇到或大或小的问题,决定用一篇博客记录下来。同时为之后的逆向原生做准备。


开发环境

- android studio

- Ndk

步骤

1  准备native函数

2  生成头文件

3  编写native函数对应的c++或c程序

4  准备 Android.mk Application.mk文件

5  ndk -build生成so文件



在正式开发开始之前,我们先做一些准备工作,在android studio 准备两个使用插件帮我们简化,javah -jni生成头文件,以及ndk -build生成so文件的过程。

File -> Settings...->Tools -> External Tools


点绿+号键添加,在这只贴出怎么配置,对as插件有兴趣的同学,请自行百度。

首先是添加javah -h插件



Program:$JDKPath$\bin\javah.exe

Parameters:-classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$

Working directory:$ModuleFileDir$\src\main\java

添加 ndk -build插件


Promgram:D:\AppData\Local\Android\sdk\ndk-bundle\ndk-build.cmd(取决与自己的ndk路径,可在local.properties 中查看)

Working directory:$ModuleFileDir$\src\main\

正式开始我们的Ndk开发:

第一步: 准备native函数

新建一个工程,在MainActivity里我们准备了三个native函数, 前2个我们用静态注册的方法实现,之后采用动态注册 第三个函数。


可以看到1 ,2的不同在于,1是非静态函数,2是静态函数。java层的逻辑是三个Button,点击Button分别从三个函数中获取字符串,并通过Toast展示。

类的路径是:com.conghuachaodan.myjnitest.MainActivity

在MainActivity里通过静态字段加载so

  static {
        System.loadLibrary("JniTest");
    }
第二步 生成头文件并编写c++(c)程序

这一步我们将使用我们刚添加的javah -jni插件,至于javah命令不清楚的可自行百度,这里我们主要是了解jni开发流程,以及为逆向做准备。我们在MainActivity处右击选择External Tools选择我们javah -jni 插件


这时候我们可以看到多了一个jni文件夹,多出了一个.h的头文件。

在jni文件夹新建test.cpp,包含我们刚才的头文件,从头文件从拷出前两个函数实现形式,并进行相应补充(补充参数及函数体,这里我们就简单返回可以区别这三个函数的简单字符串),实现如下:


JNIEXPORT jstring JNICALL Java_com_conghuachaodan_myjnitest_MainActivity_getFirString
  (JNIEnv * env, jobject object){
    return env -> NewStringUTF( "This is First String");
  }

/*
 * Class:     com_conghuachaodan_myjnitest_MainActivity
 * Method:    getSecString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_conghuachaodan_myjnitest_MainActivity_getSecString
  (JNIEnv * env, jclass clazz){
    return env -> NewStringUTF("This is Second String");
  }
静态注册最显著的特点就是函数名,例如:Java_com_conghuachaodan_myjnitest_MainActivity_getFirString 是我们类全路径加函数名,以_分隔。

在这里我们可以看出java层非静态函数和静态函数在native层实现的区别在于第二参,非静态函数是jobject object,代表是一个对象,静态函数则是jclass clazz,代表的是一个类。这是很容易理解的。在java中非static函数为实例化对象所有,而static函数为类所有。

接下来实现我们的动态注册。先贴出代码如下:

jstring native_String(JNIEnv *env, jobject object) {
    return env  -> NewStringUTF("This is Third String");
}

static JNINativeMethod getMethods[] =
        { {"getThirdString","()Ljava/lang/String;",(void *) native_String} };

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {


    JNIEnv *env = NULL;
    jint result = JNI_FALSE;

    if (vm->GetEnv((void**) &env,JNI_VERSION_1_6) != JNI_OK)
        return result;

    if (env == NULL)
        return result;

    jclass clazz = env -> FindClass("com/conghuachaodan/myjnitest/MainActivity");
    if (clazz == NULL)
        return result;

    if (env -> RegisterNatives( clazz,getMethods, sizeof(getMethods) / sizeof(getMethods[0])) < 0)
        return result;

    result = JNI_VERSION_1_6;
    return result;
}

动态注册的主要步骤就是在JNI_OnLoad中通过env -> RegisterNatives(...)实现。

在JNI_OnLoad中首先通过vm得到env,在RegisterNatives(...)中有三个参数,第一个clazz是我们的目标类也就是MainActivity,这里我们通过env -> FindClass(...)在参数中传入全路径,以 "/"分隔。第二个参数是我们要注册的函数在native实现的函数数组,第三参则是我们需要动态注册函数的数量。

native_String函数是我们需要注册的第三个java native函数的实现。添加到JNINativeMethod getMethods[]中

static JNINativeMethod getMethods[] =
        { {"getThirdString","()Ljava/lang/String;",(void *) native_String} };
在数组中我们可以清楚看到,每个JNINativeMethod分为三项,第一项是对应java的函数名称,第二项对应参数形式及返回值,第三项则是我们在native实现函数的函数指针。到此我们在native层的代码编写完成。

贴出全部代码:

//
// Created by Administrator on 2017/10/16.
//

#include <jni.h>
#include <stdlib.h>
#include <Android/log.h>
#define TAG "MainActivity"
#include "com_conghuachaodan_myjnitest_MainActivity.h"

/*
 * Class:     com_conghuachaodan_myjnitest_MainActivity
 * Method:    getFirString
 * Signature: ()Ljava/lang/String;
 */

JNIEXPORT jstring JNICALL Java_com_conghuachaodan_myjnitest_MainActivity_getFirString
  (JNIEnv * env, jobject object){
    return env -> NewStringUTF( "This is First String");
  }

/*
 * Class:     com_conghuachaodan_myjnitest_MainActivity
 * Method:    getSecString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_conghuachaodan_myjnitest_MainActivity_getSecString
  (JNIEnv * env, jclass clazz){
    return env -> NewStringUTF("This is Second String");
  }

jstring native_String(JNIEnv *env, jobject object) {
    return env  -> NewStringUTF("This is Third String");
}

static JNINativeMethod getMethods[] =
        { {"getThirdString","()Ljava/lang/String;",(void *) native_String} };

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {


    JNIEnv *env = NULL;
    jint result = JNI_FALSE;

    if (vm->GetEnv((void**) &env,JNI_VERSION_1_6) != JNI_OK)
        return result;

    if (env == NULL)
        return result;

    jclass clazz = env -> FindClass("com/conghuachaodan/myjnitest/MainActivity");
    if (clazz == NULL)
        return result;

    if (env -> RegisterNatives( clazz,getMethods, sizeof(getMethods) / sizeof(getMethods[0])) < 0)
        return result;

    result = JNI_VERSION_1_6;
    return result;
}

第三步 添加Android.mk,Application.mk,内容如下,不多作解释

//Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
LOCAL_MODULE    := JniTest
LOCAL_SRC_FILES :=  test.cpp
include $(BUILD_SHARED_LIBRARY)
//Application.mk

APP_ABI := armeabi
我们的准备完成,接下来将进行build


第四步 进行ndk -build

选中test.cpp,右击->External Tools选择我们的插件ndk -build,此时不出问题,我们应该已经多了libs,obj文件夹


在main下新建jnilibs文件夹,将jni下的文件和libs,obj下的文件移至这个目录下。


在build.gradle(app) android{...}中添加

sourceSets {
        main {
            jniLibs.srcDir 'libs'
        }
    }
至此,我们的静态注册和动态注册已经完成了



猜你喜欢

转载自blog.csdn.net/m0_37344790/article/details/78258275
今日推荐