This recipe discusses how to create and terminate native threads at Android NDK.
Readers are expected to know how to create an Android NDK project. We can refer to the Writing a Hello NDK program recipe in Chapter 1, Hello NDK, for detailed instructions.
The following steps describe how to create a simple Android application with multiple native threads:
NativeThreadsCreation
. Set the package name as cookbook.chapter6.nativethreadscreation
. Refer to the Loading native libraries and registering native methods recipe in Chapter 2, Java Native Interface for more detailed instructions.MainActivity.java
under package cookbook.chapter6.nativethreadscreation
. This Java file simply loads the native library NativeThreadsCreation
and calls the native jni_start_threads
method.mylog.h
and NativeThreadsCreation.cpp
files under the jni
folder. The mylog.h
file contains the Android native logcat
utility functions, while the NativeThreadsCreation.cpp
file contains the native code to start multiple threads. A part of the code is shown next.The jni_start_threads
function starts two threads and waits for the two threads to terminate:
void jni_start_threads() { pthread_t th1, th2; int threadNum1 = 1, threadNum2 = 2; int ret; ret = pthread_create(&th1, NULL, run_by_thread, (void*)&threadNum1); ret = pthread_create(&th2, NULL, run_by_thread, (void*)&threadNum2); void *status; ret = pthread_join(th1, &status); int* st = (int*)status; LOGI(1, "thread 1 end %d %d", ret, *st); ret = pthread_join(th2, &status); st = (int*)status; LOGI(1, "thread 2 end %d %d", ret, *st); }
The run_by_thread
function is executed to the native threads:
int retStatus; void *run_by_thread(void *arg) { int cnt = 3, i; int* threadNum = (int*)arg; for (i = 0; i < cnt; ++i) { sleep(1); LOGI(1, "thread %d: %d", *threadNum, i); } if (1 == *threadNum) { retStatus = 100; return (void*)&retStatus; } else if (2 == *threadNum) { retStatus = 200; pthread_exit((void*)&retStatus); } }
Android.mk
file in the jni
folder with the following code:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := NativeThreadsCreation LOCAL_SRC_FILES := NativeThreadsCreation.cpp LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)
logcat
output:$ adb logcat -v time NativeThreadsCreation:I *:S
The following is a screenshot of the logcat
output:
This recipe shows how to create and terminate threads at Android NDK.
Traditionally, pthread is
implemented as an external library and must be linked by providing a linker flag -lpthread
. Android's Bionic C library has its own pthread implementation bundled in. Therefore, we do not use -lpthread
in the Android.mk
file in our project.
As demonstrated in our code, a thread can be created with the pthread_create
function, which has the following prototype:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg);
This function creates and starts a new thread with attributes specified by the attr
input argument. If attr
is set to NULL
, default attributes are used. The start_routine
argument points to the function to be executed by the newly created thread with arg
as the input argument to the function. When the function returns, the thread
input argument will point to a location where the thread ID is stored and the return value will be zero to indicate success, or other values to indicate error.
In our sample code, we created two threads to execute the run_by_thread
function. We pass a pointer to an integer as input argument to the
run_by_thread
function.
The thread is terminated after it returns from the start_routine
function or we explicitly call pthread_exit
. The pthread_exit
function has the following prototype:
void pthread_exit(void *value_ptr);
This function terminates the calling thread and returns the value pointed by value_ptr
to any successful join
with the calling thread. This is also demonstrated in our sample code. We called pthread_join
on both threads we created. The pthread_join
function has the following prototype:
int pthread_join(pthread_t thread, void **value_ptr);
The function suspends the execution of the calling thread until the thread specified by the first input argument terminates. When the function returns successfully, the second argument can be used to retrieve the exit status of the terminated thread as demonstrated in our sample code.
In addition, the logcat
screenshot that we have seen previously shows that calling return from a thread is equivalent to calling pthread_exit
. Therefore, we can get the exit status when either method is called.