Native development kit (ndk) introduction
Topics covered include •
o What is an NDK and Why NDK? •
o Java Native Interface (JNI) •
o Using NDK •
o NDK and JNI by Example •
o NDK's Stable APIs
• Android NDK Multithreading
o Introduction To NDK Native Threading
o Creating and terminating native threads at Android NDK
Build with pthreads
Thread creation
Thread termination
o Synchronizing native threads with conditional variables at Android NDK
Initialize and destroy conditional variables
o Synchronizing native threads with semaphore at Android NDK
Initialize and destroy a semaphore
o Managing data for native threads at Android NDK
Mobile Application Development-Android and It’s Tools
Native development kit (ndk) introduction
1. Native Development Kit (NDK) :
Introduction
Rakesh Kumar Jha
M. Tech, MBA
Delivery Manager
2. Contents
What is an NDK and Why NDK? ·
Java Native Interface (JNI) ·
Using NDK ·
NDK and JNI by Example ·
NDK's Stable APIs
Android NDK Multithreading
4. What is an NDK
• The NDK (Native Development Kit) is a tool
that allows you to program in C/C++ for
Android devices.
• It's intended to integrate with the SDK (it's
described as a "companion tool") and used
only for performance-critical portions of a
project.
5. Why NDK ?
• The NDK (Native Development Kit) allows you
to write code with C/C++ languages and then
call it from your Java application using JNI
(Java Native Interface)
6. Why NDK ?
• Using native code in java is reasonable
especially when you are dealing with
bits/bytes operations, like bitmaps
compression/decompression.
7. Why NDK ?
• NDK will not be interpreted like Java through
the JVM, instead it will use the operating
system API (in Android case, Linux) for those
operations, and thus its performance will be
much faster.
8. Why NDK ?
• One of the big advantages of the NDK is that
you can call custom allocation of memory
using malloc() method.
• However in the native code there is no GC
(Garbage collection), hence you need to free
memory by yourself.
9. Why NDK ?
• Potentially, you can increase your application
performance but sometime it can be just
overkill, so use it appropriately.
11. General steps for Java-NDK integration
1. Declare native method in java class.
2. Create a header file according to native
implementation.
3. Implement the header file on the native side
in C/C++ class.
4. Create an Android.mk file.
5. Compile native code to .so package/s.
6. Call the java native declaration method.
12. Step 1: Declare native method in java
class
1. First thing that we need to do is to create a
native method declaration in our Android
application class.
public class NDK_Methods
{
public static native String SayHello();
}
13. Step 2: Creating header file
What is Javah?
• Javah is a java utility that produces C headers
files from Java class, in order to provide an
interface through which Java and C code can
interact.
14. Step 2: Creating header file
Creating a Header file using javah utility
• After we created our native SayHello() method
declaration, we need to create a header file
which will be implemented in C class on the
native side.
• Steps:
– Go to the root directory of your project.
– Open a CMD.
– Type in"
15. Step 2: Creating header file
• javah -classpath bin/classes/ -d
jni/ndk.NDK_Methods
• -classpath bin/classes/: location of the classes
directory in which the compiled java classes of
your android application are located.
• -d jni/ndk.NDK_Methods : is a fully qualified
name of the class from which the header class
will be generated.
16. Step 2: Creating header file
• The generated header file will be created
under the jni directory in our project.
17. Step 2: Creating header file
• In example we will get the following header file:
#include <jni.h>
/* Header for class ndk_NDK_Methods */
#ifndef _Included_ndk_NDK_Methods
#define _Included_ndk_NDK_Methods
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: ndk_NDK_Methods
* Method: SayHelo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_ndk_NDK_1Methods_SayHelo(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
18. Step 2: Creating header file
• Our java method declaration :
public static native String SayHello();
• Was generated to:
JNIEXPORT jstring JNICALL Java_ndk_NDK_1Methods_SayHelo (JNIEnv *, jclass);
• Notice that the generated name is Java
followed by package name, class and the
method separated by underscores.
•
19. Step 2: Creating header file
Since we did not pass any parameters to the
function we accept only the default parameters:
JNIEnv – is a pointer through which we are
communicating with java from the native
implementation.
jclass – a class that called the function.
21. Step 3: Implementing the Header file
• Now we have a header file and we need to
define an implementation for our method.
• We need to create a new file with c or cpp
extention in the jni directory and copy to it
the SayHello() method declaration:
22. Step 3: Implementing the Header file
//specifies the header file that is included
#include <ndk_NDK_Methods.h>
JNIEXPORT jstring JNICALL
Java_ndk_NDK_1Methods_SayHello(JNIEnv *env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from
JNI!");
}
23. Step 3: Implementing the Header file
Notice that we are returning jstring which is
different from C string in our method.
We need to return a java object, so we are doing
it through the JniEnv pointer by calling
a NewStringUTF() method.
25. Step 4: Creating an Android.mk file
• In order to compile our file to .so package we
need to define an Android.mk file in our jni
directory.
• Android.mk file describes to the build system
about your sources.
• Android.mk contains those fields:
26. Step 4: Creating an Android.mk file
• LOCAL_PATH := $(call my-dir) - Android.mk must begin with this definition,
this defines the location of the sources. The macro ‘my-dir’ is the directory
of the Android.mk file.
• include$(CLEAR_VARS) – clears all variables that might be set from a
previous module build.
• LOCAL_MODULE :=native_lib – sets the name that is used as the identifier
for a module, which later used in java.
• LOCAL_SRC_FILES := native_lib – file that will be compiled in your module,
no need to specify headers, the system will take care of it.
• include $(BUILD_SHARED_LIBRARY) – ensures that shared library becomes
a part of this make.
27. Application.mk
• Not necessary for the compilation, but in our
case we specified:
• APP_ABI := all – sets the compilation for all
the supported CPUs.
• If we will not specify this, the compilation will
be made only for the default CPU. You can also
specify your CPU destination compilation
explicitly.
29. Step 5: Compiling the native code
What is ndk-build?
• An ndk-build is an NDK tool which is
responsible for compiling your native code to
executable files.
30. Compilation
• After we created the Android.mk file we can
create the .so package using ndk-build tool.
• Steps:
– Go to your root directory of the android application.
– Lunch the ndk-build utility with its full path, in my
case: C:/android_ndk/ndk-build.
• C:/android_ndk – The directory where you have
downloaded your NDK.
• ndk-build – The utility.
• This will create a .so package/s in libs directory in
our project.
32. Step 6: Calling native method
• What’s left for us to do, is to make a call from java
to JNI method in our application:
<a>
String ndkMessage = NDK_Methods.SayHelo();
</a>
• This will call the static method in NDK_Methods
class, that will call the
Java_ndk_NDK_1Methods_SayHelo() method
which is declared in the header file and
implemented in our C class
33. Java Native Interface (JNI)
• Install the JNI/NDK package from Google
• Create your Android project
• Make a JNI folder in your Android project root directory (called 'jni')
• Put your JNI sources in the 'jni' folder
• Create an 'Android.mk' file, and place it in the 'jni' folder
• Optionally create an 'Application.mk' file, and place it in the 'jni' folder
• Open a command line terminal and navigate to the root directory of your
Android project.
• Execute 'ndk-build', (if it's in your PATH variable) or execute
'/path/to/command/ndk-build'
• The 'ndk-build' command creates the binary for your library and puts it in
the proper folder.
• Switch to Eclipse, Refresh the 'Project Explorer View' (F5)
• Rebuild the project
• Run your project testing your JNI library.
34. Java Native Interface (JNI)
• Install the JNI/NDK package from Google
• Create your Android project
• Make a JNI folder in your Android project root directory (called 'jni')
• Put your JNI sources in the 'jni' folder
• Create an 'Android.mk' file, and place it in the 'jni' folder
• Optionally create an 'Application.mk' file, and place it in the 'jni' folder
• Open a command line terminal and navigate to the root directory of your
Android project.
• Execute 'ndk-build', (if it's in your PATH variable) or execute
'/path/to/command/ndk-build'
• The 'ndk-build' command creates the binary for your library and puts it in
the proper folder.
• Switch to Eclipse, Refresh the 'Project Explorer View' (F5)
• Rebuild the project
• Run your project testing your JNI library.
37. 1 - Stay on Target
• The newest devices are generally ARMv7,
meaning that it can pay to use v7 builds and
features. The latest version of the NDK adds
support ARMv7 and NEON code
38. 2 - Do not optimize immediately
• Unless you plan on porting an existing C++
application, do not rush into native.
39. 3 - Optimize like a ninja
• When you do optimize, sneak in, turn the key
bits of your application into super-fast native
or assembly code and get out cleanly.
• That way you should not compromise your
maintainability and ease of debugging.
40. 4. Re-factor around your
optimisations
• Once you have a design in place, do not be
afraid to re-arrange code to make more of it
suitable for optimising, but avoid tinkering too
much with native code once it is working.
• The Java code is more easily rearranged and
debugged.
41. 5 - Maintain a Java fall-back
• Executing unsupported native code is a bad idea;
at best it will
• cause your application to exit unexpectedly. It is
possible to determine with some confidence
whether or not your native code will be
compatible with the device the program is
running on, so as long as you have a Java
implementation available you can always fall back
to that.
• This is where the optimized Java version from tip
4 pays off extra.
42. 6 - Allocate with care
• Whenever possible allocate in Java anything
that is needed in Java rather than relying on
later, easily forgotten, calls to C to free.
• This minimises the chances of leaks and makes
the Java code simpler.
43. 7 - Multi-thread with great care
• With that in mind it is tempting to split
everything up into threads.
• It is a good idea in general, but remember
maxing out the load on your system may
speed up the result at the expense of the
second-to-second user experience. Even so,
used sensibly threads can be very effective
44. 8 - Thread at the Java Level
• When you do break your logic into threads, it
is better to do it via Java than pthreads
wherever possible.
• . There are fewer hazards and more language-level
tools for managing access with the Java
VM
45. 8 - Thread at the Java Level
• When you do break your logic into threads, it
is better to do it via Java than pthreads
wherever possible.
• . There are fewer hazards and more language-level
tools for managing access with the Java
VM
46. Creating and terminating native
threads at Android NDK
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);
}
47. Creating and terminating native
threads at Android NDK
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);
}
}
48. Creating and terminating native
threads at Android NDK
Add an 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)
49. Creating and terminating native
threads at Android NDK
Build and run the Android project, and use the
following command to monitor
the logcat output:
$ adb logcat -v time NativeThreadsCreation:I *:S
51. Synchronizing native threads with
mutex at Android NDK
1. Create an Android application
named NativeThreadsMutex.
2. Right-click on the
project NativeThreadsMutex, select Android
Tools | Add Native Support.
3. MainActivity Java file simply loads the
nativeNativeThreadsMutex library and calls
the native jni_start_threads method
52. Synchronizing native threads with
mutex at Android NDK
4. Add two file –
4. 1. mylog.h and
5. NativeThreadsMutex.cpp in
the jni folder.NativeThreadsMutex.cpp contains
the code to start two threads.
6. The two threads will update a shared counter.
53. Synchronizing native threads with
mutex at Android NDK
int cnt = 0;
int THR = 10;
void *run_by_thread1(void *arg) {
int* threadNum = (int*)arg;
while (cnt < THR) {
pthread_mutex_lock(&mux1);
while ( pthread_mutex_trylock(&mux2) ) {
pthread_mutex_unlock(&mux1); //avoid deadlock
usleep(50000); //if failed to get mux2, release mux1 first
pthread_mutex_lock(&mux1);
}
++cnt;
LOGI(1, "thread %d: cnt = %d", *threadNum, cnt);
pthread_mutex_unlock(&mux1);
pthread_mutex_unlock(&mux2);
sleep(1);
}
}
54. Synchronizing native threads with
mutex at Android NDK
void *run_by_thread2(void *arg) {
int* threadNum = (int*)arg;
while (cnt < THR) {
pthread_mutex_lock(&mux2);
while ( pthread_mutex_trylock(&mux1) ) {
pthread_mutex_unlock(&mux2); //avoid deadlock
usleep(50000); //if failed to get mux2, release mux1 first
pthread_mutex_lock(&mux2);
}
++cnt;
LOGI(1, "thread %d: cnt = %d", *threadNum, cnt);
pthread_mutex_unlock(&mux2);
pthread_mutex_unlock(&mux1);
sleep(1);
}
}
55. Synchronizing native threads with
mutex at Android NDK
Add an Android.mk file in the jni folder with the
following content:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeThreadsMutex
LOCAL_SRC_FILES := NativeThreadsMutex.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
56. Synchronizing native threads with
mutex at Android NDK
Build and run the Android project, and use the
following command to monitor the logcat output
$ adb logcat -v time NativeThreadsMutex:I *:S
57. Synchronizing native threads with
conditional variables at Android NDK
Create an Android project that demonstrates the usage
of pthread conditional variables:
1. Create an Android application
named NativeThreadsCondVar.
2. Right-click on the project NativeThreadsCondVar,
select Android Tools | Add Native Support.
3. MainActivity.Java file simply loads the native
libraryNativeThreadsCondVar and calls the
native jni_start_threads method.
58. Synchronizing native threads with
conditional variables at Android NDK
4. Add two files under the jni folder –
4. mylog.h
5. NativeThreadsCondVar.cpp
5. NativeThreadsCondVar.cpp contains the code to
start two threads
6. The two threads will update a shared counter.
7. The jni_start_threads function initializes the
mutex, conditional variable and creates two
threads:
59. Synchronizing native threads with
conditional variables at Android NDK
pthread_mutex_t mux;
pthread_cond_t cond;
void jni_start_threads() {
pthread_t th1, th2;
int threadNum1 = 1, threadNum2 = 2;
int ret;
pthread_mutex_init(&mux, NULL);
pthread_cond_init(&cond, NULL);
ret = pthread_create(&th1, NULL, run_by_thread1,
void*)&threadNum1);
LOGI(1, "thread 1 started");
ret = pthread_create(&th2, NULL, run_by_thread2,
void*)&threadNum2);
LOGI(1, "thread 2 started");
ret = pthread_join(th1, NULL);
LOGI(1, "thread 1 end %d", ret);
ret = pthread_join(th2, NULL);
LOGI(1, "thread 2 end %d", ret);
pthread_mutex_destroy(&mux);
pthread_cond_destroy(&cond);
}
60. Synchronizing native threads with
conditional variables at Android NDK
int cnt = 0;
int THR = 10, THR2 = 5;
void *run_by_thread1(void *arg) {
int* threadNum = (int*)arg;
pthread_mutex_lock(&mux);
while (cnt != THR2) {
LOGI(1, "thread %d: about to wait", *threadNum);
pthread_cond_wait(&cond, &mux);
}
++cnt;
LOGI(1, "thread %d: cnt = %d", *threadNum, cnt);
pthread_mutex_unlock(&mux);
}
62. Synchronizing native threads with
conditional variables at Android NDK
Add an Android.mk file under the jni folder with
the following content:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeThreadsCondVar
LOCAL_SRC_FILES := NativeThreadsCondVar.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
63. Synchronizing native threads with
conditional variables at Android NDK
Build and run the Android project, and use the
following command to monitor the logcat output-
$ adb logcat -v time NativeThreadsCondVar:I *:S
64. Synchronizing native threads with
reader/writer locks at Android NDK
1. Create an Android application named
NativeThreadsRWLock.
2. Right-click on the project NativeThreadsRWLock,
select Android Tools | Add Native Support.
3. MainActivity Java file simply loads the native
libraryNativeThreadsRWLock and calls the native
method jni_start_threads.
4. Add two files
named mylog.h and NativeThreadsRWLock.cpp und
er the jni folder.
65. Synchronizing native threads with
reader/writer locks at Android NDK
5. jni_start_threads starts pNumOfReader reader
threads and pNumOfWriter writer threads:
66. Synchronizing native threads with
reader/writer locks at Android NDK
void jni_start_threads(JNIEnv *pEnv, jobject pObj, int pNumOfReader, int pNumOfWriter) {
pthread_t *ths;
int i, ret;
int *thNum;
ths = (pthread_t*)malloc(sizeof(pthread_t)*(pNumOfReader+pNumOfWriter));
thNum = (int*)malloc(sizeof(int)*(pNumOfReader+pNumOfWriter));
pthread_rwlock_init(&rwlock, NULL);
for (i = 0; i < pNumOfReader + pNumOfWriter; ++i) {
thNum[i] = i;
if (i < pNumOfReader) {
ret = pthread_create(&ths[i], NULL, run_by_read_thread, (void*)&(thNum[i]));
} else {
ret = pthread_create(&ths[i], NULL, run_by_write_thread, (void*)&(thNum[i]));
}
}
for (i = 0; i < pNumOfReader+pNumOfWriter; ++i) {
ret = pthread_join(ths[i], NULL);
}
pthread_rwlock_destroy(&rwlock);
free(thNum);
free(ths);
}
67. Synchronizing native threads with
reader/writer locks at Android NDK
void *run_by_read_thread(void *arg) {
int* threadNum = (int*)arg;
int ifRun = 1;
int accessTimes = 0;
int ifPrint = 1;
while (ifRun) {
if (!pthread_rwlock_rdlock(&rwlock)) {
if (100000*numOfWriter == sharedCnt) {
ifRun = 0;
}
if (0 <= sharedCnt && ifPrint) {
LOGI(1, "reader thread %d sharedCnt value before processing %dn", *threadNum, sharedCnt);
int j, k;//some dummy processing
for (j = 0; j < 100000; ++j) {
k = j*2;
k = sqrt(k);
}
ifPrint = 0;
LOGI(1, "reader thread %d sharedCnt value after processing %d %dn", *threadNum, sharedCnt, k);
}
if ((++accessTimes) == INT_MAX/5) {
accessTimes = 0;
LOGI(1, "reader thread %d still running: %dn", *threadNum, sharedCnt);
}
pthread_rwlock_unlock(&rwlock);
}
}
LOGI(1, "reader thread %d return %dn", *threadNum, sharedCnt);
return NULL;
}
68. Synchronizing native threads with
reader/writer locks at Android NDK
void *run_by_write_thread(void *arg) {
int cnt = 100000, i, j, k;
int* threadNum = (int*)arg;
for (i = 0; i < cnt; ++i) {
if (!pthread_rwlock_wrlock(&rwlock)) {
int lastShCnt = sharedCnt;
for (j = 0; j < 10; ++j) { //some dummy processing
k = j*2;
k = sqrt(k);
}
sharedCnt = lastShCnt + 1;
pthread_rwlock_unlock(&rwlock);
}
}
LOGI(1, "writer thread %d return %d %dn", *threadNum, sharedCnt, k);
return NULL;
}
69. Synchronizing native threads with
reader/writer locks at Android NDK
Add an Android.mk file under the jni folder with the following
content:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeThreadsRWLock
LOCAL_SRC_FILES := NativeThreadsRWLock.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
70. Synchronizing native threads with
reader/writer locks at Android NDK
Build and run the Android project, and use the following
command to monitor the logcat output:
adb logcat -v time NativeThreadsRWLock:I *:S
71. Synchronizing native threads with
semaphore at Android NDK
1 Create an Android application named
NativeThreadsSemaphore.
2 Right-click on the project NativeThreadsSemaphore, select
Android Tools | Add Native Support
3 Activity Java file simply loads the native
library NativeThreadsSemaphore and calls the
native jni_start_threads method.
4 Add two files
named mylog.h and NativeThreadsSemaphore.cpp under
the jni folder.
72. Synchronizing native threads with
semaphore at Android NDK
5. jni_start_threads creates pNumOfConsumer number of
consumer threads, pNumOfProducer number of producer
threads, and numOfSlots number of slots
73. Synchronizing native threads with
semaphore at Android NDK
void jni_start_threads(JNIEnv *pEnv, jobject pObj, int pNumOfConsumer, int pNumOfProducer, int numOfSlots) {
pthread_t *ths;
int i, ret;
int *thNum;
pthread_mutex_init(&mux, NULL);
sem_init(&emptySem, 0, numOfSlots);
sem_init(&fullSem, 0, 0);
ths = (pthread_t*)malloc(sizeof(pthread_t)*(pNumOfConsumer+pNumOfProducer));
thNum = (int*)malloc(sizeof(int)*(pNumOfConsumer+pNumOfProducer));
for (i = 0; i < pNumOfConsumer + pNumOfProducer; ++i) {
thNum[i] = i;
if (i < pNumOfConsumer) {
ret = pthread_create(&ths[i], NULL,
un_by_consumer_thread, (void*)&(thNum[i]));
} else {
ret = pthread_create(&ths[i], NULL, run_by_producer_thread, (void*)&(thNum[i]));
}
}
for (i = 0; i < pNumOfConsumer+pNumOfProducer; ++i) {
ret = pthread_join(ths[i], NULL);
}
sem_destroy(&emptySem);
sem_destroy(&fullSem);
pthread_mutex_destroy(&mux);
free(thNum);
free(ths);
}
74. Synchronizing native threads with
semaphore at Android NDK
void *run_by_producer_thread(void *arg) {
int* threadNum = (int*)arg;
int i;
for (i = 0; i < 4; ++i) {
sem_wait(&emptySem);
pthread_mutex_lock(&mux);
++numOfItems;
pthread_mutex_unlock(&mux);
sem_post(&fullSem);
}
return NULL;
}
75. Synchronizing native threads with
semaphore at Android NDK
Add an Android.mk file under the jni folder with the following
content:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeThreadsSemaphore
LOCAL_SRC_FILES := NativeThreadsSemaphore.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
78. Managing data for native threads at
Android NDK
There are several options when we want to preserve thread-wide
data across functions, including global variables,
argument passing, and thread-specific data key.
Here we will discusses all the three options with a focus on
thread-specific data key.
79. Managing data for native threads at
Android NDK
Create an Android application named NativeThreadsData.
Right-click on the project NativeThreadsData, select Android
Tools | Add Native Support.
This Java file simply loads the native library
NativeThreadsData and calls the native methods.
Add mylog.h and NativeThreadsData.cpp files under the jni
folder. The mylog.h contains the Android native logcat utility
functions, while the NativeThreadsData.cpp file contains the
native code to start multiple threads.
80. Managing data for native threads at
Android NDK
jni_start_threads starts n number of threads, where n is
specified by the variable pNumOfThreads:
81. Managing data for native threads at
Android NDK
void jni_start_threads(JNIEnv *pEnv, jobject pObj, int pNumOfThreads) {
pthread_t *ths;
int i, ret;
int *thNum;
ths = (pthread_t*)malloc(sizeof(pthread_t)*pNumOfThreads);
thNum = (int*)malloc(sizeof(int)*pNumOfThreads);
pthread_mutex_init(&mux, NULL);
pthread_key_create(&muxCntKey, free_muxCnt);
for (i = 0; i < pNumOfThreads; ++i) {
thNum[i] = i;
ret = pthread_create(&ths[i], NULL, run_by_thread, (void*)&(thNum[i]));
}
for (i = 0; i < pNumOfThreads; ++i) {
ret = pthread_join(ths[i], NULL);
}
pthread_key_delete(muxCntKey);
pthread_mutex_destroy(&mux);
free(thNum);
free(ths);
}
82. Managing data for native threads at
Android NDK
The thread_step_1 function is executed by threads. It gets the
data associated with the thread- specific key and uses it to
count the number of times the mutex is locked:
83. Managing data for native threads at
Android NDK
void thread_step_1() {
struct timeval st, cu;
long stt, cut;
int *muxCntData = (int*)pthread_getspecific(muxCntKey);
gettimeofday(&st, NULL);
stt = st.tv_sec*1000 + st.tv_usec/1000;
do {
pthread_mutex_lock(&mux);
(*muxCntData)++;
pthread_mutex_unlock(&mux);
gettimeofday(&st, NULL);
cut = st.tv_sec*1000 + st.tv_usec/1000;
} while (cut - stt < 10000);
}
84. Managing data for native threads at
Android NDK
The thread_step_2 function is executed by threads. It gets the
data associated with the thread-specific key and prints it out:
void thread_step_2(int thNum) {
int *muxCntData = (int*)pthread_getspecific(muxCntKey);
LOGI(1, "thread %d: mux usage count: %dn", thNum,
*muxCntData);
}
85. Managing data for native threads at
Android NDK
The run_by_thread function is executed by threads:
void *run_by_thread(void *arg) {
int* threadNum = (int*)arg;
int *muxCntData = (int*)malloc(sizeof(int));
*muxCntData = 0;
pthread_setspecific(muxCntKey, (void*)muxCntData);
thread_step_1();
thread_step_2(*threadNum);
return NULL;
}
86. Managing data for native threads at
Android NDK
Add an Android.mk file under the jni folder with the following
content:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NativeThreadsData
LOCAL_SRC_FILES := NativeThreadsData.cpp
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
87. Managing data for native threads at
Android NDK
Build and run the Android project, and use the following
command to monitor the logcat output –
$ adb logcat -v time NativeThreadsData:I *:S
88. Ref
Books and related material –
Android Native Development Kit Cookbook
By - by Feipeng Liu
Publisher: Packt Publishing