简介
在研究Android系统开机流程的时候,看到preloaded-classes被用来做预加载类的载体,但没明白它是怎么编译到系统中的。因此,特意去研究了下,特此记录下来。
将preloaded-classes编译到framework.jar中
frameworks/base/Android.mk
include $(CLEAR_VARS)
LOCAL_MODULE := framework
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_NO_STANDARD_LIBRARIES := true
LOCAL_STATIC_JAVA_LIBRARIES := framework-base
LOCAL_DX_FLAGS := --core-library
# Packages to include, use \* wildcard to include descendants.
LOCAL_JAR_PACKAGES := android\*
# List of classes and interfaces which should be loaded by the Zygote.
LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
include $(BUILD_JAVA_LIBRARY)
LOCAL_JAVA_RESOURCE_FILES
将文件编译到对应的jar文件中。例如上面的preloaded-classes就会被编译到framework.jar中。如图:
out/target/product/xxx/system/framework/framework.jar
读取framework.jar中preloaded-classes的内容
那我们要怎么调用这个jar包中的文件呢?系统framework中给出了调用方法,代码如下:
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
private static final String PRELOADED_CLASSES = "preloaded-classes";
private static void preloadClasses() {
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream(
PRELOADED_CLASSES);
if (is == null) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
} else {
...
try {
BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
...
}
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
IoUtils.closeQuietly(is);
...
}
}
}
通过上面的代码我们知道,主要的读取的代码是这段
ClassLoader.getSystemClassLoader().getResourceAsStream(PRELOADED_CLASSES);
通过这段代码,我们可以获取到preloaded-classes的输入流,这样我们就可以读取其中的内容了。那问题来了,ClassLoader有什么怎么去读取的呢?这个放到下一篇文章来讲解,这里暂不讲解,只讲它的实现和调用。下面我们来看下怎么添加自己的文件到jar包,并去读取其内容呢,其实通过上面的分析,我们要添加自定义文件到framework.jar中也是非常简单的了。
添加自定义资源文件到framework.jar中
第一步 修改Android.mk
我们要添加自定义资源文件到jar中,我们只需要在mk文件,将LOCAL_JAVA_RESOURCE_FILES
添加上我们的文件名即可,当然路径要一致。
frameworks/base/Android.mk
include $(CLEAR_VARS)
LOCAL_MODULE := framework
...
# List of classes and interfaces which should be loaded by the Zygote.
LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/test.txt
LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/image.jpg
include $(BUILD_JAVA_LIBRARY)
上面我们通过LOCAL_JAVA_RESOURCE_FILES
将test.txt和image.jpg添加到了framework.jar中。我们先看test.txt和image.jpg的内容。
frameworks/base/test.txt
“你知道吗,总有一天,你终将会变成你讨厌的人。”
“谢谢吉言,我讨厌有钱人。”
frameworks/base/image.jpg
第二步 编译framework.jar
在framework/base目录直接执行mm命令即可。
第三步 检查生成的framework.jar是否编译成功
打开out/target/product/xxx/system/framework/framework.jar,结果如图:
第四步 测试结果
先上测试结果,如下:
完美!!!
实现代码
TestActivity.java
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.test_layout);
TextView mTV = (TextView) findViewById(R.id.id_tv);
ImageView mIV = (ImageView) findViewById(R.id.id_iv);
String text = "";
InputStream isText = ClassLoader.getSystemResourceAsStream("test.txt");
if (isText != null) {
BufferedReader br = new BufferedReader(new InputStreamReader(isText));
String line;
try {
while ((line = br.readLine()) != null) {
Log.d("TAG", "line:"+line);
text += line + "\n";
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
isText.close();
isText = null;
} catch (IOException e) {
e.printStackTrace();
}
}
mTV.setText(text);
}
InputStream isImage = ClassLoader.getSystemResourceAsStream("image.jpg");
if (isImage != null) {
Bitmap bitmap = BitmapFactory.decodeStream(isImage);
if (bitmap != null) {
mIV.setImageBitmap(bitmap);
}
try {
isImage.close();
isImage = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
test_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/id_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<ImageView
android:id="@+id/id_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
</LinearLayout>
注:我这里使用的是ClassLoader.getSystemResourceAsStream,其实跟ClassLoader.getSystemClassLoader().getResourceAsStream是一样的,只不过系统已经将封装好了。看下代码你就明白了。
public static InputStream getSystemResourceAsStream(String resName) {
return SystemClassLoader.loader.getResourceAsStream(resName);
}
public static ClassLoader getSystemClassLoader() {
return SystemClassLoader.loader;
}
所以它们调用的是同一个方法。有源码的同学可以实际操作一把,理解就深刻了。争取下一篇文章写一下ClassLoader getSystemResourceAsStream的调用流程。
总结
Read The Fucking Source Code