在之前的文章中,介紹瞭如何在Framework中添加接口。當時添加的接口是編譯到ROM原有的jar包中,如果framework中定製需求較多,可以將Framework原有代碼與需求實現代碼分開,將定製需求實現代碼編譯到一個jar中。這樣Framework就變得較爲整潔,容易維護。
本文以Amlogic905代碼爲例,通過一個demo,來簡單介紹下怎麼編出一個定製的jar。該demo需求如下:
1>編出的jar名稱爲ysten.jar。
2>通過判斷不同的屬性(persist.sys.yst.province)來獲取不同的Intent。
一、添加編譯項
這個章節介紹下定製jar的編譯配置。既然就要配置編譯項,就需要了解下Android系統的編譯過程。Android的編譯過程是個較大的課題,本章節只是簡單介紹。
1.1 Android編譯過程
1>source build/envsetup.sh
該步驟調用了build/envsetup.sh腳本,其主要作用是初始化編譯環境,加載了編譯時使用到的函數(help、lunch,m,mm,mmm等)命令。
2>lunch p201_iptv-eng
該步驟的作用是調用lunch函數,用來指定此次編譯的目標設備選項以及編譯類型。目標設備選項可以用芯片原生的,也可以由廠商自己添加,如p201_iptv就是芯片原有的編譯項。至於編譯類型,可以這樣簡單理解:
編譯類型 | 意義 |
---|---|
eng | debug版本 |
user | release版本 |
userdebug | 部分debug版本 |
3>make otapackage -j32 2>&1
該步驟纔開始進行真正的編譯,make 的參數“-j”指定了同時編譯的Job數量,這是個整數,該值通常是編譯服務器CPU支持的併發線程總數的1倍或2倍。
執行make命令的結果就是去執行當前目錄下的Makefile文件,編譯命令是在系統源碼根目錄執行的,所以首先執行到的就是根目錄的Makefile文件,然後一路追下去就是不斷調用其他.mk(也包括Android.mk這個最常用的.mk文件)的過程。
以Makefile和Android.mk爲例,這兩個文件可以簡單理解爲編譯過程中最大的和最小的模塊。Makefile文件控制整個Android系統源碼的編譯規則:如指定需要生成哪些目標文件、指定生成這些目標文件依賴哪些源文件、職工生成的目標文件放在哪個文件夾下等等。make就是一個命令工具,可以解析Makefile文件中的指令的一個命令工具。Android.mk也是一樣的功能,只不過它是Android編譯環境下的一種特殊的Makefile文件,格式非常簡單,且與普通的Makefile文件書寫格式不一樣。
1.2 要添加的編譯選項
由1.1章節可知,要添加編譯項其實就是要在一堆的mk文件中添加編譯,至於爲什麼要在如下.mk文件中添加,主要參考的是android.policy.jar的編譯項。具體如下:
1.2.1> build/target/product/base.mk
--- a/build/target/product/base.mk
+++ b/build/target/product/base.mk
@@ -20,6 +20,7 @@ PRODUCT_PACKAGES += \
95-configured \
am \
android.policy \
+ ysten \
android.test.runner \
1.2.2> build/target/product/core_base.mk
--- a/build/target/product/core_base.mk
+++ b/build/target/product/core_base.mk
@@ -68,4 +68,4 @@ PRODUCT_PACKAGES += \
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
# Override the PRODUCT_BOOT_JARS set in core_minimal.mk
-PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:telephony-common:voip-common:mms-common:android.policy:services:apache-xml:webviewchromium
+PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:telephony-common:voip-common:mms-common:android.policy:ysten:services:apache-xml:webviewchromium
1.2.3> build/target/product/core_minimal.mk
--- a/build/target/product/core_minimal.mk
+++ b/build/target/product/core_minimal.mk
@@ -55,7 +55,7 @@ PRODUCT_PACKAGES += \
sensorservice \
uiautomator
-PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:android.policy:services:apache-xml:webviewchromium
+PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:android.policy:ysten:services:apache-xml:webviewchromium
1.2.4> build/tools/releasetools/default_filesystem_config.txt
diff --git a/build/tools/releasetools/default_filesystem_config.txt b/build/tools/releasetools/default_filesystem_config.txt
index 476d457..73c23f1 100755
--- a/build/tools/releasetools/default_filesystem_config.txt
+++ b/build/tools/releasetools/default_filesystem_config.txt
@@ -452,6 +452,7 @@ system/framework/ime.jar 0 0 644
system/framework/com.google.widevine.software.drm.jar 0 0 644
system/framework/input.jar 0 0 644
system/framework/android.policy.jar 0 0 644
+system/framework/ysten.jar 0 0 644
system/framework/javax.obex.jar 0 0 644
system/framework/android.test.runner.jar 0 0 644
system/framework/svc.jar 0 0 644
1.2.5> dalvik/docs/hello-world.html
--- a/dalvik/docs/hello-world.html
+++ b/dalvik/docs/hello-world.html
@@ -168,7 +168,7 @@ export ANDROID_ROOT=$root
# configure bootclasspath
bootpath=$root/framework
-export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.policy.jar:$bootpath/services.jar
+export BOOTCLASSPATH=$bootpath/core.jar:$bootpath/ext.jar:$bootpath/framework.jar:$bootpath/android.policy.jar:$bootpath/ysten.jar:$bootpath/services.jar
1.2.6> device/amlogic/common/base.mk
--- a/device/amlogic/common/base.mk
+++ b/device/amlogic/common/base.mk
@@ -20,6 +20,7 @@ PRODUCT_PACKAGES += \
95-configured \
am \
android.policy \
+ ysten \
android.test.runner \
app_process \
applypatch \
@@ -114,4 +115,4 @@ $(call inherit-product, $(SRC_TARGET_DIR)/product/embedded.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_minimal.mk)
# Override the PRODUCT_BOOT_JARS set in core_minimal.mk
-PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:telephony-common:voip-common:mms-common:android.policy:services:apache-xml:webviewchromium
+PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:telephony-common:voip-common:mms-common:android.policy:ysten:services:apache-xml:webviewchromium
1.2.7> device/amlogic/common/tv_amlogic.mk
--- a/device/amlogic/common/tv_amlogic.mk
+++ b/device/amlogic/common/tv_amlogic.mk
@@ -257,4 +257,4 @@ PRODUCT_MANUFACTURER := TV
PRODUCT_CHARACTERISTICS := TV
# Override the PRODUCT_BOOT_JARS set in core_minimal.mk
-PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:telephony-common:voip-common:mms-common:android.policy:services:apache-xml:webviewchromium:tv
+PRODUCT_BOOT_JARS := core:conscrypt:okhttp:core-junit:bouncycastle:ext:framework:framework2:telephony-common:voip-common:mms-common:android.policy:ysten:services:apache-xml:webviewchromium:tv
1.2.8> external/smali/baksmali/src/main/java/org/jf/baksmali/main.java
--- a/external/smali/baksmali/src/main/java/org/jf/baksmali/main.java
+++ b/external/smali/baksmali/src/main/java/org/jf/baksmali/main.java
@@ -295,7 +295,7 @@ public class main {
deodex = false;
if (bootClassPath == null) {
- bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar";
+ bootClassPath = "core.jar:ext.jar:framework.jar:android.policy.jar:ysten.jar:services.jar";
}
}
@@ -416,7 +416,7 @@ public class main {
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
.withDescription("the bootclasspath jars to use, for analysis. Defaults to " +
- "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar. If the value begins with a " +
+ "core.jar:ext.jar:framework.jar:android.policy.jar:ysten.jar:services.jar. If the value begins with a " +
":, it will be appended to the default bootclasspath instead of replacing it")
.hasOptionalArg()
.withArgName("BOOTCLASSPATH")
1.2.9> frameworks/base/CleanSpec.mk
--- a/frameworks/base/CleanSpec.mk
+++ b/frameworks/base/CleanSpec.mk
@@ -53,6 +53,8 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/FrameworkTest_intermediates/)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android.policy*)
$(call add-clean-step, rm -rf $(TARGET_OUT_JAVA_LIBRARIES)/android.policy.jar)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/ysten*)
+$(call add-clean-step, rm -rf $(TARGET_OUT_JAVA_LIBRARIES)/ysten.jar)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
$(call add-clean-step, rm -f $(PRODUCT_OUT)/obj/lib/libequalizer.so)
$(call add-clean-step, rm -f $(PRODUCT_OUT)/obj/lib/libequalizertest.so)
1.2.10> frameworks/base/ysten/Android.mk
這個文件是新加的文件,內容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_MODULE := ysten
include $(BUILD_JAVA_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
該文件中主要的內容有兩塊:
1)LOCAL_SRC_FILES,該變量定義的是所需要的源碼目錄。
2)LOCAL_MODULE,該變量定義的是jar名稱。
二、具體實現代碼
該部分代碼的業務邏輯比較容易理解,就是一個簡單工廠模式的使用demo。至於目錄結構的設計,參考的是android.policy.jar對應的源碼結構。
ysten.jar對應的源碼總路徑是frameworks/base/ysten/src/com/ysten/am。這樣編譯出來的jar,import的路徑就是src文件夾下的具體目錄,即com.ysten.am。該目錄下有三個文件,具體如下:
2.1 抽象基類
本demo中對應的是frameworks/base/ysten/src/com/ysten/am/BaseActivity.java,該文件是一個抽象基類,定義一個抽象接口,內容如下:
package com.ysten.am;
import android.content.Intent;
public abstract class BaseActivity {
public abstract Intent getYstenHomeIntent();
}
2.2 具體實現類
本demo中對應的是frameworks/base/ysten/src/com/ysten/am/DemoActivityManager.java,該文件是具體實現類,實現BaseActivity.java中定義的抽象接口,內容如下:
package com.ysten.am;
import android.content.Intent;
import android.content.ComponentName;
public class DemoActivityManager extends BaseActivity {
private Intent intent = null;
public DemoActivityManager(){
intent = new Intent();
}
public Intent getYstenHomeIntent(){
ComponentName componentName = new ComponentName("com.yst.whitebox","com.yst.whitebox.MainActivity");
intent.setComponent(componentName);
return intent;
}
}
2.3 工廠類
本demo中對應的是frameworks/base/ysten/src/com/ysten/am/ActivityFactory.java,該文件是一個子類生產的工廠,生產出什麼類型的子類取決於調用的類中傳下來的persist.sys.yst.province參數(本demo重點不在該模式的使用,所以只會產生一個子類),內如如下:
package com.ysten.am;
import android.util.Log;
public class ActivityFactory {
private static final int PROVINCE_DEMO = 571;
private BaseActivity baseActivity = null;
private String TAG = "ActivityFactory";
public BaseActivity getActivity(String tmpProvince){
int province = Integer.parseInt(tmpProvince);
Log.d(TAG,"the province is: "+province);
switch(province){
case PROVINCE_DEMO:
baseActivity = new DemoActivityManager();
}
return baseActivity;
}
}
三、調用代碼
本章節主要介紹一下怎麼調用ysten.jar中的接口。主要分爲兩部分,具體如下:
3.1 聲明jar
本demo中聲明使用該jar的地方是frameworks/base/services/java/Android.mk,內容如下:
--- a/frameworks/base/services/java/Android.mk
+++ b/frameworks/base/services/java/Android.mk
@@ -11,7 +11,7 @@ LOCAL_SRC_FILES := \
LOCAL_MODULE:= services
-LOCAL_JAVA_LIBRARIES := android.policy conscrypt telephony-common
+LOCAL_JAVA_LIBRARIES := android.policy conscrypt telephony-common ysten
3.2 使用jar
本demo中使用的地方是frameworks/base/services/java/com/android/server/am/ActivityManagerService.java,即在該文件中調用ysten.jar中的接口,具體方式同調用其他系統原生jar中的方式一下,內容如下:
import com.ysten.am.*;
private ActivityFactory activityFactory = new ActivityFactory();
String tmpProvince = (SystemProperties.get("persist.sys.yst.province")).substring(1);
BaseActivity baseActivity = activityFactory.getActivity(tmpProvince);
if(baseActivity.getYstenHomeIntent() != null){
intent = baseActivity.getYstenHomeIntent();
}
至此,添加定製jar的方式已簡單介紹完畢。