专栏首页李蔚蓬的专栏NDK开发案例 | C/C++调用java层代码

NDK开发案例 | C/C++调用java层代码

随笔分类 - jni

NDK开发(三)——C/C++代码如何调用java层代码

E:\AndroidProject\TheTestPro\NDKDemo\app\build\intermediates\javac\debug\classes>javap -s -p com.lwp.ndkdemo.MainActivity
Compiled from "MainActivity.java"
public class com.lwp.ndkdemo.MainActivity extends androidx.appcompat.app.AppCompatActivity {
  public static java.lang.String TAG;
    descriptor: Ljava/lang/String;
  public java.lang.String s;
    descriptor: Ljava/lang/String;
  public static int count;
    descriptor: I
  private java.util.Date date;
    descriptor: Ljava/util/Date;
  public com.lwp.ndkdemo.MainActivity();
    descriptor: ()V

  public native java.lang.String accessFiled();
    descriptor: ()Ljava/lang/String;

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

  public native java.lang.String accessStaticMethod();
    descriptor: ()Ljava/lang/String;

  public static java.lang.String getUUID();
    descriptor: ()Ljava/lang/String;

  public native int accessMethod();
    descriptor: ()I

  public int genRandomInt(int);
    descriptor: (I)I

  public native long accessConstructor();
    descriptor: ()J

  protected void onCreate(android.os.Bundle);
    descriptor: (Landroid/os/Bundle;)V

  public native java.lang.String stringFromJNI();
    descriptor: ()Ljava/lang/String;

  public native java.lang.String stringFromJNITwo();
    descriptor: ()Ljava/lang/String;

  public native java.lang.String hehedaFromJNI();
    descriptor: ()Ljava/lang/String;

  static {};
    descriptor: ()V
}

MainActivity

package com.lwp.ndkdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.util.Date;
import java.util.Random;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    //***********************************************
    public  static String TAG = "MainActivity";
    //待修改的属性,一会儿我们通过C++把它修改为super shadow
    public String s = "shadow";
    //这个方法就是触发点,告诉C/C++层代码去修改上面的成员属性s
    public native String accessFiled();
    //***********************************************

    //静态属性
    public static int count  = 3;
    public native void accessStaticFiled();

    //触发native层调用静态方法
    public native String accessStaticMethod();
    //静态方法,用来产生一个随机的UUID字符串
    public static String getUUID(){
        return UUID.randomUUID().toString();
    }

    //触发native层调用非静态方法
    public native int accessMethod();
    ////产生指定范围的随机数
    public int genRandomInt(int max){
        return new Random().nextInt(max);
    }

    private Date date;
    //访问Date类的构造函数
    public native long accessConstructor();

    @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(hehedaFromJNI());

        //***********************************************
        //通过打印log我们看一下修改是否成功
        Log.e(TAG + "修改前s是:" ,s);
        //调用native方法,让C代码修改成员变量s
        accessFiled();
        Log.e(TAG + "修改后s是:" ,s);
        //***********************************************

        Log.e(TAG + "修改前count是:" ,count + "");
        //调用native方法,让C代码修改成员变量county
        accessStaticFiled();
        Log.e(TAG + "修改后count是:" ,count + "");


        //触发native层 调用 静态方法
//        tv.setText(accessStaticMethod());

        //触发native层 调用 非静态方法
//        tv.setText(accessMethod() + "");

        //触发native层 调用 构造方法
        tv.setText(accessConstructor() + "");
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();

    public native String stringFromJNITwo();

    public native String hehedaFromJNI();
}

heheheda.cpp

