Android-NDK学习(2)

写在前面

本文需要一些CMakeJNI的基础知识,对于CMake的使用推荐Android官网的NDK入门。CMake是Android Studio 2.2以上新增的支持原生编程的工具,CMake是一个跨平台的编译工具,可以用简单的语句描述所有平台的编译过程。恩,暂时先记一下吧,本文现处于自嗨状态,不适合作为各位将之作为NDK的学习资料。

环境配置

在创建项目时勾选include c++,在项目创建完毕,文件目录如下:

目录结构

可以看到,可以看到app目录下有一个CMakeLists.txt,我在这个txt里加了一些注释

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
# 增加cpp动态共享库
add_library( # Sets the name of the library.
             hello

             # Sets the library as a shared library.
             SHARED

             # Provides a relative path to your source file(s).
             src/main/cpp/hello.cpp )

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

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 )

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

# 生成链接动态库
target_link_libraries( # Specifies the target library.
                       hello

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

如果你有多个cpp动态共享库,可以再添加一个add_library。在app的build.gradle中也有相关的配置:

    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }

关于CMake更详细的配置参见:https://developer.android.com/ndk/guides/cmake.html?hl=zh-cn

可以看到我的CMake文件中添加的动态库为hello,这里在Java文件中创建一个JNI入口类,叫做JNIEntry:

package com.xiasuhuei321.forjni.entry;

import android.util.Log;

/**
 * Created by xiasuhuei321 on 2017/8/26.
 * author:luo
 * e-mail:xiasuhuei321@163.com
 */

public class JNIEntry {
    static {
        System.loadLibrary("hello");
    }

    // 实例域
    public String TAG1 = "JNIEntry_INSTANCE";
    // 静态域
    public static final String TAG2 = "JNIEntry_STATIC";
    public static final String TAG = "JNIEntry";

    public static void testStaticMethod(){
        Log.e(TAG,"静态测试方法被调用了");
    }

    private void testInstanceMethod(){
        Log.e(TAG,"实例测试方法被调用了");
    }

    /**
     * 从C++获取字符串
     *
     * @return 字符串
     */
    public static native String stringFromJNI();

    /**
     * 将大写字符串转成小写
     *
     * @return 小写字符串
     */
    public static native String toLowCase(String str);

    public static native void changeArray(int[] array);

    public static native void accessField(Object obj);

    public static native void accessMethod(Object obj);
}

hello.cpp完整代码

这里再记一下c++源码,方便以后自己查阅
cpp源码:hello.cpp

//
// Created by xiasuhuei321 on 2017/8/25.
//

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


extern "C"
{
JNIEXPORT jstring JNICALL
Java_com_xiasuhuei321_forjni_entry_JNIEntry_stringFromJNI(JNIEnv *env, jobject instance) {
    // 用c字符串创建Java字符串
    __android_log_print(ANDROID_LOG_DEBUG, "jni", "Hello World");
    // 在内存溢出的情况下,返回NULL
    return env->NewStringUTF("Hello World");
}

JNIEXPORT jstring JNICALL
Java_com_xiasuhuei321_forjni_entry_JNIEntry_toLowCase(JNIEnv *env, jclass type, jstring str_) {
    // 为了在原生代码中使用Java字符串,需要先将Java字符串转换成C字符串
    // 这个函数可以将Unicode格式的Java字符串转换成C字符串
    // 第二个参数指定了是指向堆内原有对象还是拷贝一份新的对象
    // 这种获取的字符串只读
    const char *str = env->GetStringUTFChars(str_, JNI_FALSE);
    // 获取字符串长度
    jint length = env->GetStringLength(str_);
    char strs[length];

    // 这里必须要这么复制,自己手动改会有一个特殊字符(猜测是结尾的符号),
    // 在生成UTF字符串的时候会报错
    strcpy(strs, str);
    for (int i = 0; i < length; i++) {
        char c = str[i];
        if (c >= 'A' && c <= 'Z') {
            strs[i] = (char) (c + 32);
        } else {
            strs[i] = c;
        }
    }

    __android_log_print(ANDROID_LOG_DEBUG, "jni", "%s", strs);
    // 释放JNI函数返回的C字符串
    env->ReleaseStringUTFChars(str_, str);

    return env->NewStringUTF(strs);
}

JNIEXPORT void JNICALL
Java_com_xiasuhuei321_forjni_entry_JNIEntry_changeArray(JNIEnv *env, jclass type,
                                                        jintArray array_) {
    // 将Java数组复制到C数组中
//    env->GetIntArrayRegion()
    // 从C数组向Java数组提交所做的修改
//    env->SetIntArrayRegion()
    // 获取指向数组元素的直接指针
    jint *value = env->GetIntArrayElements(array_, NULL);
    jint length = env->GetArrayLength(array_);
    // 这里对每个元素做+1操作
    for (int i = 0; i < length; i++) {
        value[i] += 1;
    }
    env->ReleaseIntArrayElements(array_, value, 0);
}

JNIEXPORT void JNICALL
Java_com_xiasuhuei321_forjni_entry_JNIEntry_accessField(JNIEnv *env, jclass type,
                                                        jobject instance) {
    jclass clazz = env->GetObjectClass(instance);
    // JNI提供了用域ID访问两类域的方法,可以通过给定实例的class对象获取域ID
    // 用GetObjectClass函数可以获得class对象
    // 获取id
    jfieldID instanceFieldId = env->GetFieldID(clazz, "TAG1", "Ljava/lang/String;");
    // 获取属性值
    jstring jstr_value = (jstring) env->GetObjectField(instance, instanceFieldId);
    const char *ch_value = env->GetStringUTFChars(jstr_value, NULL);

    __android_log_print(ANDROID_LOG_DEBUG, "jni", "%s", ch_value);

    // 获取id
    jfieldID staticFieldId = env->GetStaticFieldID(clazz, "TAG2", "Ljava/lang/String;");
    // 获取属性值
    jstring static_value = (jstring) env->GetStaticObjectField(clazz, staticFieldId);
    const char *st_value = env->GetStringUTFChars(static_value, NULL);
    __android_log_print(ANDROID_LOG_DEBUG, "jni", "%s", st_value);

}

JNIEXPORT void JNICALL
Java_com_xiasuhuei321_forjni_entry_JNIEntry_accessMethod(JNIEnv *env, jclass type,
                                                         jobject obj) {
    // 获取class对象
    jclass clazz = env->GetObjectClass(obj);
    // 获取方法id
    jmethodID i_id = env->GetMethodID(clazz, "testInstanceMethod", "()V");
    jmethodID s_id = env->GetStaticMethodID(clazz, "testStaticMethod", "()V");

    // 调用实例方法
    env->CallVoidMethod(obj, i_id);
    // 调用静态方法
    env->CallStaticVoidMethod(clazz,s_id);
}
}

凑个字数

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,757评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,478评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,540评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,593评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,903评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,329评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,659评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,383评论 0 195
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,055评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,337评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,864评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,227评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,820评论 3 231
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,999评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,750评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,365评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,260评论 2 258

推荐阅读更多精彩内容