android基础巩固之ndk

环境配置

为了工程不出现问题最好是新建项目的时候选择c++/c项目支持 (android studio创建项目)

app/library module gradle 配置

apply plugin: 'com.android.library'

android {
    defaultConfig {
       externalNativeBuild {
           cmake {
               cppFlags ""
           }
       }

       ndk {
           ldLibs "log"//实现__android_log_print
           abiFilters  'x86', 'armeabi-v7a'
       }

    }

    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }

}

src/main/cpp/CMakeList.txt 项目创建的时候会自动生成,如果没有从其它地方复制 , main/cpp目录不存在就自己创建

cmake_minimum_required(VERSION 3.4.1)

add_library( # Sets the name of the library.
             native-lib

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             native-lib.cpp
        )

find_library( # Sets the name of the path variable.
              log-lib

              # Specifies the name of the NDK library that
              # you want CMake to locate.
              log )


target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )

native-lib.cpp 选择cpp是因为了解这块,各选语言###

#include <jni.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <android/log.h>

using namespace std;

#define  LOG_TAG    "zzg-ndk"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_zyframework_SDKApi_isExpired(JNIEnv *env, jobject jobj, jlong time) {

    LOGE("native isExpret time = %lld ", time);
    return (jboolean) b;
}

使用示例

1.数据传递

  • java传递数据给c(包含对象)
  • c传递数据给java(包含对象)

java传递数据给c,c返回数据给java(包含对象)

java代码

static {
    System.loadLibrary("native-lib");
}

private String    tag = "zzg-ndk";

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Example of a call to a native method
    TextView tv = findViewById(R.id.sample_text);
    tv.setText(stringFromJNI());

    SimpleDateFormat simpleDateFormat = new SimpleDateFormat
            ("yyyy-MM-dd HH:mm:ss");
    String dateStr = simpleDateFormat.format(new Date());
    sayHello(dateStr);

    int addV = add(12, 5);
    Log.e(tag, "c add result = " + addV);

    float f = getFloat(3.1415926f);
    Log.e(tag, "c getFloat result = " + f);

    double d = getDouble(35.0);
    Log.e(tag, "c getDouble result = " + d);

    boolean b = getBoolean(true);
    Log.e(tag, "c getBoolean result = " + b);

    String s = getString("打开电视");
    Log.e(tag, "c getString result = " + s);

    long l = getLong(3500l);
    Log.e(tag, "c getDouble result = " + l);

    String[] n_arr = getStringArray(new String[]{"上海", "天津", "济南"});
    for (String s_t : n_arr) {
        Log.e(tag, s_t);
    }

    /**
     * 在c层进行对象的转换
     */
    ResultInfo resultInfo = new ResultInfo("2018-12-22", 39);
    Log.e(tag, "resultInfo = " + resultInfo);
    WeatherInfo weatherInfo = convertInfo(resultInfo);
    if (weatherInfo != null) {
        Log.e(tag, "weatherInfo = " + weatherInfo);
    }
}

public native String stringFromJNI();
private native void sayHello(String str);
private native int add(int a, int b);
private native float getFloat(float f);
private native double getDouble(double d);

private native boolean getBoolean(boolean b);
private native String getString(String str);
private native long getLong(long l);
private native String[] getStringArray(String[] sa);
/**
 * 对象转换
 */
private native WeatherInfo convertInfo(ResultInfo info);

cpp代码

#include <jni.h>
#include <stdlib.h>
#include <string>
#include <iostream>
#include <android/log.h>

using namespace std;