#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jstring JNICALL
Java_com_lwp_ndkdemo_MainActivity_hehedaFromJNI(JNIEnv *env, jobject thiz) {
    std::string hello = "heheda heheda heheda from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_lwp_ndkdemo_MainActivity_accessFiled(JNIEnv *env, jobject thiz) {
    //1.obj就代表MainActivity的实体对象,但我们这里需要的是MainActiviyt.class字节码文件,
    // 通过以下方法可得到MainActiviy对应的字节码文件。
    jclass cls = (env->GetObjectClass(thiz));

    //2.找到属性对应的id-->jfieldID,函数三个参数:
    // cls就是上面得到的 MainActiviyt.class字节码文件。
    // s就是属性名称,这个是我们在MainActivity中自己定义的。
    // 最后一个是属性签名,
    jfieldID fid = env->GetFieldID(cls, "s", "Ljava/lang/String;");

    //3.获取属性的值,
    // 修改原来的属性shadow为super shadow.。
    // 一般获取某个属性的值,用到的方法都是这种模式:Get<Type>Field
    jstring jstr = static_cast<jstring>(env->GetObjectField(thiz, fid));

    //4.将jni数据类型转为对应的C数据类型,jstring -> c字符串,
    //方便下一步的字符串拼接操作,
    //关于第二个参数,isCopy 是否赋值(true代表赋值,false不赋值),一般写为NULL或JNI_FALSE就可以
    char *c_str = const_cast<char *>(env->GetStringUTFChars(jstr, JNI_FALSE));

    //拼接得到新的字符串,下面这两句都是c语言的语法,首先定义了一个字符数组(也即字符串)
    //然后把它和上面得到的字符串c_str拼接到一起
    char text[20] = "super  ";
    strcat(text,c_str);

    //5.再把C数据类型转回到jni对应的数据类型,c字符串 ->jstring
    jstring new_jstr = env->NewStringUTF(text);

    //6.修改属性,模式:Set<Type>Field.这个和上面的GetObjectField方法类似,只是多了一个参数
    //这个参数就是我们修改后的属性的值。
    env->SetObjectField(thiz, fid, new_jstr);

    //只要使用了GetStringUTFChars或GetStringUTF函数,记得一定要去释放。
    //释放GetStringUTFChars函数或GetStringUTF函数
    env->ReleaseStringUTFChars(jstr,c_str);

    //7.返回修改后的属性值
    return new_jstr;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_lwp_ndkdemo_MainActivity_accessStaticFiled(JNIEnv *env, jobject thiz) {
    jclass cls = env->GetObjectClass(thiz);
    jfieldID fid = env->GetStaticFieldID(cls, "count", "I");
    jint count = env->GetStaticIntField(cls, fid);
    //修改静态属性值
    count++;
    //SetStatic<Type>Field
    env->SetStaticIntField(cls,fid,count);
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_lwp_ndkdemo_MainActivity_accessStaticMethod(JNIEnv *env, jobject thiz) {

    //jclass
    jclass cls = env->GetObjectClass(thiz);
    //获取方法对应的id
    jmethodID mid = env->GetStaticMethodID(cls, "getUUID", "()Ljava/lang/String;");
    //调用java中的静态方法getUUID(),模式:CallStatic<Type>Method
    jstring uuid = static_cast<jstring>(env->CallStaticObjectMethod(cls, mid));
    return uuid;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_lwp_ndkdemo_MainActivity_accessMethod(JNIEnv *env, jobject thiz) {
    jclass cls = env->GetObjectClass(thiz);
    jmethodID mid = env->GetMethodID(cls, "genRandomInt", "(I)I");
    //第三个参数为方法的形参,个数不限
    //这里则是在Java层定义的genRandomInt(int max)中的max,即产生的随机数最大不能超过200
    jint random = env->CallIntMethod(thiz, mid, 200);
    return random;
}

extern "C"
JNIEXPORT jlong JNICALL
Java_com_lwp_ndkdemo_MainActivity_accessConstructor(JNIEnv *env, jobject thiz) {
    //一般的,当我们可以通过一个类的对象拿到对应的jclass时,就用getObjectClass()方法
    //当没有一个类的对象,我们就需要通过这个类来拿到其对应的jclass,这个时候就用findClass()
    jclass cls = env->FindClass("java/util/Date");
    //jmethodID,<init>就代表的是构造方法
    // 这里"<init>"可能会爆红,不用管。。。直接运行,没问题的
    jmethodID constructor_mid = env->GetMethodID(cls, "<init>", "()V");
    //实例化一个Date对象
    jobject date_obj = env->NewObject(cls, constructor_mid);
    //通过得到的Date对象其调用getTime方法
    jmethodID mid = env->GetMethodID(cls, "getTime", "()J");
    jlong time = env->CallLongMethod(date_obj, mid);
    return time;
}

native-lib.cpp

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_lwp_ndkdemo_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_lwp_ndkdemo_MainActivity_stringFromJNITwo(
        JNIEnv *env,
        jobject thiz) {
    std::string hello = "Hehehehehehehehehehe from C++";
    return env->NewStringUTF(hello.c_str());
}

CMakeLists.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.

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 heheheda.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.
                       native-lib

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

app.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.lwp.ndkdemo"
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
                cppFlags ""
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • TypeError: parse() got an unexpected keyword argument 'transport_encoding'

    凌川江雪
  • Binary XML file line #19: Attempt to invoke virtual method 'boolean java.lang.String.equals(java....

    APP中需要实现LayoutInflater布局加载器动态加载布局,然而开启程序一运行就闪退。。。

    凌川江雪
  • AVD Nexus_5X_API_24 is already running. If that is not the case, delete the files at C:\...\.a...

    查了网上博文,说是因为虚拟机刚搭建好的时候默认会建立"*.lock"文件夹,而且当你把虚拟机关掉时这个文件夹会自动删除,可能是上次关PC时太草率,所以这个逗比文...

    凌川江雪
  • 逻辑斯蒂回归(Logistic Regression)

    在之前的博客,简单的介绍了线性回归,今天来看看和其十分相关的Logistic Regression。 1. 问题背景 线性回归可以让我们呢学习得到特征和...

    用户1148830
  • SAP技术专家的ABAP调试器培训材料

    版权声明:本文为博主汪子熙原创文章,未经博主允许不得转载。 https://jerry.bl...

    Jerry Wang
  • JAVA反射简单实例

    import java.lang.reflect.Field; import java.lang.reflect.Method; import java.l...

    用户2192970
  • Angular开发实践(八): 使用ng-content进行组件内容投射

    在Angular中,组件属于特殊的指令,它的特殊之处在于它有自己的模板(html)和样式(css)。因此使用组件可以使我们的代码具有强解耦、可复用、易扩展等特性...

    laixiangran
  • SAP Fiori和WebClient UI的有状态和无状态行为设计原理

    As is almost known to us all, Fiori ( at least deployed in Netweaver having ABAP...

    Jerry Wang
  • 使用 github 做代码管理,知道这些就够了

    只要掌握了下面的常用命令,基本上用使用 github 就没有问题。github 有两种认证方式,一种是通过 ssh 私钥的方式,一种通过 https 的账号名...

    古时的风筝
  • Git常用操作

    git pull 拉远程分支并合并到本地 git fetch 拉远程分支不做合并 忽略文件 .gitignore 添加文件 git add . 提交所有修改 g...

    苦咖啡

扫码关注云+社区

领取腾讯云代金券