4.3.2 预加载Class资源和Resource资源

注册Socket后,便进入第二步:预加载Class资源和Resource资源。这部分功能在preload方法中完成,该方法封装了对preloadClasses()和preloadResources()的调用。下面分别分析这两个方法。

1.preloadClasses方法

preloadClasses()方法用于加载preloaded-classes文件中指定的Java类,其代码如下:


private static void preloadClasses(){

final VMRuntime runtime=VMRuntime.getRuntime();

/PRELOADED_CLASSES是一个常量,定义为preloaded-classes/

InputStream is=ZygoteInit.class.getClassLoader()

.getResourceAsStream(PRELOADED_CLASSES);

if(is==null){//错误处理

}else{

……//省略部分内容

/为了安全考虑,加载前通过系统函数setregid降低权限/

setEffectiveGroup(UNPRIVILEGED_GID);

setEffectiveUser(UNPRIVILEGED_UID);

……//省略部分内容

try{

BufferedReader br

=new BufferedReader(new InputStreamReader(is),256);

int count=0;

String line;

while((line=br.readLine())!=null){

//过滤注释和空行

line=line.trim();

if(line.startsWith("#")||line.equals("")){

continue;

}

try{

/line便是要预加载的类名,这里通过Java反射机制加载/

Class.forName(line);

if(Debug.getGlobalAllocSize()>PRELOAD_GC_THRESHOLD){

……//省略部分内容

System.gc();

runtime.runFinalizationSync();

Debug.resetGlobalAllocSize();

}

count+;

}catch(ClassNotFoundException e){

}catch(Throwable t){

}

}

}catch(IOException e){

}finally{

……//省略部分内容

setEffectiveUser(ROOT_UID);//还原权限

setEffectiveGroup(ROOT_GID);

}

}

}


preloadClasses()做了两部分工作:

1)从preloaded-classes文件中加载类名。

2)利用Java反射机制加载这些类。

那么preloaded-classes文件中到底是什么内容呢?先来看preloaded-classes文件,内容如下:


Classes which are preloaded by com.android.internal.os.ZygoteInit.

Automatically generated by

frameworks/base/tools/preload/WritePreloadedClassFile.java.

MIN_LOAD_TIME_MICROS=1250

MIN_PROCESSES=10

android.R$styleable

android.accounts.Account

android.accounts.Account$1

android.accounts.AccountManager

……//一共2307行


该文件是由frameworks/base/tools/preload/WritePreloadedClassFile.java类生成的(该类很简单,读者可自行分析)。frameworks/base/tools/preload包下的文件最终会被编译为preload工具模块,系统利用这个工具做了两部分判断工作:

1)判断某个类的加载时间是否超过MIN_LOAD_TIME_MICROS(默认值为1250)指定的微秒数。

2)判断某个类是否至少被MIN_PROCESSES(默认值为10)个进程加载。

注意 preloaded-classes文件超过2000行,读取每一行并加载每一行所指定的类需要花费较长时间,这也是影响系统启动速度的原因之一。不过,由于这些类已经预先加载到内存中,当新的应用程序启动时,可以共享这部分资源,这样便加快了应用程序启动和运行的速度。

2.preloadResources方法

preloadResources方法用于加载框架层定义的资源文件,其代码如下:


private static void preloadResources(){

final VMRuntime runtime=VMRuntime.getRuntime();

Debug.startAllocCounting();

try{

System.gc();

runtime.runFinalizationSync();

/初始化全局变量mResources,类型是Resources/

mResources=Resources.getSystem();

/*设置初始状态,防止重复加载资源

*mPreloaded=true;

mPreloading=true;/

mResources.startPreloading();

/PRELOAD_RESOURCES设置在zygote初始化时是否加载Resource,默认为true/

if(PRELOAD_RESOURCES){

TypedArray ar=mResources.obtainTypedArray(

com.android.internal.R.array.preloaded_drawables);

/加载com.android.internal.R.array.preloaded_drawables/

int N=preloadDrawables(runtime, ar);

……//省略部分内容

ar=mResources.obtainTypedArray(

com.android.internal.R.array.preloaded_color_state_lists);

//加载com.android.internal.R.array.preloaded_color_state_lists

N=preloadColorStateLists(runtime, ar);

}

mResources.finishPreloading();

}catch(RuntimeException e){

}

……//省略部分内容

}


preloadResources加载了两种系统资源:drawables和color。这些系统资源定义在frameworks/base/core/res/res/values/arrays.xml文件中,最终会被编译进framework-res.apk。

其中com.and roid.internal.R.array.preloaded_drawables表示arrays.xml中name为preloaded_drawables的资源项,代码如下:


<array name="preloaded_drawables">

……//省略部分内容

<!—每一个item都代表一个系统默认的图片,这些资源是系统共享的。item的值表示

该图片位于frameworks/base/core/res/res/drawable-XXX路径下

—>

<item>@drawable/toast_frame</item>

<item>@drawable/toast_frame_holo</item>

<item>@drawable/btn_check_on_selected</item>

<item>@drawable/btn_check_on_pressed_holo_light</item>

<item>@drawable/btn_check_on_pressed_holo_dark</item>

……//省略部分内容

</array>

frameworks/base/core/res/res/preloaded_color_state_lists与之类似,不再详述。


注意 frameworks/base/core/res/res目录下还有很多其他资源,这里并没有预加载。可见,系统只是预加载了一些需要共享的系统资源,这样当应用程序启动时便可以共享这些通用资源,避免每个应用程序重复加载相同的资源,大大提高了执行效率。