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