1,、項目實現了一個簡單的四則運算,項目的目錄層次如下:
AndroidManifest.xml Android.mk jni res src
資源文件簡簡單單,一個布局文件,稍後會有demo的下載地址
主要記錄備忘的內容如下:
MainActivity.java
[html] view plain public native int add(int x, int y);
public native int substraction(int x, int y);
public native float multiplication(int x, int y);
public native float division(int x, int y);
static{
System.loadLibrary("arithmetic");
}
2、生成lib的名稱為libarithmetic.so.注意load的時候寫"arithmetic"
jni 目錄下有兩個文件,一個是Android.mk,一個是c++源文件long.cpp
jni/Android.mk如下:
[html] view plain LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= libarithmetic
LOCAL_SRC_FILES:= \
long.cpp
LOCAL_SHARED_LIBRARIES := \
libutils
LOCAL_STATIC_LIBRARIES :=
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
LOCAL_CFLAGS +=
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
3、注釋:
LOCAL_PATH(必須定義,而且要第一個定義),宏函數『my-dir』, 由編譯系統提供,用於返回當前路徑(即包含Android.mk
file文件的目錄);
include $( CLEAR_VARS),
CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE為你清除許多LOCAL_XXX變數(例如 LOCAL_MODULE,
LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH
。這是必要的,因為所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變數都是全局的;
LOCAL_MODULE_TAGS :=user eng tests optional
user: 指該模塊只在user版本下才編譯
eng: 指該模塊只在eng版本下才編譯
tests: 指該模塊只在tests版本下才編譯
optional:指該模塊在所有版本下都編譯
LOCAL_MODULE(必須定義),標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。Note:編譯系統會自動產生合適的前綴和後綴,例如:arithmetic編譯成功後將生成libarithmetic.so庫文件
LOCAL_SRC_FILES 變數必須包含將要編譯打包進模塊中源代碼文件。不用在這里列出頭文件和包含文件。
LOCAL_SHARED_LIBRARIES中加入所需要鏈接的動態庫(*.so)的名稱
LOCAL_STATIC_LIBRARIES加入所需要鏈接的靜態庫(*.a)的名稱
LOCAL_CFLAG可選的編譯器選項,用法之一是定義宏,例如LOCAL_CFLAGS := -Werror作用是編譯警告也作為錯誤信息
LOCAL_PRELINK_MODULE:=false,不作prelink處理,默認是要prelink操作的,有可能造成地址空間沖突(這地方目前還不明白)
long.cpp源代碼如下:
[html] view plain #define LOG_TAG "LongTest2 long.cpp"
#include
#include
#include "jni.h"
jint add(JNIEnv *env, jobject thiz, jint x, jint y){
return x + y;
}
jint substraction(JNIEnv *env, jobject thiz, jint x, jint y){
return x - y;
}
jfloat multiplication(JNIEnv *env, jobject thiz, jint x, jint y){
return (float)x * (float)y;
}
jfloat division(JNIEnv *env, jobject thiz, jint x, jint y){
return (float)x/(float)y;
}
static const char *classPathName = "com/inspur/test2/MainActivity";
static JNINativeMethod methods[]= {
{"add", "(II)I", (void*)add},
{"substraction", "(II)I", (void*)substraction},
{"multiplication", "(II)F", (void*)multiplication},
{"division", "(II)F", (void*)division},
};
typedef union{
JNIEnv* env;
void* venv;
}UnionJNIEnvToVoid;
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods){
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL)
return JNI_FALSE;
if (env->RegisterNatives(clazz, gMethods, numMethods)<0)
return JNI_FALSE;
return JNI_TRUE;
}
static int registerNatives(JNIEnv *env){
if (!registerNativeMethods(env, classPathName,
methods, sizeof(methods)/sizeof(methods[0])))
{
return JNI_FALSE;
}
return JNI_TRUE;
}
jint JNI_OnLoad(JavaVM* vm, void* reserved){
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv *env = NULL;
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK){
goto l;
}
env = uenv.env;
env = uenv.env;
if (registerNatives(env) != JNI_TRUE){
goto l;
}
result = JNI_VERSION_1_4;
l:
return result;
}
除了利用 編寫native JAVA類,通過javah生成.h文件,根據.h文件編寫.c/cpp文件
方法外(名字像老太太的裹腳步,又臭又長,而且不靈活),Android還可以通過引用JNI_Onload方式實現。jint JNI_onLoad(JavaVM*
vm, void* reverced),改方法在so文件被載入時調用。
JNI_OnLoad()有兩個重要的作用:
指定JNI版本:告訴VM該組件使用那一個JNI版本(若未提供JNI_OnLoad()函數,VM會默認該使用最老的JNI
1.1版),如果要使用新版本的JNI,例如JNI
1.4版,則必須由JNI_OnLoad()函數返回常量JNI_VERSION_1_4(該常量定義在jni.h中) 來告知VM。
初始化設定,當VM執行到System.loadLibrary()函數時,會立即先呼叫JNI_OnLoad()方法,因此在該方法中進行各種資源的初始化操作最為恰當。
JNI_OnUnload()的作用與JNI_OnLoad()對應,當VM釋放JNI組件時會呼叫它,因此在該方法中進行善後清理,資源釋放的動作最為合適。
4、項目根目錄下Android.mk文件:
[html] view plain LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JNI_SHARED_LIBRARIES := libarithmetic
LOCAL_PACKAGE_NAME := LongTest
LOCAL_SHARED_LIBRARIES := \
libutils\
liblog
include $(BUILD_PACKAGE)
include $(LOCAL_PATH)/jni/Android.mk
# Also build all of the sub-targets under this one: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))
LOCAL_PACKAGE_NAME:項目名稱,即最終生成apk的名字
LOCAL_JNI_SHARED_LIBRARIES := libxxx就是把so文件放到apk文件里的libs/armeabi里
執行BUILD_PACKAGE。它的定義也是在config.mk中定義如下:BUILD_PACKAGE:=
$(BUILD_SYSTEM)/package.mk
$(call all-java-files-under, src)編譯的源代碼文件列表添加src目錄下所有的java 源文件
$(call all-makefiles-under, $(LOCAL_PATH))編譯器會在編譯完當前目錄下的文件後再深入子目錄編譯
如果make過android源碼,可以在項目根目錄下執行mm命令進行編譯。前提是執行過source
androidSRC/build/envsetup.sh
或者直接把source androidSRC/build/envsetup.sh添加到~/.bashrc中,會更加方便
『貳』 Android源碼編譯是干什麼
編譯Android系統。
『叄』 如何單獨編譯安卓系統源碼指定模塊
Android源碼目錄下的build/envsetup.sh文件,描述編譯的命令
- m: Makes from the top of the tree.
- mm: Builds all of the moles in the current directory.
- mmm: Builds all of the moles in the supplied directories.
要想使用這些命令,首先需要在android源碼根目錄執行. build/envsetup.sh 腳本設置環境
m:編譯所有的模塊
mm:編譯當前目錄下的模塊,當前目錄下要有Android.mk文件
mmm:編譯指定路徑下的模塊,指定路徑下要有Android.mk文件
下面舉個例子說明,假設我要編譯android下的\hardware\libhardware_legacy\power模塊,當前目錄為源碼根目錄,方法如下:
1、. build/envsetup.sh
2、mmm hardware/libhardware_legacy/power/
編譯完後 運行 make snod
會重新將你改過的模塊打入到system.img中
『肆』 如何在Android系統源碼中添加C項目
以hello_android為例,步驟如下:
1、在external目錄下創建hello_android目錄,然後在hello_android目錄中編寫hello_android C語言實現文件hello_android.h,hello_android.c:
(註:hello_android目錄可以放置在Android系統源碼下的任意目錄中,並非一定要在external下。)hello_android.h
#include<stdio.h>
#include<stdlib.h>void makePrintf(char *str)
{
printf("%s", str);
}hello_android.c
#include <stdio.h>
#include <stdlib.h>
#include "hello_android.h"int main(int argc, char** argv)
{
makePrintf("hello, android!\n");return 0;
}
2、編寫負責編譯的Android.mk文件:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES := hello_android.cLOCAL_C_INCLUDES += $(LOCAL_PATH)LOCAL_MODULE := hello_androidLOCAL_MODULE_TAGS := enginclude $(BUILD_EXECUTABLE)
3、利用mm編譯生成hello_android二進制可執行文件;
4、將helllo_android文件復制到/system/bin目錄下執行:
#./hello_android
hello, android!
5、代碼結構
$ pwd
external/hello_android
$ tree
『伍』 如何單獨編譯安卓系統源碼指定模塊
1.make模塊名稱需要查看Android.mk文件的LOCAL_PACKAGE_NAME變數。2.mmm命令用於在源碼根目錄編譯指定模塊,參數為模塊的相對路徑。只能在第一次編譯後使用。比如要編液薯檔譯Phone部分源碼,需要手畝在終端中執行以下命鬧亂令:$mmmpackages/apps/phone3.mm命令用於在模塊根目錄編譯這個模塊。只能在第一次編譯後使用。例如要編譯Phone部分源碼,需要在終端中執行以下命令:$cdpackages/apps/phone$mm註:mmm和mm命令必須在執行「.build/envsetup.sh」之後才能使用,並且只編譯發生變化的文件。如果要編譯模塊的所有文件,需要-B選項,例如mm-B。
『陸』 如何在Android系統源碼的環境下用make來編譯
第一個方法簡單點,不過需要在Android系統源碼的環境下用make來編譯:
1. 在應用程序的AndroidManifest.xml中的manifest節點中加入
android:sharedUserId="android.uid.system"這個屬性。
2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform這一行
3. 使用mm命令來編譯,生成的apk就有修改系統時間的許可權了。
『柒』 怎麼單獨編譯安卓系統源碼指定模塊
一. 首先在Android源代碼目錄下的build目錄下,有個腳本文件envsetup.sh,執行這個腳本文件後,就可以獲得一些有用的工具:
USER-NAME@MACHINE-NAME:~/Android$ . ./build/envsetup.sh
注意,這是一個source命令,執行之後,就會有一些額外的命令可以使用:
- croot: Changes directory to the top of the tree.
- m: Makes from the top of the tree.
- mm: Builds all of the moles in the current directory.
- mmm: Builds all of the moles in the supplied directories.
- cgrep: Greps on all local C/C++ files.
- jgrep: Greps on all local Java files.
- resgrep: Greps on all local res/*.xml files.
- godir: Go to the directory containing a file.
這些命令的具體用法,可以在命令的後面加-help來查看,這里我們只關注mmm命令,也就是可以用它來編譯指定目錄的所有模塊,通常這個目錄只包含一個模塊。
二. 使用mmm命令來編譯指定的模塊,例如Email應用程序:
USER-NAME@MACHINE-NAME:~/Android$ mmm packages/apps/Email/
編譯完成之後,就可以在out/target/proct/generic/system/app目錄下看到Email.apk文件了。Android系統自帶的App都放在這具目錄下。另外,Android系統的一些可執行文件,例如C編譯的可執行文件,放在out/target/proct/generic/system/bin目錄下,動態鏈接庫文件放在out/target/proct/generic/system/lib目錄下,out/target/proct/generic/system/lib/hw目錄存放的是硬體抽象層(HAL)介面文件,後面的文章裡面,我們會陸續提及到,敬請關注。
三. 編譯好模塊後,還要重新打包一下system.img文件,這樣我們把system.img運行在模擬器上時,就可以看到我們的程序了。
USER-NAME@MACHINE-NAME:~/Android$ make snod
四. 參照Ubuntu上下載、編譯和安裝Android最新源代碼一文介紹的方法運行模擬器:
USER-NAME@MACHINE-NAME:~/Android$ emulator
這樣一切就搞定了。
『捌』 andriod 源碼編譯時,如何添加一個可執行文件到文件系統中
是這樣的
1.進入你的android系統源碼的build/target/board/generic目錄下
2.把你的abc拷貝到 這個目錄下
3.編輯這個目錄下的vi AndroidBoard.mk這個文件
4.添加 PRODUCT_COPY_FILES += $(LOCAL_PATH)/abc:system/bin/abc
這樣在make源碼的時候,就可以自動進行這次拷貝了