一、系统资源预加载 

  android系统资源加载分两种方式,预加载和使用进程中加载。
     预加载是在zygote进程启动的时候被执行,一般系统中多应用共享的资源会被列为预加载资源,预加载的好处在于系统只在zygote执行一次加载操作,所有应用用到该资源不需要再重新加载,减少资源加载耗时。
     有哪些资源会被zygote进程加载? framework中将会被系统多个应用进程共享的资源添加到res/values/array.xml中数组资源中,名为preload_drawable、preload_color_states_list, 如果有这些资源会被多个应用共享,可以将其添加到res/values/array.xml数组中,避免每个应用进程都去加载资源。
     预加载的原理很简单,就是在zygote进程启动后将资源读取出来,保存到Resources一个全局静态变量中,下次读取系统资源的时候优先从静态变量中查找。主要代码在zygoteInit.java类中方法preloadResources(),主要代码如下:

mResources = Resources.getSystem();
            mResources.startPreloading();
            if (PRELOAD_RESOURCES) {
                Log.i(TAG, "Preloading resources...");

                long startTime = SystemClock.uptimeMillis();
                TypedArray ar = mResources.obtainTypedArray(
                        com.android.internal.R.array.preloaded_drawables);
                int N = preloadDrawables(runtime, ar);
                ar.recycle();
                Log.i(TAG, "...preloaded " + N + " resources in "
                        + (SystemClock.uptimeMillis()-startTime) + "ms.");
                    addBootEvent(new String("Zygote:Preload "+ N + " obtain resources in " +
                                        (SystemClock.uptimeMillis() - startTime) + "ms"));

                startTime = SystemClock.uptimeMillis();
                ar = mResources.obtainTypedArray(
                        com.android.internal.R.array.preloaded_color_state_lists);
                N = preloadColorStateLists(runtime, ar);
                ar.recycle();
             }
             mResources.finishPreloading();



分析上面这段代码:

(1)调用Resources.getSystem()获取到Resources对象,该方法是一个androidSDK公开方法,但一般在应用开发中较少用到,因为该方法返回的Resources对象仅能访问framework资源。

(2)调用mResources.startPreloading()和mResources.finishPreloading()分别在开始和结束的时候重置预加载标志位mPreloading,这个标志位在Resources.loadDrawable()方法中将起到关键性作用,区分是否为zygote进程预加载资源。

(3)调用preloadDrawables()和preloadColorStateLists()分别加载res/values/array.xml数组preload_drawable、preload_color_states_list中定义的资源。首先看下preloadDrawables方法,该方法有两个参数,第一个TypeArray类型参数ar,主要装载的是res/values/array.xml数组preload_drawable

<!-- Do not translate. These are all of the drawable resources that should be preloaded by
         the zygote process before it starts forking application processes. -->
    <array name="preloaded_drawables">
       <item>@drawable/toast_frame</item>
       <item>@drawable/btn_check_on_pressed_holo_light</item>
       <item>@drawable/btn_check_on_pressed_holo_dark</item>



preloadDrawables()方法中处理的事情很简单,就是调用Resources.loadDrawable()方法逐一读取preload_drawable数组中的资源,在Resouces类中loadDrawable函数中会判断变量mPreloading,为true表示当前为zygote进程加载资源,loadDrawable函数关键代码如下:


if (mPreloading) {
                    final int changingConfigs = cs.getChangingConfigurations();
                    if (isColorDrawable) {
                        if (verifyPreloadConfig(changingConfigs, 0, value.resourceId,
                                "drawable")) {
                            sPreloadedColorDrawables.put(key, cs);
                        }
                    } else {
                        if (verifyPreloadConfig(changingConfigs,
                                LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
                            if ((changingConfigs&LAYOUT_DIR_CONFIG) == 0) {
                                // If this resource does not vary based on layout direction,
                                // we can put it in all of the preload maps.
                                sPreloadedDrawables[0].put(key, cs);
                                sPreloadedDrawables[1].put(key, cs);
                            } else {
                                // Otherwise, only in the layout dir we loaded it for.
                                final LongSparseArray<Drawable.ConstantState> preloads
                                        = sPreloadedDrawables[mConfiguration.getLayoutDirection()];
                                preloads.put(key, cs);
                            }
                        }
                    }
               }



这个方法中值得注意的是,除了判断为zygote进程预加载资源,还要验证drawble的changingConfigs信息,这个信息是在编译的时候生成,但目前还没找到哪些配置会影响到changingConfigs,奇怪的一点是,用Samsung 的代码编译原生preload资源,基本上都符合changingConfigs信息验证,而用MTk代码却只有少部分能通过验证,这个问题后续还要继续排查。能够通过changingConfigs验证的资源将会被添加到静态变量sPreloadedDrawables。这样就资源就保存在这个静态变量中,不同进程用到共享资源就不需要再各自去加载。直接从sPreloadedDrawables中取出来。

二、资源缓存
     上面列举的是不同进程共享资源如何缓存共用,其实,在同一个进程中同一个资源也会被缓存到一个变量中,只要该Resources对象不被销毁并且缓存链表没有因为内存不足被回收,资源就会一直被缓存,下次再读取资源时优先从Resources对象缓存变量中获取对应的资源,取不到的情况下,再去查找保存预加载资源的静态变量sPreloadedDrawables,都找不到的情况下,才会去重新加载资源。同样,加载到资源之后会将资源添加到当前对象缓存链表中,以便下次复用。