#define  LOG_TAG    "zzg-ndk"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOGD(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_hncpp_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    jstring sr = env->NewStringUTF(hello.c_str());
    const char *locstr = env->GetStringUTFChars(sr, 0);
    LOGE("stringFromJNI locstr = %s", locstr);
    env->ReleaseStringUTFChars(sr, locstr);
    return sr;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_hncpp_MainActivity_sayHello(JNIEnv *env, jobject obj, jstring str_) {
    const char *str = env->GetStringUTFChars(str_, 0);
    env->ReleaseStringUTFChars(str_, str);
    LOGE("sayHello str = %s", str);
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_hncpp_MainActivity_add(JNIEnv *env, jobject obj, jint a, jint b) {
    int i = a;
    int j = b;
    LOGE("add 接收到的java 数据:a =  %d , b =  %d", a, b);
    LOGE("add 转换之后c的数据:i = %d , j = %d", i, j);
    return i + j;
}

extern "C"
JNIEXPORT jfloat JNICALL
Java_com_example_hncpp_MainActivity_getFloat(JNIEnv *env, jobject obj, jfloat f) {
    float f_l = f;
    LOGE("getFloat 接收到的java 数据:f = %3f ", f);
    LOGE("getFloat 转换之后c的数据:f_l = %3f", f_l);
    f_l = f_l * 0.2f;
    LOGE("getFloat 计算之后的数据:f_l = %3f", f_l);
    return (jfloat) f_l;
}

extern "C"
JNIEXPORT jdouble JNICALL
Java_com_example_hncpp_MainActivity_getDouble(JNIEnv *env, jobject obj, jdouble d) {
    double d_1 = d;
    LOGE("getDouble 接收到的java 数据:d = %1f ", d);
    LOGE("getDouble 转换之后c的数据:d_l = %1f", d_1);
    d_1 = 3 * d_1;
    LOGE("getFloat 计算之后的数据:d_1 = %1f", d_1);
    return (jdouble) d_1;
}


extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_hncpp_MainActivity_getBoolean(JNIEnv *env, jobject obj, jboolean b) {
    unsigned char b_1 = b;
    LOGE("getDouble 接收到的java 数据:d = %lu", b_1);
    if (b_1) {
        LOGE("true");
    } else {
        LOGE("false");
    }
    return !b_1;
}

//
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_hncpp_MainActivity_getString(JNIEnv *env, jobject obj, jstring str_) {
    const char *str = env->GetStringUTFChars(str_, 0);
//    LOGE("getDouble 接收到的java 数据:str = %s", str);
//    std::string hello = "收到指令,准备进行操作, 操作成功";
//    env->ReleaseStringUTFChars(str_, str);
//    return env->NewStringUTF(hello.c_str());

//字符串拼接
//    string str = "Dream a Dream";
//    str.insert(str.length()," i have");
//    str.insert(0, " i have ");

//字符串拼接
    string tmp_s_1 = "收到指令 ", tmp_s_2 = " , 准备进行操作, 操作成功";
    tmp_s_1.insert(tmp_s_1.length(), str);
    tmp_s_1.insert(tmp_s_1.length(), tmp_s_2);
    env->ReleaseStringUTFChars(str_, str);
    return env->NewStringUTF(tmp_s_1.c_str());
}



extern "C"
JNIEXPORT jlong JNICALL
Java_com_example_hncpp_MainActivity_getLong(JNIEnv *env, jobject obj, jlong l) {
    long l_1 = l;
    LOGE("getDouble 接收到的java 数据:l = %ld ", l);
    LOGE("getDouble 转换之后c的数据:l_1 = %ld", l_1);
    l_1 = 1.5f * l_1;
    LOGE("getFloat 计算之后的数据:l_1 = %ld", l_1);
    return (jlong) l_1;
}

extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_example_hncpp_MainActivity_getStringArray(JNIEnv *env, jobject obj, jobjectArray sa) {
    int len = (*env).GetArrayLength(sa);
    int i = 0;
    // 获取数据类型
    jclass objCls = env->FindClass("java/lang/String");
    // 生成新的数组
    jobjectArray jarr = (*env).NewObjectArray(len, objCls, 0);
    for (i = 0; i < len; i++) {
        jobject jobj = (*env).GetObjectArrayElement(sa, i);
        jstring str = static_cast<jstring>(jobj);
        const char *szStr = (*env).GetStringUTFChars(str, 0);
        LOGE("arr[%d] = %s ", i, szStr);
        env->ReleaseStringUTFChars(str, szStr);
//拼接数据
        string t_new = "new_";
        t_new.insert(t_new.length(), szStr);
        env->SetObjectArrayElement(jarr, i, env->NewStringUTF(t_new.c_str()));
    }
    return jarr;
}

//对象转换
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_hncpp_MainActivity_convertInfo(JNIEnv *env, jobject jobj, jobject rInfo) {
    // 获取传入的对象的值
    jclass r_info_jcls = (*env).GetObjectClass(rInfo);
    // 这里通过反射的方式取字段的值
    jfieldID r_info_date_jfid = (*env).GetFieldID(r_info_jcls, "date", "Ljava/lang/String;");
    // 取date值
    jobject r_info_date_jobj = (*env).GetObjectField(rInfo, r_info_date_jfid);
    // 这里是java字符串
    jstring r_info_date_jstr = static_cast<jstring>(r_info_date_jobj);
    const char *r_info_date_cstr = (*env).GetStringUTFChars(r_info_date_jstr, 0);
    // 释放资源
    (*env).ReleaseStringUTFChars(r_info_date_jstr, r_info_date_cstr);
    LOGE("ndk ->ndk -> date = %s", r_info_date_cstr);

    // 这里通过调用方法取到tmp值
    jmethodID jmid_rinfo_get_tmp = (*env).GetMethodID(r_info_jcls, "getTmp", "()I");
    jint val_rinfo_tmp = (*env).CallIntMethod(rInfo, jmid_rinfo_get_tmp);
    LOGE("ndk ->ndk -> val_rinfo_tmp = %d", val_rinfo_tmp);


    // 在c层中构建WeatherInfo对象,设置数据,可以通过调用方法也可以通过设置成员变量来实现
    jclass w_info_jcls = (*env).FindClass("com/example/hncpp/WeatherInfo");
    // 找到构造方法id,无参构造
    jmethodID jmid_const_w_info = (*env).GetMethodID(w_info_jcls, "<init>", "()V");
    // 构建java对象
    jobject jobj_w_info = (*env).NewObject(w_info_jcls, jmid_const_w_info);
    // 设置属性可以通过调用GetFieldID->SetFileID来设置,也可以通过GetMethodId->CallMethodId,总之,哪个方便用哪个
    // 我们这里用简单的操作
    jmethodID jmid_winfo_setid = (*env).GetMethodID(w_info_jcls, "set_id", "(I)V");
    (*env).CallVoidMethod(jobj_w_info, jmid_winfo_setid, 35);

    jmethodID jmid_winfo_setdate = (*env).GetMethodID(w_info_jcls, "setDate",
                                                      "(Ljava/lang/String;)V");
    (*env).CallVoidMethod(jobj_w_info, jmid_winfo_setdate, r_info_date_jstr);

    jmethodID jmid_winfo_settmp = (*env).GetMethodID(w_info_jcls, "setTmp", "(I)V");
    (*env).CallVoidMethod(jobj_w_info, jmid_winfo_settmp, val_rinfo_tmp);
    return jobj_w_info;
}

2.c调用java成员变量\方法\创建java层对象

Utils类代码

public class Utils {
    public static String TEST_LOG_TAG = "zzg_log";
    private static String TEST_LOG_TAG2 = "zzg_log_2";
    private static int build_num = 35;
    public String app_old_version = "v0.0.3";
    public long person_id = 430644198806230032l;
    private String app_version = "v1.3.0";
    private int test_num = 50;
    private long phone_num = 13244105539l;

    private Utils() {
        Log.e(TEST_LOG_TAG, "私有构造触发了");
    }

    public Utils(int t) {
        Log.e(TEST_LOG_TAG, "公有构造触发了,param = " + t);
    }

    public static void logE(String str) {
        Log.e(TEST_LOG_TAG, str);
    }

    public static void logE(String logTag, String str) {
        Log.e(logTag, str);
    }

    private static long getAppRunTimer() {
        return 3500l;
    }

    private boolean isAppRunning() {
        return true;
    }

    public native void test();
}

java方法

cpp代码

公有静态成员变量调用

//公开的静态方法和成员变量
jclass jcls = env->GetObjectClass(jobj);

//调用java的静态成员变量
jfieldID jfid = (*env).GetStaticFieldID(jcls, "TEST_LOG_TAG", "Ljava/lang/String;");
jstring jlog_tag = static_cast<jstring>((*env).GetStaticObjectField(jcls, jfid));
const char *jlogtag_str = (*env).GetStringUTFChars(jlog_tag, 0);
(*env).ReleaseStringUTFChars(jlog_tag, jlogtag_str);
LOGE("获取到的属性 log_tag value = %s", jlogtag_str);

调用私有java的静态成员变量

jfieldID log2_jfid = (*env).GetStaticFieldID(jcls, "TEST_LOG_TAG2", "Ljava/lang/String;");
jstring jlog2_tag = static_cast<jstring>((*env).GetStaticObjectField(jcls, log2_jfid));
const char *jlog2tag_str = (*env).GetStringUTFChars(jlog2_tag, 0);
(*env).ReleaseStringUTFChars(jlog2_tag, jlog2tag_str);
LOGE("获取到的属性 log_tag jlog2_tag = %s", jlog2tag_str);

调用java的公有静态方法

jmethodID loge_m = (*env).GetStaticMethodID(jcls, "logE", "(Ljava/lang/String;)V");
jstring log_c = (*env).NewStringUTF("从c调用的方法,请刷新界面");
(*env).CallStaticVoidMethod(jcls, loge_m,log_c);

调用java的公有非静态成员变量

jfieldID jfid = (*env).GetFieldID(jcls, "app_old_version", "Ljava/lang/String;");
jstring jlog_tag = static_cast<jstring>((*env).GetObjectField(jobj, jfid));
const char *jlogtag_str = (*env).GetStringUTFChars(jlog_tag, 0);
(*env).ReleaseStringUTFChars(jlog_tag, jlogtag_str);
LOGE("获取到的属性 log_tag value = %s", jlogtag_str);

调用私有非静态的成员变量

//    GetFieldID默认获取的就是私有的
jfieldID jfid = (*env).GetFieldID(jcls, "test_num", "I");
jint test_n = (*env).GetIntField(jobj, jfid);
LOGE("获取到的属性 test_num value = %d", test_n);

调用私有非静态的成员String变量

jfieldID app_vers_fid = (*env).GetFieldID(jcls, "app_version", "Ljava/lang/String;");
// 这里容易传错GetObjectField(jobj, app_vers_fid)可千万要注意
jobject app_vers_obj = (*env).GetObjectField(jobj, app_vers_fid);
jstring app_ver_str = static_cast<jstring>(app_vers_obj);
if (app_ver_str != NULL) {
    const char *app_ver_cstr = (*env).GetStringUTFChars(app_ver_str, 0);
    (*env).ReleaseStringUTFChars(app_ver_str, app_ver_cstr);
    LOGE("获取到的属性 log_tag app_version = %s", app_ver_cstr);
}

调用公私有非静态的成员long变量

person_id公有

jfieldID person_id_fid = (*env).GetFieldID(jcls, "person_id", "J");
// 这里容易传错GetObjectField(jobj, app_vers_fid)可千万要注意
jlong person_id = (*env).GetLongField(jobj, person_id_fid);
LOGE("获取到的属性 log_tag person_id = %lld", person_id);

phone_num私有

jfieldID phone_num_fid = (*env).GetFieldID(jcls, "phone_num", "J");
// 这里容易传错GetObjectField(jobj, app_vers_fid)可千万要注意
jlong phone_num = (*env).GetLongField(jobj, phone_num_fid);
LOGE("获取到的属性 log_tag phone_num = %lld", phone_num);

3. static native 使用示例

3. static native 静态的native方法

c代码中调用java中的方法示例

java代码

public class SDKUtils {
    public void setTag(String tag) {
        Log.e("zzg-ndk", "setTag tag = " + tag);
    }

    public static native void updateTag(String key);
}
cpp代码
extern "C"
JNIEXPORT void JNICALL
Java_com_example_sdkjiagu_SDKUtils_updateTag(JNIEnv *env, jclass jcls, jstring key_) {
    const char *key = env->GetStringUTFChars(key_, 0);
    LOGE("native updateTag key = %s ", key);
    env->ReleaseStringUTFChars(key_, key);
    // 得到jobject不会走构造方法
    jobject jobj = (*env).AllocObject(jcls);
    jmethodID _mid = (*env).GetMethodID(jcls, "setTag", "(Ljava/lang/String;)V");

    string t_new = "2019-01-18 23:59:59";
    (*env).CallVoidMethod(jobj,_mid,env->NewStringUTF(t_new.c_str()));
}

辅助资料

ndk编译之后so的路径

\HelloNdkCpp\ZYFramework\build\intermediates\cmake\debug\obj\x86

方法的方法签名的查看

as 3.1.2

app/build/intermediates/classes/debug/com/example/hncpp

as 3.3.0

cmd -> javap -s Utils

public class com.example.hncpp.Utils {
  public java.lang.String app_old_version;
    descriptor: Ljava/lang/String;
  public static java.lang.String TEST_LOG_TAG;
    descriptor: Ljava/lang/String;
  public com.example.hncpp.Utils(int);
    descriptor: (I)V

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

  public static void logE(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)V

  public native void test();
    descriptor: ()V

  static {};
    descriptor: ()V
}
    原文作者:草蜢的逆袭
    原文地址: https://www.jianshu.com/p/5869df8999db
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