Android JNI反射调用Java构造方法、成员方法和静态方法

Android开发中一般讲Java接口调用放在APP层,但是如果想对外隐藏Java接口调用,应该怎么办呢?我们可以将接口调用放在JNI层,通过反射调用所需接口,之后打包成.so库,这样既可对外隐藏所有调用细节。下面开始讲解JNI怎么调用Java方法。

首先模拟实现一个类,代表想隐藏的接口

代码如下:

package com.lb6905.jnidemo;

import android.util.Log;

public class TestClass {
    private final static String TAG = "TestClass";

    public TestClass(){
        Log.i(TAG, "TestClass");
    }

    public void test(int index) {
        Log.i(TAG, "test : " + index);
    }

    public static void testStatic(String str) {
        Log.i(TAG, "testStatic : " + str);
    }

    public static class InnerClass {
        private int num;
        public InnerClass() {
            Log.i(TAG, "InnerClass");
        }

        public void setInt(int n) {
            num = n;
            Log.i(TAG, "setInt: num = " + num);
        }
    }
}

这个类包一个构造方法、一个成员方法,一个静态方法,一个内部类,大多数的类都是由这三种方法组成的。下面要做的就是怎么在JNI调用这些方法。

查看方法签名

这里首先Make Project,否则不会生成Java文件对应的class文件

进入到classpath目录下:
命令: cd app/build/intermediates/classes/debug

查看外部类的签名
javap -s -p com.lb6905.jnidemo.TestClass

查看内部类的签名
javap -s -p com.lb6905.jnidemo.TestClass$InnerClass

结果如下:

F:\Apps\jniDemo\JNIDemo\app\build\intermediates\classes\debug>javap -s -p com.lb6905.jnidemo.TestClass
Compiled from "TestClass.java"
public class com.lb6905.jnidemo.TestClass {
  private static final java.lang.String TAG;
    descriptor: Ljava/lang/String;
  public com.lb6905.jnidemo.TestClass();
    descriptor: ()V

  public void test(int);
    descriptor: (I)V

  public static void testStatic(java.lang.String);
    descriptor: (Ljava/lang/String;)V
}

F:\Apps\jniDemo\JNIDemo\app\build\intermediates\classes\debug>javap -s -p com.lb6905.jnidemo.TestClass$InnerClass
Compiled from "TestClass.java"
public class com.lb6905.jnidemo.TestClass$InnerClass {
  private int num;
    descriptor: I
  public com.lb6905.jnidemo.TestClass$InnerClass();
    descriptor: ()V

  public void setInt(int);
    descriptor: (I)V
}

在JNI中反射调用上述方法

JNIEXPORT void JNICALL Java_com_lb6905_jnidemo_MainActivity_JNIReflect
(JNIEnv *env, jobject thiz)
{
    //实例化Test类
    jclass testclass = (*env)->FindClass(env, "com/lb6905/jnidemo/TestClass");
    //构造函数的方法名为<init>
    jmethodID testcontruct = (*env)->GetMethodID(env, testclass, "<init>", "()V");
    //根据构造函数实例化对象
    jobject testobject = (*env)->NewObject(env, testclass, testcontruct);

    //调用成员方法,需使用jobject对象
    jmethodID test = (*env)->GetMethodID(env, testclass, "test", "(I)V");
    (*env)->CallVoidMethod(env, testobject, test, 1);

    //调用静态方法
    jmethodID testStatic = (*env)->GetStaticMethodID(env, testclass, "testStatic", "(Ljava/lang/String;)V");
    //创建字符串,不能在CallStaticVoidMethod中直接使用"hello world!",会报错的
    jstring str = (*env)->NewStringUTF(env, "hello world!");
    //调用静态方法使用的是jclass,而不是jobject
    (*env)->CallStaticVoidMethod(env, testclass, testStatic, str);

    //实例化InnerClass子类
    jclass innerclass = (*env)->FindClass(env, "com/lb6905/jnidemo/TestClass$InnerClass");
    jmethodID innercontruct = (*env)->GetMethodID(env, innerclass, "<init>", "()V");
    jobject innerobject = (*env)->NewObject(env, innerclass, innercontruct);

    //调用子类的成员方法
    jmethodID setInt = (*env)->GetMethodID(env, innerclass, "setInt", "(I)V");
    (*env)->CallVoidMethod(env, innerobject, setInt, 2);
}

生成.so文件

ndk-build打包成.so库,之后只需要在Android中使用就可以了

static {
    System.loadLibrary("hello-jni");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    ......
    JNIReflect();
}

结果如下,达到预期效果

10-17 11:00:29.685 26729-26729/com.lb6905.jnidemo I/TestClass: TestClass
10-17 11:00:29.685 26729-26729/com.lb6905.jnidemo I/TestClass: test : 1
10-17 11:00:29.685 26729-26729/com.lb6905.jnidemo I/TestClass: testStatic : hello world!
10-17 11:00:29.686 26729-26729/com.lb6905.jnidemo I/TestClass: InnerClass
10-17 11:00:29.686 26729-26729/com.lb6905.jnidemo I/TestClass: setInt: num = 2

这种方法也可以调用Android的自带的接口,可以很大程度提高安全性,因为.so文件比较难破解。

关于env,在C和C++语言中调用方式是不同的,如下,本例使用的C语言

在C中:
(*env)->方法名(env,参数列表)
在C++中:
env->方法名(参数列表)

代码地址(顺手给个Star啊):点击查看源码

作者:lb377463323
出处:http://blog.csdn.net/lb377463323
原文链接:http://blog.csdn.net/lb377463323/article/details/75303125
转载请注明出处!

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值