Native Development Kit (NDK) :
Introduction
Rakesh Kumar Jha
M. Tech, MBA
Delivery Manager
Contents
What is an NDK and Why NDK? ·
Java Native Interface (JNI) ·
Using NDK ·
NDK and JNI by Example ·
NDK's ...
What is an NDK and Why NDK?
What is an NDK
• The NDK (Native Development Kit) is a tool
that allows you to program in C/C++ for
Android devices.
•...
Why NDK ?
• The NDK (Native Development Kit) allows you
to write code with C/C++ languages and then
call it from your J...
Why NDK ?
• Using native code in java is reasonable
especially when you are dealing with
bits/bytes operations, like bi...
Why NDK ?
• NDK will not be interpreted like Java through
the JVM, instead it will use the operating
system API (in And...
Why NDK ?
• One of the big advantages of the NDK is that
you can call custom allocation of memory
using malloc() method...
Why NDK ?
• Potentially, you can increase your application
performance but sometime it can be just
overkill, so use it ...
Why NDK ?
General steps for Java-NDK integration
1. Declare native method in java class.
2. Create a header file according to nati...
Step 1: Declare native method in java
class
1. First thing that we need to do is to create a
native method declaration ...
Step 2: Creating header file
What is Javah?
• Javah is a java utility that produces C headers
files from Java class, in...
Step 2: Creating header file
Creating a Header file using javah utility
• After we created our native SayHello() method ...
Step 2: Creating header file
• javah -classpath bin/classes/ -d
jni/ndk.NDK_Methods
• -classpath bin/classes/: location...
Step 2: Creating header file
• The generated header file will be created
under the jni directory in our project.
Step 2: Creating header file
• In example we will get the following header file:
#include <jni.h>
/* Header for class n...
Step 2: Creating header file
• Our java method declaration :
public static native String SayHello();
• Was generated to...
Step 2: Creating header file
Since we did not pass any parameters to the
function we accept only the default parameters:...
Step 3: Implementing the Header file
Step 3: Implementing the Header file
• Now we have a header file and we need to
define an implementation for our method....
Step 3: Implementing the Header file
//specifies the header file that is included
#include <ndk_NDK_Methods.h>
JNIEXPOR...
Step 3: Implementing the Header file
Notice that we are returning jstring which is
different from C string in our method...
Step 4: Creating an Android.mk file
Step 4: Creating an Android.mk file
• In order to compile our file to .so package we
need to define an Android.mk file i...
Step 4: Creating an Android.mk file
• LOCAL_PATH := $(call my-dir) - Android.mk must begin with this definition,
this de...
Application.mk
• Not necessary for the compilation, but in our
case we specified:
• APP_ABI := all – sets the compilati...
Step 5: Compiling the native code
Step 5: Compiling the native code
What is ndk-build?
• An ndk-build is an NDK tool which is
responsible for compiling y...
Compilation
• After we created the Android.mk file we can
create the .so package using ndk-build tool.
• Steps:
– Go t...
Step 6: Calling native method
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: ...
Java Native Interface (JNI)
• Install the JNI/NDK package from Google
• Create your Android project
• Make a JNI folder...
Java Native Interface (JNI)
• Install the JNI/NDK package from Google
• Create your Android project
• Make a JNI folder...
Android NDK Multithreading
10 Android NDK Tips
1 - Stay on Target
• The newest devices are generally ARMv7,
meaning that it can pay to use v7 builds and
features. The...
2 - Do not optimize immediately
• Unless you plan on porting an existing C++
application, do not rush into native.
3 - Optimize like a ninja
• When you do optimize, sneak in, turn the key
bits of your application into super-fast native...
4. Re-factor around your
optimisations
• Once you have a design in place, do not be
afraid to re-arrange code to make m...
5 - Maintain a Java fall-back
• Executing unsupported native code is a bad idea;
at best it will
• cause your applicati...
6 - Allocate with care
• Whenever possible allocate in Java anything
that is needed in Java rather than relying on
late...
7 - Multi-thread with great care
• With that in mind it is tempting to split
everything up into threads.
• It is a good...
8 - Thread at the Java Level
• When you do break your logic into threads, it
is better to do it via Java than pthreads
...
8 - Thread at the Java Level
• When you do break your logic into threads, it
is better to do it via Java than pthreads
...
Creating and terminating native
threads at Android NDK
void jni_start_threads() {
pthread_t th1, th2;
int threadNum1 =...
Creating and terminating native
threads at Android NDK
int retStatus;
void *run_by_thread(void *arg) {
int cnt = 3, i;...
Creating and terminating native
threads at Android NDK
Add an Android.mk file in the jni folder with the
following code...
Creating and terminating native
threads at Android NDK
Build and run the Android project, and use the
following command...
Creating and terminating native
threads at Android NDK
Synchronizing native threads with
mutex at Android NDK
1. Create an Android application
named NativeThreadsMutex.
2. R...
Synchronizing native threads with
mutex at Android NDK
4. Add two file –
4. 1. mylog.h and
5. NativeThreadsMutex.cpp i...
Synchronizing native threads with
mutex at Android NDK
int cnt = 0;
int THR = 10;
void *run_by_thread1(void *arg) {
i...
Synchronizing native threads with
mutex at Android NDK
void *run_by_thread2(void *arg) {
int* threadNum = (int*)arg;
w...
Synchronizing native threads with
mutex at Android NDK
Add an Android.mk file in the jni folder with the
following cont...
Synchronizing native threads with
mutex at Android NDK
Build and run the Android project, and use the
following command...
Synchronizing native threads with
conditional variables at Android NDK
Create an Android project that demonstrates the u...
Synchronizing native threads with
conditional variables at Android NDK
4. Add two files under the jni folder –
4. mylog...
Synchronizing native threads with
conditional variables at Android NDK
pthread_mutex_t mux;
pthread_cond_t cond;
void ...
Synchronizing native threads with
conditional variables at Android NDK
int cnt = 0;
int THR = 10, THR2 = 5;
void *run_...
Synchronizing native threads with
conditional variables at Android NDK
void *run_by_thread2(void *arg) {
int* threadNum...
Synchronizing native threads with
conditional variables at Android NDK
Add an Android.mk file under the jni folder with ...
Synchronizing native threads with
conditional variables at Android NDK
Build and run the Android project, and use the
f...
Synchronizing native threads with
reader/writer locks at Android NDK
1. Create an Android application named
NativeThrea...
Synchronizing native threads with
reader/writer locks at Android NDK
5. jni_start_threads starts pNumOfReader reader
th...
Synchronizing native threads with
reader/writer locks at Android NDK
void jni_start_threads(JNIEnv *pEnv, jobject pObj, ...
Synchronizing native threads with
reader/writer locks at Android NDK
void *run_by_read_thread(void *arg) {
int* threadN...
Synchronizing native threads with
reader/writer locks at Android NDK
void *run_by_write_thread(void *arg) {
int cnt = 1...
Synchronizing native threads with
reader/writer locks at Android NDK
Add an Android.mk file under the jni folder with th...
Synchronizing native threads with
reader/writer locks at Android NDK
Build and run the Android project, and use the foll...
Synchronizing native threads with
semaphore at Android NDK
1 Create an Android application named
NativeThreadsSemaphore...
Synchronizing native threads with
semaphore at Android NDK
5. jni_start_threads creates pNumOfConsumer number of
consum...
Synchronizing native threads with
semaphore at Android NDK
void jni_start_threads(JNIEnv *pEnv, jobject pObj, int pNumOf...
Synchronizing native threads with
semaphore at Android NDK
void *run_by_producer_thread(void *arg) {
int* threadNum = (...
Synchronizing native threads with
semaphore at Android NDK
Add an Android.mk file under the jni folder with the followin...
Synchronizing native threads with
semaphore at Android NDK
$ adb logcat -v time NativeThreadsSemaphore:I *:S
Managing data for native threads at
Android NDK
Managing data for native threads at
Android NDK
 There are several options when we want to preserve thread-wide
data a...
Managing data for native threads at
Android NDK
 Create an Android application named NativeThreadsData.
 Right-click ...
Managing data for native threads at
Android NDK
 jni_start_threads starts n number of threads, where n is
specified by...
Managing data for native threads at
Android NDK
void jni_start_threads(JNIEnv *pEnv, jobject pObj, int pNumOfThreads) { ...
Managing data for native threads at
Android NDK
 The thread_step_1 function is executed by threads. It gets the
data a...
Managing data for native threads at
Android NDK
void thread_step_1() {
struct timeval st, cu;
long stt, cut;
int *mux...
Managing data for native threads at
Android NDK
The thread_step_2 function is executed by threads. It gets the
data ass...
Managing data for native threads at
Android NDK
The run_by_thread function is executed by threads:
void *run_by_thread(...
Managing data for native threads at
Android NDK
Add an Android.mk file under the jni folder with the following
content:...
Managing data for native threads at
Android NDK
Build and run the Android project, and use the following
command to mon...
Ref
Books and related material –
Android Native Development Kit Cookbook
By - by Feipeng Liu
Publisher: Packt Publishi...
Questions ?
of 89

Native development kit (ndk) introduction

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
Published on: Mar 3, 2016
Published in: Mobile      
Source: www.slideshare.net


Transcripts - 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
  • 3. What is an NDK and Why NDK?
  • 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.
  • 10. Why NDK ?
  • 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.
  • 20. Step 3: Implementing the Header file
  • 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.
  • 24. Step 4: Creating an Android.mk file
  • 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.
  • 28. Step 5: Compiling the native code
  • 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.
  • 31. Step 6: Calling native method
  • 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.
  • 35. Android NDK Multithreading
  • 36. 10 Android NDK Tips
  • 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
  • 50. Creating and terminating native threads at Android NDK
  • 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); }
  • 61. Synchronizing native threads with conditional variables at Android NDK void *run_by_thread2(void *arg) { int* threadNum = (int*)arg; while (cnt < THR) { pthread_mutex_lock(&mux); if (cnt == THR2) { pthread_cond_signal(&cond); } else { ++cnt; LOGI(1, "thread %d: cnt = %d", *threadNum, cnt); } pthread_mutex_unlock(&mux); sleep(1); } }
  • 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)
  • 76. Synchronizing native threads with semaphore at Android NDK $ adb logcat -v time NativeThreadsSemaphore:I *:S
  • 77. Managing data for native threads at Android NDK
  • 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
  • 89. Questions ?

Related Documents