Android LOCAL_JAVA_RESOURCE_FILES 的妙用

来源:互联网 发布:倩女幽魂联赛对战算法 编辑:程序博客网 时间:2024/05/16 01:27

简介

在研究Android系统开机流程的时候,看到preloaded-classes被用来做预加载类的载体,但没明白它是怎么编译到系统中的。因此,特意去研究了下,特此记录下来。

将preloaded-classes编译到framework.jar中

frameworks/base/Android.mk

include $(CLEAR_VARS)LOCAL_MODULE := frameworkLOCAL_MODULE_CLASS := JAVA_LIBRARIESLOCAL_NO_STANDARD_LIBRARIES := trueLOCAL_STATIC_JAVA_LIBRARIES := framework-baseLOCAL_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-classesinclude $(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-classesLOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/test.txtLOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/image.jpginclude $(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

原创粉丝点击