HTQ_QQ

Project Url: HuTianQi/HTQ_QQ
Introduction: 高仿腾讯 QQ 的一款 IM 软件,在界面上几乎与腾讯 QQ 一模一样,目前只实现了发送文本与表情的功能
More: Author   ReportBugs   
Tags:
QQ-IM-聊天-腾讯-listview-下拉刷新-侧滑删除-slidingmenu-

基于 C++模板函数与 Fluent API 设计的 JNI 反射库,极大的简化 JNI 反射调用。

设计目标

  • 弱化数据类型概念,强化操作意图

    在执行中间任何操作的时候无需关心数据类型,内部会依据传入的参数签名自动调用对应数据类型的原 JNI 函数,只需要在最终进行 get 操作与 set 操作时通过泛型指定数据类型

在执行中间操作时只需关注操作意图,即:

  1. 对什么操作:on
  2. 操作什么:构造函数用 construct,域用 field,静态域用 staicField,函数调用用 call,静态函数调用用 staticCall
  3. 最终目的:获取值用 get,修改值用 set

功能特性

  • 基于 OOD 设计的 Fluent API:

    int intField = Reflect::on(env, object).field("intField", "I").get<jint>(); // 获取 Filed
    Reflect::on(env, object).field("intField", "I").set<jint>(1); // 设置 Field
    jobject object = Reflect::on(env, "tech/huqi/jnioor/Sample").construct("()V").get<jobject>();   // 调用构造函数
    jstring content = Reflect::on(env, object).call("toString","()Ljava/lang/String;").get<jstring>(); // 调用 toString 函数
    
  • 完善的判空与异常检测逻辑

    当执行 JNI 函数的 GetFieldID,GetMethodID,CallXxxMethod,CallStaticXxxMethod 调用时,如果获取的域,方法,调用的方法不存在时会抛出异常导致程序奔溃

    这在对一些非官方系统(如非安卓原生,java 原生)的类进行 JNI 调用时很常见,比如 Xposed 检测,如果你的代码运行在一个未安装 Xposed 框架的机器上,那么关于 Xposed 的域,方法操作都会出现异常导致崩溃。所以一个健壮稳定的 JNI 调用一般类似这样:

    jclass jcls_Sample = env->FindClass("tech/huqi/jnioor/Sample");
    if (jcls_Sample != NULL) {
        jmethodID jmid_init = env->GetMethodID(jcls_Sample, "<init>", "()V");
        if (jmid_init != NULL) {
            jobject job_Sample = env->NewObject(jcls_Sample, jmid_init);
            if (job_Sample != NULL) {
                jfieldID jfid_intField = env->GetFieldID(jcls_Sample, "intField", "I");
                if (jfid_intField != NULL) {
                    int intField = env->GetIntField(job_Sample, jfid_intField);
                    // ...... 省略后续其他逻辑
                } else {
                    checkAndClearException(env);// 检测并清理异常
                }
            } else {
                checkAndClearException(env);// 检测并清理异常
            }
        } else {
            checkAndClearException(env);// 检测并清理异常
        }
    } else {
        checkAndClearException(env);// 检测并清理异常
    }
    env->DeleteLocalRef(jcls_Sample); // 释放局部引用
    

    即对三方框架的类进行 JNI 调用时进行判空或异常检测后再进行下一步操作,如果为空进行异常检测并清理,如果中间产生了一些临时局部引用,还需要释放局部引用。但这样做代码结构嵌套太深,不利于阅读与后期维护,虽然这种嵌套可以采用 goto 语句来避免

    现在有了这个库,只需要这样就包含了上述代码的功能:

    jobject object = Reflect::on(env, "tech/huqi/jnioor/Sample").construct("()V").get<jobject>();
    if(object!=NULL){
        int intField = Reflect::on(env, object).field("intField", "I").get<jint>();
    }
    

示例

(以下示例基于文档最后的 Sample 类进行操作)

  // 获取非态域
  int intField = Reflect::on(env, object).field("intField", "I").get<jint>();
  long longField = Reflect::on(env, object).field("longField", "J").get<jlong>();
  float floatField = Reflect::on(env, object).field("floatField", "F").get<jfloat>();
  double doubleField = Reflect::on(env, object).field("doubleField", "D").get<jdouble>();
  jstring stringField = Reflect::on(env, object).field("stringField",
                                                           "Ljava/lang/String;").get<jstring>();

   // 获取静态域
   int staticIntField = Reflect::on(env, clazz).staticField("staticIntField",
                                                               "I").get<jint>();
   long staticLongField = Reflect::on(env, clazz).staticField("staticLongField",
                                                                 "J").get<jlong>();
   float staticFloatField = Reflect::on(env, clazz).staticField("staticFloatField",
                                                                   "F").get<jfloat>();
   double staticDoubleField = Reflect::on(env, clazz).staticField("staticDoubleField",
                                                                     "D").get<jdouble>();
   jstring staticStringField = Reflect::on(env, clazz).staticField("staticStringField",
                                                                      "Ljava/lang/String;").get<jstring>();

   // 设置非静态域
   Reflect::on(env, object).field("intField", "I").set<jint>(1);
   Reflect::on(env, object).field("longField", "J").set<jlong>(1);
   Reflect::on(env, object).field("floatField", "F").set<jfloat>(1.0f);
   Reflect::on(env, object).field("doubleField", "D").set<jdouble>(1.0);
   Reflect::on(env, object).field("stringField", "Ljava/lang/String;").set<jobject>(
              env->NewStringUTF("modify stringField value"));

   // 设置静态域
   Reflect::on(env, clazz).staticField("staticIntField", "I").set<jint>(1);
   Reflect::on(env, clazz).staticField("staticLongField", "J").set<jlong>(1);
   Reflect::on(env, clazz).staticField("staticFloatField", "F").set<jfloat>(1.0f);
   Reflect::on(env, clazz).staticField("staticDoubleField", "D").set<jdouble>(1.0);
   Reflect::on(env, clazz).staticField("staticStringField","Ljava/lang/String;").set<jobject>(
              env->NewStringUTF("modify staticStringField value"));
   // 调用构造函数
   jobject object = Reflect::on(env, clazz).construct("()V").get<jobject>();
   // 调用函数
   jstring content = Reflect::on(env, object).call("toString","()Ljava/lang/String;").get<jstring>();

Sample 类

public class Sample {
    private static final String TAG = "JniReflectSample";
    private int intField = 0;
    private static int staticIntField = 0;

    private long longField = 0;
    private static long staticLongField = 0;

    private float floatField = 0.1f;
    private static float staticFloatField = 0.1f;

    private double doubleField = 0.1;
    private static double staticDoubleField = 0.1;

    private String stringField = "this is a non-static String";
    private static String staticStringField = "this is a static String";

    public static native void nativePrint();

    public int getIntField() {
        return intField;
    }

    public long getLongField() {
        return longField;
    }

    public float getFloatField() {
        return floatField;
    }

    public double getDoubleField() {
        return doubleField;
    }

    public String getStringField() {
        return stringField;
    }

    @Override
    public String toString() {
        return "Sample{" +
                "intField=" + intField +
                ", longField=" + longField +
                ", floatField=" + floatField +
                ", doubleField=" + doubleField +
                ", stringField='" + stringField + '\'' +
                ", staticIntField=" + staticIntField +
                ", staticLongField=" + staticLongField +
                ", staticFloatField=" + staticFloatField +
                ", staticDoubleField=" + staticDoubleField +
                ", staticStringField='" + staticStringField + '\'' +
                '}';
    }
}
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools