Date post: | 06-Aug-2015 |
Category: |
Mobile |
Upload: | khiem-kim-ho-xuan |
View: | 125 times |
Download: | 6 times |
Introduction
• What is the Android NDK?• Write, Compile and Embed Native code into Apps• Porting existing C/C++ code
Android SDK
• Write Apps in Java• Resources and
compiled codes packed In APK
• Get access to the Android Framework
• Interpreted by the Dalvik VM
What is the Android NDK?
• A tool to Write, Compile and Embed Native code in Apps• Usable for 3 types architecture:
• ARM, X86 and MIPS• Makefile and GDB Debugger support!
What is the Android NDK?
• Download NDK from this page:• https://developer.android.com/tools/sdk/ndk/
index.html
Why use Android NDK?
• It is for you…. if:• Need performance (Games or intensive Apps)
• Control memory allocation and alignment yourself• Write CPU-intensive operations• Exceed Java Apps memory limitations
• Port C/C++ code• Reuse legacy codes
Why use Android NDK?
• the NDK will not benefit most apps! • Best for game engines • CPU intensive workload • Signal processing • Physics simulation
Why use Android NDK?
• It is not for you… if:• Think Java is complicated:
• C/C++ would make it worse• JNI is a headache
Android Studio
• Not an easy setup for Android Studio. • Gradle hack!• Check https://bitbucket.org/khiemkimxuan/ndk
Creating a NDK project with Android Studio
• In short:1. Create an Android project
2. Add new «JNI» folder to the Project.
3. Add ndk.dir=location of ndk, to local.properties file
4. Add hacks on gradle build file (check the link from the previous slide!)
5. C/C++ files and Makefiles must be in the Project’s jni folder
6. Compile it!
1. Binary SO librares generated in /lib/armeabi
7. Alternatively, use standalone toolchain to cross compile C/C++ or Assembly file and run it on adb shell!
Write your C/C++ source file
#include <jni.h>#include <string.h>
JNIEXPORT jstring JNICALL Java_com_myproject_MyActivity_getMyData(JNIEnv* pEnv, jobject pThis){ return (*pEnv)->NewStringUTF(pEnv, "My native project talks C, not C++ ok? Pointer difference!!");}
#include <jni.h>#include <string.h>
extern "C"JNIEXPORT jstring JNICALL Java_com_myproject_MyActivity_getMyData(JNIEnv* pEnv, jobject pThis){ return pEnv->NewStringUTF("My native project talks C++, not C ok? Pointer difference!!");}}
C code
C++ code
Write your Java Activity
package com.myproject;
Import ..............
public class MyActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setTitle(getMyData());
}
public native String getMyData();
static {
System.loadLibrary("mylib");
}
}
Write a Makefile
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := mylibLOCAL_SRC_FILES := MyActivity.c
include $(BUILD_SHARED_LIBRARY)LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv2
# Typical filename should end with .mk. To build, add ndk directory to # System PATH and run ndk-build inside the jni folder.# You should also create Application.mk file that describes APP_ABI #and APP_PLATFORM
16
Queen Game - example C vs Javastatic int isConsistent(int q[], int n) { int i; for(i = 0; i < n; i++) { if(q[i] == q[n]) return FALSE; if(q[i] - q[n] == (n - i)) return FALSE; if(q[n] - q[i] == (n-i)) return FALSE; } return TRUE;} static void enumerateRec(int board[], int n) { int board_length = sizeof(board); if(n == board_length) { //print_result(board); } else { int i = 0; for(i = 0; i < board_length; i++) { board[n] = i; if(isConsistent(board,n) == TRUE) enumerateRec(board,n+1); } } }
30.04.2015
static void enumerate(int N) { int board[N]; memset(board,0,sizeof(board)); enumerateRec(board, 0);} JNIEXPORT void JNICALL Java_com_example_kkh_myapplication_MainActivity_runQueen(JNIEnv *env, jobject obj,jint arg) { int n = (int)arg; enumerate(n);}
17
Queen Game - example C vs Java
30.04.2015
public class MainActivity extends Activity { public native String stringFromJNI(); public native void runQueen(int N); static { System.loadLibrary("main"); }
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.inject(this); runQueen(200000); } …
18
Queen Game - example C vs Java
30.04.2015
public static boolean isConsistent(int[] q, int n) { for (int i = 0; i < n; i++) { if (q[i] == q[n]) return false; if ((q[i] - q[n]) == (n - i)) return false; if ((q[n] - q[i]) == (n - i)) return false; } return true; }… public static void enumerate(int[] q, int n) { int N = q.length; if (n == N) printQueens(q); else { for (int i = 0; i < N; i++) { q[n] = i; if (isConsistent(q, n)) enumerate(q, n + 1); } } }
public static void enumerate(int N) { int[] a = new int[N]; enumerate(a, 0); }}
19
Makefile(s) needed
• Android.mk:LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS) LOCAL_MODULE := mainLOCAL_C_INCLUDES := $(LOCAL_PATH)LOCAL_SRC_FILES := main.c queengame.c threadexample.cLOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv2
include $(BUILD_SHARED_LIBRARY)
• Application.mk:APP_ABI := armeabi armeabi-v7a x86 mips APP_PLATFORM := android-21 #APP_STL := stlport_static
30.04.2015
20
Measure Performance C vs Java
30.04.2015
• Queen Game:• C: 0.00001• Java: 0.00222
• Fibonacci:• C Result: 0.01251• Java: 0.04512
The C/C++ side...jint JNICALL Java_com_myproject_MyStore_addition (JNIEnv *pEnv, jobject pObj, jint pa, jint pb) { return pa + pb;}...
• JNIEnv allows manipulating the Virtual Machine (functions are mapped to Java methods)
• Java types are mapped to JNI native types • Primitives can be converted to classic C/C++ primitives• Mainly Java Reflection: ...
struct JNINativeInterface { jclass (*FindClass)(JNIEnv*, const char*); jint (*ThrowNew)(JNIEnv *, jclass, const char *); jobject (*NewGlobalRef)(JNIEnv*, jobject); jobject (*NewLocalRef)(JNIEnv*, jobject); jobject (*NewObject)(JNIEnv*, jclass, jmethodID, ...); jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...); jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
...};
Java Primitive Type Data
Java Type JNI Type C Type
boolean jboolean unsigned char
byte jbyte signed char
char jchar unsigned short
double jdouble double
float jfloat float
byte jbyte signed char
int jint int
long jlong long long
short jshort short
Java Reference Types Mapping
Java Type Native Type
java.lang.Class jclass
java.lang.Throwable jthrowable
java.lang.String jstring
Other objects jobject
java.lang.Object[] jobjectArray
boolean[] jbooleanArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray
Other arrays Jarray
Usage of Libraries
• Android Log library• Log message to LogCat
__android_log_print(ANDROID_LOG_INFO,"TAG","Message me\n");
• Other C Libraries • Memory management• File management• NativeThreads• Time• OpenGL• ...
15.04.202325
Bionic API (1/3) – Introduction
• Bionic is the POSIX standard C library for Android Platforms
• Best suited for mobile computing and provides lightweight wrapper around kernel.
• Bionic provides C standard library macros, type definitions, functions etc.
• Not every function in the standard C library is supported by Bionic.
15.04.202326
Bionic API (2/3) – Memory Management
• Dynamic Memory Management for C• Always include standard C library header:
• #include<stdlib.h>• Allocate Dynamic Memory in C:
• void* malloc(size_t size);• Deallocate Dynamic Memory in C:
• void free(void* memory);• Changing Dynamic Memory Allocation in C:
• void* realloc(void* memory, size_t size);
15.04.202327
Bionic API (3/3) – Standard File I/O
• Standard Streams:• stdin: Standard input stream• stdout: Standard output stream• stderr: Standard error stream
• Always include standard I/O C library header:• #include<stdio.h>
• Most file I/O functions can be used:• write: fopen, fwrite, fputs, fputc....• read: fread, fgets, fgetc, fscanf....• seek: fseek
15.04.202328
Native Threads (POSIX Threads)
• A part of the Bionic C standard library• #include <pthread.h>
• Pthread functions:• pthread_create, pthread_join,
• The POSIX threads are not known to the Java VM.• Solution? Attach them to the Java VM!
• Use jint JNI_OnLoad (JavaVM *vm, void* reserved) function as it gets invoked by the virtual machine when the shared library is loaded.
• Cannot share JNIEnv, it’s thread local• Use AttachCurrentThread, DetachCurrentThread the thread
before exit
29
Native Thread Example C side jint JNI_OnLoad (JavaVM* vm, void* reserved) { jvm1 = vm; return JNI_VERSION_1_6;} void *run_task(void *args) { JNIEnv* env = NULL; int n = (*jvm1)->AttachCurrentThread(jvm1,&env, NULL); if (n == 0) { jstring msg = (*env)->NewStringUTF(env,"Yes Thread Running."); (*env)->CallVoidMethod(env, obj1, mid1, msg); (*env)->DeleteGlobalRef(env,obj1); (*jvm1)->DetachCurrentThread(jvm1); }} 30.04.2015
30
Native Thread Example C sidevoid init_instance(JNIEnv *env) { jclass jz1 = (*env)->FindClass(env,"com/example/kkh/myapplication/MainActivity"); jmethodID mid = (*env)->GetMethodID(env,jz1,"<init>","()V"); jobject myobj = (*env)->NewObject(env,jz1,mid); obj1 = (*env)->NewGlobalRef(env,myobj); mid1 = (*env)->GetMethodID(env, jz1, "setMsg", "(Ljava/lang/String;)V"); } JNIEXPORT void JNICALL Java_com_example_kkh_myapplication_MainActivity_startNativeThread (JNIEnv *env) { init_instance(env); pthread_t thread1; int n = pthread_create(&thread1,NULL,run_task,NULL); if (n != 0) { jclass exceptionClazz = (* env)-> FindClass(env,"java/lang/RuntimeException"); (* env)-> ThrowNew(env,exceptionClazz, "create thread error."); } }
30.04.2015
31
Native Thread Example in Javapublic class MainActivity extends Activity {
@InjectView(R.id.thread_start_button) Button mStartNativeThreadButton;
public static MainActivity instance; public native void startNativeThread();
static { System.loadLibrary("main"); } @Override protected void onCreate(Bundle savedInstanceState) { … ButterKnife.inject(this); instance = this; }
30.04.2015
@OnClick(R.id.thread_start_button) public void startThreadButton(View v) { if(v.getId() == R.id.thread_start_button) { startNativeThread(); } } public void setMsg(final String msg) {Toast.makeText(instance, msg, Toast.LENGTH_LONG).show();}
15.04.202332
C/C++ App (1/3)
• Write only C or C++ app for Android rather than having any Java code.
• Need to specify native_app_glue at the local static library flag.
• Add app_dummy() at android_main() to make sure that the glue isn’t stripped
• Make sure to add android.app.NativeActivity as Android name for the Activity.• http://developer.android.com/reference/android/app/
NativeActivity.html
15.04.202333
C/C++ App (2/3)
• In the AndroidManifest.xml ( or create an Activity file and extend android.app.NativeActivity and load the library)
<activity android:name="android.app.NativeActivity" android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:screenOrientation="landscape" android:uiOptions="none"> <meta-data android:name="android.app.lib_name" android:value="main" /> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter></activity>
15.04.202334
C/C++ App (3/3)
• Example Brick Breaker (download code at the link given from slide 10)
Porting existing C/C++ code
• Cross-compile on cmd or a terminal in Linux!• Commands to cross-compile (add to environment path and remember to
include sysroot platforms):• ARM: arm-linux-androideabi-gcc/g++-<version> --sysroot <path>
<files.c/cpp>…
• Intel X86: i686-linux-android-gcc/g++ --sysroot <path> <files.c/cpp>…
• MIPS: mipsel-linux-android-gcc/g++ --sysroot <path> <files.c/cpp>…
• Build your own NDK Makefile• Example project for porting C code to an existing app:
• The Emerald Programming Language:
https://bitbucket.org/khiemkimxuan/emerald-lite
15.04.202340
Example of Standalone Toolchain
• Simple Socket example from Beej Client and Server• Cross compile Client and port it!
• Follow these steps:• <architecture-gcc> -fPIE -pie --sysroot <path of sysroot> filename.c -o
filename• adb push filename /local/data/tmp/filename• adb shell chmod 0755 /local/data/tmp/filename• adb shell <path to filename>• ./filename
• Binary files need permission (chmod that file(s)!).• Android runtime runs binaries only if they have
permission.
15.04.202341
Thank you for listening!
“Before downloading the NDK, you should understand that the NDK will not benefit most apps. As a developer, you need to balance its benefits against its drawbacks. Notably, using native code on Android generally does not result in a noticable performance improvement, but it always increases your app complexity. In general, you should only use the NDK if it is essential to your app—never because you simply prefer to program in C/C++.”
– Android Developers
42 © Computas AS 15.04.2023
Questions?
Computas AS Tel +47 67 83 10 00Lysaker Torg 45, pb 482 Fax +47 67 83 10 011327 Lysaker Org.nr: NO 986 352 325 MVANorway www.computas.com
Khiem-Kim Ho Xuan