在之前的文章中,介绍了如何在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>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.1章节可知,要添加编译项其实就是要在一堆的mk文件中添加编译,至于为什么要在如下.mk文件中添加,主要参考的是android.policy.jar的编译项。具体如下:
1.2.1> build/target/product/base.mk
该文件中添加的编译型较多,不仅有framework层代码编出来的jar包,还有一些要编译出来的bin文件,如dhcpcd等,可简单理解为该文件中的编译型是Android系统所需要的基本编译项。
--- 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
该文件中定义了PRODUCT_BOOT_JARS变量,该变量的值最终被编译到system/framework,并被添加到BOOTCLASSPATH路径。
--- 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
该文件中也是定义了PRODUCT_BOOT_JARS变量,作用不再赘述。
--- 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
该文件的作用是指定system目录及其子目录下文件的权限配置信息, 如system/bin、system/fonts等。
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
在上面的一些脚本中,在PRODUCT_BOOT_JARS变量里添加了ysten.jar。在该文件中,导入PRODUCT_BOOT_JARS变量。
--- 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
该文件中也是定义了一些Android系统所需要的基本编译项。
--- 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
该文件中也是定义了PRODUCT_BOOT_JARS变量,作用不再赘述。
--- 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
该文件中添加ysten.jar的作用是将ysten.jar添加到bootclasspath
--- 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。该目录下有三个文件,具体如下:
本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();
}
本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;
}
}
本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中的接口。主要分为两部分,具体如下:
本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
本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的方式已简单介绍完毕。