前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >NDK开发案例 | C/C++调用java层代码

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

作者头像
凌川江雪
发布2020-07-22 15:54:50
4330
发布2020-07-22 15:54:50
举报

随笔分类 - 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'
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 随笔分类 - jni
  • NDK开发(三)——C/C++代码如何调用java层代码
  • MainActivity
  • heheheda.cpp
  • native-lib.cpp
  • CMakeLists.txt
  • app.gradle
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档