First step, we set up a C++ project in Android Studio for the smaller NDK so dynamic library to run easily,
Gradle files need to be configured in a small way
android {
compileSdkVersion 29
defaultConfig {
applicationId "com.dr.demo.componentapp"
minSdkVersion 14
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags "-std=c++11"
}
ndk{
abiFilters "armeabi-v7a"}}}}Copy the code
Step 2: In the hands-on writing project, I need to think about how to design my own Base class. BaseActivity first needs to abstract the business layer,
Public abstract class BaseActivity extends AppCompatActivity implements JNILogMessageCall {// The JNI service structure abstractions the interface protected JNIManager jniManager; // We can add a private TextView to our business logic to print logslogTextView;
protected abstract JNIManager getJNIManager();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutId());
jniManager = getJNIManager();
if(jniManager ! = null) { jniManager.setJNILogMessageCall(this); }logTextView = findViewById(R.id.logView);
initView();
}
public abstract void initView();
public abstract int getLayoutId();
@Override
public void logMessage(String message) {
Log.i("ydr", message);
if (logTextView ! = null) {logTextView.append(message);
logTextView.append("\n"); }}}Copy the code
In order to add JNI business layer more elegantly, we design the interface JNILogMessageCall (JNI layer Log printing), JNIManager (JNI abstract interface).
public interface JNILogMessageCall {
void logMessage(String message);
}
public interface JNIManager {
void setJNILogMessageCall(JNILogMessageCall messageCall);
}
Copy the code
A real JNI business class could be designed this way
public class TestJNIManager implements JNIManager {
JNILogMessageCall messageCall;
static {
System.loadLibrary("demo01-lib");
}
@Override
public void setJNILogMessageCall(JNILogMessageCall messageCall) { this.messageCall = messageCall; } public native String stringFromJNI(); // We use this method in JNI layer to implement C to call Java layer method private voidsetLogMessage(String message) {
if(messageCall ! = null) { messageCall.logMessage(message); }}}Copy the code
The editor below CPP automatically builds the native. CPP we can design a little bit and create a folder demo01 in CPP, and create a cmakelist.txt to organize the dynamic library compiled by the current project
Aux_source_directories (.demo01) // Specify header directories(.. /includeAdd_library (demo01-lib SHARED ${demo01}) // Link dynamic library target_link_libraries(demo01-lib log)Copy the code
Then cmakelist. TXT under CPP specifies the demo directory,
Cmake_minimum_required (VERSION 3.4.1 track) add_subdirectory (demo01)Copy the code
This allows us to structure multiple file directories demo01 demo02…. under the CPP project structure , etc.
How do we organize our C++ business code? We can do it in two ways: one is that the compiler locates our JNI functions by initially locating the Java full class name + the method name, and the other is that we can do it dynamically
First we’ll introduce the <jni.h> header file
Define two members:
const char *className = "com/dr/demo/componentapp/jni/TestJNIManager";
Copy the code
static JNINativeMethod methods[] = {
{"stringFromJNI"."()Ljava/lang/String;", (void *) stringFromJNI}, }; // this member dynamically links the C++ and Javanative methods in JNICopy the code
Then implement two methods in JNI:
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
Copy the code
JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) Copy the code
Register and unregister native methods defined in our Java
jint registerMethod(JNIEnv *env, jint version) {
jclass clazz = env->FindClass(className);
if (clazz == nullptr) {
return JNI_ERR;
}
jint size = sizeof(methods) / sizeof(methods[0]);
jint res = env->RegisterNatives(clazz, methods, size);
if (res == 0) {
return version;
}
return JNI_ERR;
}
void unRegisterMethod(JNIEnv *env, jint version) {
jclass clazz = env->FindClass(className);
if(className ! =nullptr) { env->UnregisterNatives(clazz); }}/ / register
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = nullptr;
jint res = vm->GetEnv((void **) &env, JNI_VERSION_1_4);
if (res == 0) {
return registerMethod(env, JNI_VERSION_1_4);
} else if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == 0) {
return registerMethod(env, JNI_VERSION_1_6);
}
return JNI_ERR;
}
/ / the registration
JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv *env = nullptr;
if ((vm->GetEnv((void **) &env, JNI_VERSION_1_4))) {
unRegisterMethod(env, JNI_VERSION_1_4);
} else if ((vm->GetEnv((void**) env, JNI_VERSION_1_6))) { unRegisterMethod(env, JNI_VERSION_1_6); }}Copy the code
The specific JNI business implementation is implemented in this way
Static jmethodID methodID = NULL; static voidlogInfo(JNIEnv *env, jobject _clazz, const char *format, ...) {
if (NULL == methodID) {
jclass clazz = env->GetObjectClass(_clazz);
methodID = env->GetMethodID(clazz, "setLogMessage"."(Ljava/lang/String;) V");
env->DeleteLocalRef(clazz);
}
if(NULL ! = methodID) {// Format the log message char buffer[MAX_LOG_MESSAGE_LENGTH]; va_list ap; va_start(ap, format); vsnprintf(buffer, MAX_LOG_MESSAGE_LENGTH, format, ap); va_end(ap); Jstring message = env->NewStringUTF(buffer);if(NULL ! Env ->CallVoidMethod(_clazz, methodID, message); Env ->DeleteLocalRef(message); }} // The code I really want to implement is extern"C" JNIEXPORT jstring JNICALL stringFromJNI(
JNIEnv *env,
jobject thiz) {
std::string hello = "Hello from C++";
logInfo(env, thiz, "log info");
return env->NewStringUTF(hello.c_str());
}Copy the code
Project test has passed, welcome to leave a message to discuss attached source code
Github.com/tank2014gz/…