Learn how to use threads in Native code.

The thread standard supported in Native is POSIX threads, which defines a set of apis for creating and manipulating threads.

We can use POSIX threads in Native code as if we were using a library. We first need to include the library’s header file:

#include <pthread.h>
Copy the code

This header defines a number of thread-specific functions, some of which are used here for the moment.

Create a thread

POSIX thread creation functions are as follows:

int pthread_create(
	pthread_t* __pthread_ptr, 
	pthread_attr_t const* __attr, 
	void* (*__start_routine)(void*), 
	void* arg);
Copy the code

Its parameters correspond to the following:

  • __pthread_ptr is a pointer to a variable of type pthread_t, which represents a handle to the return thread.

  • __attr is a pointer to the pthread_attr_t structure, which can be used to specify attributes of a new thread, such as stack size, scheduling priority, etc. If there are no special requirements, use the default value and set the variable to NULL.

  • The third argument is the function pointer to the thread starter, which is the method to execute when the thread starts. This is similar to the Run method in Java Runnable, which has the following function signature format:

void* start_routine(void* args)
Copy the code

The initiator treats the thread argument as a void pointer and returns a void pointer.

  • The fourth argument is the thread launcher argument, the function argument, which can be NULL if no arguments need to be passed.

The pthread_create function returns 0 if executed successfully, or any other error code if executed successfully.

Next, we can experiment with the pthread_create method to create a thread.

First, create a function to run when the thread starts:

void *printThreadHello(void *);
void *printThreadHello(void *) {
    LOGD("hello thread");
    // Remember to have a return value
    return NULL;
}
Copy the code

Note that the thread-start function must have a return value, without which it will crash.

This function is executed as soon as pthread_create is called.

Next create the thread:

JNIEXPORT void JNICALL
Java_com_glumes_cppso_jnioperations_ThreadOps_createNativeThread(JNIEnv *, jobject) {
    pthread_t handles; // Thread handle
    int result = pthread_create(&handles, NULL, printThreadHello, NULL);
    if(result ! =0) {
        LOGD("create thread failed");
    } else {
        LOGD("create thread success"); }}Copy the code

Since there are no attributes for this thread and no arguments are required for thread-run functions, the above program can be executed and printThreadHello is run on a new thread.

Attach the thread to the Java virtual machine

In the thread startup function above, we simply print the log. If we want to perform Java-related operations, such as calling Java functions from JNI, we need to use the Java virtual machine environment, that is, using the JNIEnv pointer. After all, all calling functions start with it.

Pthread_create creates a thread in C++ that is not recognized by the virtual machine. In order to interact with the Java space, you need to attach the POSIX thread to the Java virtual machine and then get the JNIEnv pointer for the current thread. Because the JNIEnv pointer is only valid in the current thread.

The AttachCurrentThread method attaches the current thread to the Java virtual machine and obtains a JNIEnv pointer.

The AttachCurrentThread method is invoked by the JavaVM pointer, which represents the Java virtual machine interface pointer and can be obtained when JNI_OnLoad is loaded and stored as a global variable

static JavaVM *gVm = NULL;
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) ! = JNI_OK) {return JNI_ERR;
    }
    gVm = vm;
    return JNI_VERSION_1_6;
}
Copy the code

DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread DetachCurrentThread

Specific use is as follows:

First, we define the callback method in C++ thread in Java, mainly printing the thread name:

    private void printThreadName(a) {
        LogUtil.Companion.d("print thread name current thread name is " + Thread.currentThread().getName());
            Thread.sleep(5000);
    }
Copy the code

Then define the method to run in Native thread:

void *run(void *);
void *run(void *args) {
    JNIEnv *env = NULL;
    // Add the current thread to the Java virtual machine
    // Assume there are arguments passed
    ThreadRunArgs *threadRunArgs = (ThreadRunArgs *) args;
    if (gVm->AttachCurrentThread(&env, NULL) = =0) {
    // The current thread id is not the main thread
        env->CallVoidMethod(gObj, printThreadName);
        // Separate the current thread from the Java virtual machine
        gVm->DetachCurrentThread();  
    }
    return (void *) threadRunArgs->result;
}
Copy the code

Finally, create a thread and run the method:

		// Create the parameters to pass
	    ThreadRunArgs *threadRunArgs = new ThreadRunArgs();
        threadRunArgs->id = i;
        threadRunArgs->result = i * i;
        // Run the thread
        int result = pthread_create(&handles, NULL, run, (void *) threadRunArgs);
Copy the code

With this call, Java-related functions can be called in Native threads.

Wait for the thread to return the result

As mentioned earlier, a thread-run function must have a return value. It initially returns a NULL pointer, and a new thread is started in a method. When the new thread runs, the method immediately returns and exits.

Now, you can also wait for the thread to finish executing in this method, and then get the result of the thread’s completion.

The pthread_JOIN method lets you wait for a thread to terminate.

int pthread_join(pthread_t __pthread, void** __return_value_ptr);
Copy the code

Among them:

  • __pThread represents the handle to create the thread
  • __return_value_ptr represents the result returned by a thread running a function

Use as follows:

	for (int i = 0; i < num; ++i) {
        pthread_t pthread;
        // Create thread,
        int result = pthread_create(&handles[i], NULL, run, (void*) threadRunArgs); }}for (int i = 0; i < num; ++i) {
        void *result = NULL; // Thread execution returns the result
        // Wait for the thread to finish executing
        if(pthread_join(handles[i], &result) ! =0) {
            throwByName(env, runtimeException, "Unable to join thread");
        } else {
	        LOGD("return value is %d",result); }}Copy the code

If pthread_JOIN returns 0, the execution succeeds. If pthread_join is not 0, the execution fails.

Specific code examples can be found in my Github project, welcome Star.

Github.com/glumes/Andr…

JNI Series articles:

  1. Reference management in Android JNI
  2. Exception handling when Android JNI calls
  3. Android JNI basic operation

Welcome to pay attention to wechat public number: [paper talk], get the latest article push ~~~