<pre name="code" class="cpp">ndk開發: 1.編譯android本地程序的二種方法.q 2.安裝ndk編譯工具. 3.編寫android.apk程序. 4.編寫jni接口.定義應用程序接口, 5.編寫Java文件,生成相應的字節碼文件. 6.使用javah -jni Test 命令生成該java文件相應的c的頭文件. 7.使用ndk-build命令生成相應的庫文件. 一:建立一個arm本地程序.直接使用arm-linux-gcc 進行編譯,假設使用到庫的話需要使用 -statickeyword進行靜態連接庫文件. 1.編譯android本地程序的三種方法: 1.使用ndk開發工具進行編譯. a)安裝ndk android-ndk-r9d-linux-x86_64.tar.bz2 使用tar -xvf android-ndk-r9d-linux-x86_64.tar.bz2. b)解壓完畢需要配置環境變量: vim ~/.bashrc 加入如下的行. export PATH=/home/zshh/android-ndk-r9d:$PATH //這個是解壓後ndk所在文件文件夾/home/zshh/android-ndk-r9d c)拷貝一個ndk的例子文件到測試文件夾.例子文件存在/home/zshh/android-ndk-r9d/samples中. $ cd /home/zshh/android-ndk-r9d/samples 下. $ cp -a hello-jni/ ~/work/android/JNI/ $ zshh@HP:~/work/android/JNI/hello-jni/jni$ vim Android.mk LOCAL_PATH := $(call my-dir) // LOCAL_PATH 指的是要編譯的文件夾. cd ~/work/android/JNI/hello-jni/ 使用ndk-build,該文件夾指的就是~/work/android/JNI/hello-jni/文件夾. include $(CLEAR_VARS) // 使用ndk進行編譯的時候.必須指定這項.它會清空Makefile中所有的變量值.使用android源碼進行編譯的時候,不能使用該項. LOCAL_MODULE := hello-jni // 編譯完畢以後的模塊的名稱. LOCAL_SRC_FILES := hello-jni.c // 生成模塊需要的.c文件. //如下的是二者選其一. include $(BUILD_SHARED_LIBRARY) // 指定生成什麼樣的文件.仍是動態庫文件. include $(BUILD_EXECUTABLE) // 指定生成什麼樣的文件.是可運行文件 改動一下hello-jni.c輸出Hello字符. 2.改動文件.生成可運行文件. zshh@HP:~/work/android/JNI/hello-jni/jni$ vim hello-jni.c #if 0 #endif 凝視其它文件. 改動爲例如如下: #include <string.h> #include <jni.h> int main(void) { printf("Hello\n"); return 0 ; } zshh@HP:~/work/android/JNI/hello-jni/jni$ ndk-build /home/zshh/work/android/JNI/hello-jni/jni/hello-jni.c:57:2: warning: incompatible implicit declaration of built-in function 'printf' [enabled by default] 進行編譯會輸出如上警告.是覺得printf沒有包括stdlib.h頭文件.需要加入#include<stdlib.h>頭文件. zshh@HP:~/work/android/JNI/hello-jni/jni$ ndk-build 生成例如如下文件. zshh@HP:~/work/android/JNI/hello-jni/jni$ ndk-build [armeabi-v7a] Gdbserver : [arm-linux-androideabi-4.6] libs/armeabi-v7a/gdbserver [armeabi-v7a] Gdbsetup : libs/armeabi-v7a/gdb.setup [armeabi] Gdbserver : [arm-linux-androideabi-4.6] libs/armeabi/gdbserver [armeabi] Gdbsetup : libs/armeabi/gdb.setup [x86] Gdbserver : [x86-4.6] libs/x86/gdbserver [x86] Gdbsetup : libs/x86/gdb.setup [mips] Gdbserver : [mipsel-linux-android-4.6] libs/mips/gdbserver [mips] Gdbsetup : libs/mips/gdb.setup [armeabi-v7a] Install : hello-jni => libs/armeabi-v7a/hello-jni [armeabi] Install : hello-jni => libs/armeabi/hello-jni [x86] Install : hello-jni => libs/x86/hello-jni [mips] Install : hello-jni => libs/mips/hello-jni 他會編譯生成三個平臺的可運行文件,平臺如上. x86,mips,armeabi-v7a, 3.改動編譯生成指定平臺的可運行文件. zshh@HP:~/work/android/JNI/hello-jni/jni$ vim Application.mk APP_ABI := all 這個是生成支持的三種平臺的可運行文件. APP_ABI := armeabi-v7a 僅僅會生成armeabi-v7a平臺的可運行代碼. 4.將生成的文件下載到開發版運行. //開機進入系統僅僅會.檢查usb是否插好.等待進入系統以後,運行掛載命令.是 zshh@HP:~/work/android/JNI/hello-jni/jni$ adb shell mount -o remount,rw /system //這個命令的做用又一次使用讀寫權限掛載這個文件. //切換到編譯完畢的可運行文件所在文件夾. zshh@HP:$ cd ~/work/android/JNI/hello-jni/libs/armeabi-v7a zshh@HP:~/work/android/JNI/hello-jni/libs/armeabi-v7a$ adb push hello-jni /system //將應用程序下載到開發版的/system路徑下. 208 KB/s (9500 bytes in 0.044s) 5.最後測試可否成功輸出,Hello. zshh@HP:~/work/android/JNI/hello-jni/libs/armeabi-v7a$ adb shell //經過android調試橋登入android操做系統. root@android:cd /system root@android:/system # ./hello-jni Hello 最後測試完畢.輸出Hello. 6.假設使用編譯完畢的android源碼進行編譯的話.需要改動Android.MK文件, a. 改動Android.mk zshh@HP:~/work/android/JNI/hello-jni/jni$ vim Android.mk LOCAL_PATH := $(call my-dir) // LOCAL_PATH 指的是要編譯的文件夾. cd ~/work/android/JNI/hello-jni/ 使用ndk-build,該文件夾指的就是~/work/android/JNI/hello-jni/文件夾. #include $(CLEAR_VARS) // 這項需要凝視掉,它的做用是清除MK文件裏變量的值.使用android源碼編譯時,不需要這樣作.切記凝視. LOCAL_MODULE := hello-jni // 編譯完畢以後的模塊的名稱. LOCAL_SRC_FILES := hello-jni.c // 生成模塊需要的.c文件. //如下的是二者選其一,指定生成什麼樣的文件. include $(BUILD_SHARED_LIBRARY) // 這個是生成動態庫文件. include $(BUILD_EXECUTABLE) // 這個是生成可運行文件 zshh@HP$ cd /home/zshh/work/arm/ARM1/Android/android-4.2.2_r1 b.設置當前shell的運行環境. zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source ~/.bashrc zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source setenv including device/asus/grouper/vendorsetup.sh including device/asus/tilapia/vendorsetup.sh including device/friendly-arm/tiny4412/vendorsetup.sh including device/generic/armv7-a-neon/vendorsetup.sh including device/generic/armv7-a/vendorsetup.sh including device/generic/mips/vendorsetup.sh including device/generic/x86/vendorsetup.sh including device/lge/mako/vendorsetup.sh including device/samsung/maguro/vendorsetup.sh including device/samsung/manta/vendorsetup.sh including device/samsung/toroplus/vendorsetup.sh including device/samsung/toro/vendorsetup.sh including device/ti/panda/vendorsetup.sh including sdk/bash_completion/adb.bash c.使用mmm編譯需要編譯的文件夾. zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/JNI/jni_source_build/jni/ target Executable: hello-jni (out/target/product/tiny4412/obj/EXECUTABLES/hello-jni_intermediates/LINKED/hello-jni) /home/zshh/work/arm/ARM1/Android/android-4.2.2_r1/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.6 /bin/../lib/gcc/arm-linux-androideabi/4.6.x-google/../../../../arm-linux-androideabi/bin/ld: out/target/product/tiny4412/obj/lib/crtbegin_dynamic.o: in function _start:crtbrand.c(.text+0x60): error: undefined reference to '__libc_init' 假設出現的這個錯誤.那麼需要作包括libc庫, LOCAL_SHARED_LIBRARIES :=libc 加入完畢以後的Android.mk文件例如如下 LOCAL_PATH := $(call my-dir) // LOCAL_PATH 指的是要編譯的文件夾. cd ~/work/android/JNI/hello-jni/ 使用ndk-build,該文件夾指的就是~/work/android/JNI/hello-jni/文件夾. #include $(CLEAR_VARS) // 這項需要凝視掉,它的做用是清除MK文件裏變量的值.使用android源碼編譯時,不需要這樣作.切記凝視. LOCAL_MODULE := hello-jni // 編譯完畢以後的模塊的名稱. LOCAL_SRC_FILES := hello-jni.c // 生成模塊需要的.c文件. LOCAL_SHARED_LIBRARIES :=libc //如下的是二者選其一,指定生成什麼樣的文件. include $(BUILD_SHARED_LIBRARY) // 這個是生成動態庫文件. include $(BUILD_EXECUTABLE) // 這個是生成可運行文件 zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/JNI/jni_source_build/jni/' Install: out/target/product/tiny4412/system/bin/hello-jni 假設成功編譯,生成的目標文件存在hello-jni文件out/target/product/tiny4412/system/bin/hello-jni d.接下來的步驟,和步驟5.是同樣的. 二:建立一個NDK本地程序. 該程序的功能是控制led的亮滅. 1.搭建一個eclipse環境.eclipse.tar.gz a.解壓這文件. zshh@HP:~/work/android$ tar -xvf eclipse.tar.gz -C software //解壓 zshh@HP:~/work/android$ cd software/eclipse/ zshh@HP:~/work/android/software/eclipse$ ./eclipse & //運行eclipse b.使用這個ide環境建立一個android apk應用程序. 它大概的功能是提供四個button,當按下當中某個button的時候,就讓某個燈亮起來. c.建立一個Test文件,當中包括接口文件例如如下: 1.定義c和java之間的接口.控制亮滅, 1.1.第一個接口應該是打開設備文件. native int openLed(); 1.2.最後一個是關閉設備文件. native int closeLed(); 1.3.第二個接口應該是亮, native int ledOn(int no); 1.4.第三個接口應該是滅. native int ledOff(int no); 2. zshh@HP:~/work/android/JNI/NDKnative$ vim Test.java /************************************************************************* > File Name: Test.java > Author: zshh0604 > Mail: zshh0604@.com > Created Time: Mon 22 Dec 2014 10:48:09 PM ************************************************************************/ public class Test { native int openLed(); native int closeLed(); native int onLed(int no); native int offLed(int no); static{ System.loadLibrary("led"); } public static void main(String[] args) { } } 3.編譯生成字節碼文件. zshh@HP:~/work/android/JNI/NDKnative$ javac Test.java 4.生成相應的頭文件,該命令運行完畢以後.會生成Test.h頭文件. zshh@HP:~/work/android/JNI/NDKnative$ javah -jni Test 5.拷貝一個NDK代碼例子.並把Test.h頭文件複製到當前文件的jni文件夾中. zshh@HP:~/work/android/JNI$ mkdir Jni 6.獲取ndk開發工具的代碼例子. zshh@HP:~/work/android/JNI/Jni$ cp -a ../../../../android-ndk-r9d/samples/hello-jni/ ./ zshh@HP:~/work/android/JNI/Jni/jni$ cp ../../NDKnative/Test.h 7.將Test.h更名爲led.c, mv Test.h led.c zshh@HP:~/work/android/JNI/Jni/jni$ mv Test.h led.c 8.獲取apk應用的完整類名.com_embsky_MainActivity //講這個名稱替換當前Test類名. 使用vim打開led.c文件,再命令行模式下使用例如如下命令吧Test替換成com_embsky_MainActivity :%s/Test/com_embsky_MainActivity/g. 獲得例如如下文件: #include <jni.h> #include <sys/types.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> static int fd; static int flags = 0; /* * Class: com_embsky_MainActivity * Method: openLed * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_openLed (JNIEnv *env, jobject obj) { if(flags == 0) { fd = open("dev/leds",O_RDWR); if(fd< 0) { return -EPERM; } flags = 1; return 0 ; } return -EBUSY; } /* * Class: com_embsky_MainActivity * Method: closeLed * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_closeLed (JNIEnv *env , jobject obj) { if(flags == 1) { close(fd); flags = 0; return 0; } return -ENODEV; } /* * Class: com_embsky_MainActivity * Method: onLed * Signature: (I)I */ JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_onLed (JNIEnv *env , jobject obj, jint no) { int ret; if(flags == 1) { ret = ioctl(fd,1 ,no); if(ret < 0 ) { return -EPERM; } return 0 ; } return -ENODEV; } /* * Class: com_embsky_MainActivity * Method: offLed * Signature: (I)I */ JNIEXPORT jint JNICALL Java_com_embsky_MainActivity_offLed (JNIEnv * env, jobject obj, jint no) { int ret; if(flags == 1) { ret = ioctl(fd, 0, no); if(ret < 0) { return -EPERM; } return 0; } return -ENODEV; } 9.改動Android.mk文件,例如如下: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := leds LOCAL_SRC_FILES := led.c include $(BUILD_SHARED_LIBRARY) 10.zshh@HP:~/work/android/JNI/Jni/jni$ ndk-build 11.生成動態庫例如如下:[armeabi-v7a] Install : libleds.so => libs/armeabi-v7a/libleds.so 12.將動態庫push到開發板的/system/lib文件夾下. zshh@HP:~/work/android/JNI/Jni/libs/armeabi-v7a$ adb push libleds.so /system/lib 13.動態庫製做完畢. 14.建立一個apk應用,建立一個類: com.embsky.MainActivity.再這個類中載入並調用本地庫led. javah -d ../jni com.onesuncomm.JniCallCTest 三: 使用android源碼編譯apk應用程序.System.loadLibaray("led"); zshh@HP:~/work/android/android/07Jar/JniAndroidSrc$ ls Android.mk led.c 使用這兩個文件編譯生成libled.so文件. //zshh@HP:~/work/android/android/06Jni/JniAndroidSrc$ vim Android.mk。 僅僅需要。Android.mk和led.c兩個文件. 1.使用android源碼編譯android應用程序, LOCAL_PATH := $(call my-dir) LOCAL_MODULE := libled //注意必須是在led前面加上lib,編譯生成的庫名是.libled.so,使用 LOCAL_SRC_FILES := led.c LOCAL_SHARED_LIBRARIES :=libc include $(BUILD_SHARED_LIBRARY) 2.編譯libled.so庫文件. zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source setenv zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/android/06Jni/JniAndroidSrc/ 3.輸出編譯完畢的路徑例如如下. Install: out/target/product/tiny4412/system/lib/libled.so 4.將生成的libled.so push到/system/lib中. Install: out/target/product/tiny4412/system/lib/libled.so zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1/out/target/product/tiny4412/system/lib$ adb push libled.so /system/lib/ 115 KB/s (5276 bytes in 0.044s) 5.生成Android fremework文件夾的 jar包。 zshh@HP:~/work/android/android/07Jar/Jar$ ls Android.mk com/embsky/Led.java //包括兩個java文件. package com.embsky; public class Led { native int openLed(); native int closeLed(); native int ledOn(int no); native int ledOff(int no); static { System.loadLibrary("led"); } public int ledStart(){ /*nothing*/ return openLed(); } public int ledStop(){ return closeLed(); } public int ledOps(int no, int on){ if(on == 1){ return ledOn(no); } return ledOff(no); } } 6.改動Android.mk文件. LOCAL_PATH :=$(call my-dir) LOCAL_SRC_FILES :=$(call all-subdir-java-files) //當前src文件夾下的所有文件. LOCAL_MODULE :=led LOCAL_JAVA_LIBRARIES := include $(BUILD_JAVA_LIBRARY) //包括這個生成的是Java的類庫. 7.完畢以後。可以編譯生成這個.jar文件. zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ source setenv zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/android/07Jar/Jar/ 8. 生成的jar包會存放在, Install: out/target/product/tiny4412/system/framework/led.jar 注意編譯的時候,該文件夾必定要由讀寫權限.不然會失敗. 假設因爲權限問題。可以使用chown zshh:zshh out/target/product/tiny4412/system/framework -R 更改用戶屬主. 生成完畢以後需要將這個framework下的led.jar放到開發版的/system/framework文件夾下. zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1/out/target/product/tiny4412/system/framework$ adb push led.jar /system/framework/ 9.需要改動開發板中 /etc/permissions //再platform.xml文件裏聲明咱們的框架庫文件.不然這個led.jar沒法使用. root@android:/etc/permissions # vim platform.xml <library name="led" file="/system/framework/led.jar"/> 以後從新啓動一下開發版, 10.編譯android apk zshh@HP:~/work/android/android/07Jar/Apk$ ls //apk包括例如如下文件和文件夾. AndroidManifest.xml Android.mk res src 需要改動AndroidManifest.xml文件. <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.embsky" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <uses-library android:name="led" /> //聲明依賴的led.jar文件.假設不聲明可以編譯經過,但運行會由錯誤. <activity android:name="com.embsky.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> 11.zshh@HP:~/work/android/android/07Jar/Apk$ gedit Android.mk LOCAL_PATH :=$(call my-dir) LOCAL_SRC_FILES :=$(call all-subdir-java-files) LOCAL_PACKAGE_NAME :=Led LOCAL_JAVA_LIBRARIES :=led //注意這裏必須聲明依賴的java庫. include $(BUILD_PACKAGE) 12.zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1$ mmm ~/work/android/android/07Jar/Apk/ 這個是編譯生成的apk文件Install: out/target/product/tiny4412/system/app/Led.apk zshh@HP:~/work/arm/ARM1/Android/android-4.2.2_r1/out/target/product/tiny4412/system/app$ adb push Led.apk /system/app/ 5099 KB/s (294516 bytes in 0.056s)