Android App启动时Apk资源加载机制源码分析

在Andorid开发中我们要设置文字或图片显示,都直接通过Api一步调用就完成了,不仅是我们工程下res资源以及系统自带的framwork资源也可以,那这些资源打包成Apk之后是如何被系统加载从而显示出来的呢。

这里我要从Apk安装之后启动流程开始讲起,在桌面应用click事件之后
会通过Binder机制通知ActivityManagerService启动,具体由ActivityManagerNative.getDefault返回ActivityManagerService的远程接口代理对象ActivityManagerProxy,通知ActivityManagerService执行startActivity进入启动流程. 该Service会进行些获取目标内容,检查权限之后,再检查对应进程的ProcessRecord是否存在了.如果ProcessRecord是null, ActivityManagerService会创建新的进程来实例化目标activity从而把App启动了起来,详细的App启动流程可以参考老罗的文章:http://blog.csdn.net/luoshengyang/article/details/6689748。

进程的创建及绑定Application

  • 创建进程
    ActivityManagerService调用startProcessLocked()方法来创建新的进程, 该方法会通过前面讲到的socket通道传递参数给Zygote进程. Zygote孵化自身, 并调用ZygoteInit.main()方法来实例化ActivityThread对象并最终返回新进程的pid。代码如下
 public final class ActivityManagerService extends ActivityManagerNative  
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {  

    ......  

    private final void startProcessLocked(ProcessRecord app,  
                String hostingType, String hostingNameStr) {  

        ......  

        try {  
            int uid = app.info.uid;  
            int[] gids = null;  
            try {  
                gids = mContext.getPackageManager().getPackageGids(  
                    app.info.packageName);  
            } catch (PackageManager.NameNotFoundException e) {  
                ......  
            }  


            int debugFlags = 0;  

            ......  

            int pid = Process.start("android.app.ActivityThread",  
                mSimpleProcessManagement ? app.processName : null, uid, uid,  
                gids, debugFlags, null);  

            ......  

        } catch (RuntimeException e) {  

            ......  

        }  
    }  

    ......  

}  

这里主要是调用Process.start接口来创建一个新的进程,新的进程会导入android.app.ActivityThread类,并且执行它的main函数。

  • 绑定Application
    这个是通过调用上文的ActivityThread对象中调用handleBindApplication方法完成的. 我们可以通过Thread.dumpStack()来查看流程:
 at java.lang.Thread.dumpStack(Thread.java:505)
06-21 17:42:18.448 8620-8620/cn.terminal.egame.myphone W/System.err:     at cn.terminal.egame.myphone.MyApplication.onCreate(MyApplication.java:13)
06-21 17:42:18.448 8620-8620/cn.terminal.egame.myphone W/System.err:     at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1015)
06-21 17:42:18.448 8620-8620/cn.terminal.egame.myphone W/System.err:     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4793)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at android.app.ActivityThread.access$1600(ActivityThread.java:165)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1437)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at android.os.Looper.loop(Looper.java:150)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5621)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
06-21 17:42:18.449 8620-8620/cn.terminal.egame.myphone W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)

那么ActivityThread的handleBindApplication又是谁来调用的(这里就简单介绍下,不能偏离主题太远,深入太远,最后不知所云,没有了方向):

在ActivityThread的main方法中会执行ActivityThread对象的attach方法,会调用用了ActivityManagerService的远程接口本地代理对象ActivityManagerProxy的attachApplication函数通知attachApplication,并传入参数是mAppThread,这是ApplicationThread类型的Binder对象,用来接受ActivityManagerService的进程间消息。

public final class ActivityThread {

 ......

 public static void main(String[] args) {
  .....
  ActivityThread thread = new ActivityThread();
        thread.attach(false);
   .....

 }

 private void attach(boolean system) {
   .......
    final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }

            ....
    }
}

ActivityManagerService在接受到attachApplication函数调用远程消息之后,一系列处理之后,会有两个重要Binder通信,一个就是通过传来的参数Binder参数ApplicationThread来通知ActivityThread中mAppThread远程中调用bindApplication(),另一个是scheduleLaunchActivity。在Ams中收到attachApplication时代码如下:

AMS
 @Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            //获取applicationThread的进程id(也就是淘宝应用进程)
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }


 private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {

        // Find the application record that is being attached...  either via
        // the pid if we are running in multiple processes, or just pull the
        // next app record if we are emulating process with anonymous threads.
        ProcessRecord app;
        if (pid != MY_PID && pid >= 0) {
            synchronized (mPidsSelfLocked) {
                app = mPidsSelfLocked.get(pid);
            }
        } else {
            app = null;
        }

        //因为进程由AMS启动,所以在AMS中一定会有ProcessRecord(进程记录)
        //如果没有ProcessRecord,则需要杀死该进程并退出
        if (app == null) {
            ``````
            return false;
        }

        // If this application record is still attached to a previous
        // process, clean it up now.
        if (app.thread != null) {
            //如果从ProcessRecord中获取的IApplicationThread不为空,则需要处理该IApplicationThread
            //因为有可能此Pid为复用,旧应用进程刚释放,内部IApplicationThread尚未清空,
            //同时新进程又刚好使用了此Pid
            handleAppDiedLocked(app, true, true);
        }


        //创建死亡代理(进程kill后通知AMS)
        AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);

        //进程注册成功,移除超时通知
        mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

        ``````
        try {
            //******绑定Application******
            thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode,
                    mBinderTransactionTrackingEnabled, enableTrackAllocation,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());

            updateLruProcessLocked(app, false, null);
        } catch (Exception e) {

            ``````
            //bindApplication失败后,重启进程
            startProcessLocked(app, "bind fail", processName);
            return false;
        }

        try {
            //******启动Activity(启动应用MainActivity)******
            if (mStackSupervisor.attachApplicationLocked(app)) {
                didSomething = true;//didSomething表示是否有启动四大组件
            }
        } catch (Exception e) {
            badApp = true;
        }

        ``````
        //绑定service和Broadcast的Application


        if (badApp) {
            //如果以上组件启动出错,则需要杀死进程并移除记录
            app.kill("error during init", true);
            handleAppDiedLocked(app, false, true);
            return false;
        }

        //如果以上没有启动任何组件,那么didSomething为false
        if (!didSomething) {
            //调整进程的oom_adj值, oom_adj相当于一种优先级
            //如果应用进程没有运行任何组件,那么当内存出现不足时,该进程是最先被系统“杀死”
            updateOomAdjLocked();
        }
        return true;
    }

从上午可以看到在attachApplicationLocked中有两个比较重要的方法函数:


thread.bindApplication(…) : 绑定Application到ActivityThread
mStackSupervisor.attachApplicationLocked(app) : 启动Activity(7.0前为mMainStack.realStartActivityLocked())

bindApplication

通过AIDL接口IApplicationThread远程通知到ApplicationThreadNative的onTransact方法指定执行BIND_APPLICATION_TRANSACTION方法,而ActivityThread的内部类ApplicationThread实现ApplicationThreadNative抽象类bindApplication(),由于bindApplication()是运行在服务端Binder的线程池中,所以bindApplication会通过Handler发送BIND_APPLICATION的Message消息,ActivityThread中handler接受到之后调用handleBindApplication。

public final class ActivityThread {


private class ApplicationThread extends ApplicationThreadNative {
       .....
          public final void bindApplication(String processName, ApplicationInfo appInfo,
                List providers, ComponentName instrumentationName,
                ProfilerInfo profilerInfo, Bundle instrumentationArgs,
                IInstrumentationWatcher instrumentationWatcher,
                IUiAutomationConnection instrumentationUiConnection, int debugMode,
                boolean enableOpenGlTrace, boolean trackAllocation, boolean isRestrictedBackupMode,
                boolean persistent, Configuration config, CompatibilityInfo compatInfo,
                Map services, Bundle coreSettings) {

           .........

            AppBindData data = new AppBindData();
            ......
            sendMessage(H.BIND_APPLICATION, data);
        }
    .....
    }

 private class H extends Handler {
      .....
         public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                case LAUNCH_ACTIVITY: {
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

                    r.packageInfo = getPackageInfoNoCheck(
                            r.activityInfo.applicationInfo, r.compatInfo);
                    handleLaunchActivity(r, null);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                } break;
                ....
               case BIND_APPLICATION:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                    AppBindData data = (AppBindData)msg.obj;
                    handleBindApplication(data);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;

                    .....

            }

            ....
        }
   }

初始化ContextImpl加载Apk资源

在handleBindApplication的具体实现中就可以看到资源加载:

private void handleBindApplication(AppBindData data) {
      //..........
      // Context初始化(ContextImpl)
      final ContextImpl appContext = ContextImpl.createAppContext(this/*ActivityThread*/, data.info/*LoadedApk*/);
      //........
  }

最终会调用到ContextImpl这个构造函数:

 private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration, int createDisplayWithId) {
            //.......
            // LoadedApk赋值 
            mPackageInfo = packageInfo;
            mResourcesManager = ResourcesManager.getInstance();
            // resources初始化:通过LoadedApk.getResources来创建一个Resources实例
            Resources resources = packageInfo.getResources(mainThread);
            if (resources != null) {
            if (displayId != Display.DEFAULT_DISPLAY
                    || overrideConfiguration != null
                    || (compatInfo != null && compatInfo.applicationScale
                            != resources.getCompatibilityInfo().applicationScale)) {
                resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
                        packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
                        packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
                        overrideConfiguration, compatInfo);
            }
        }
            mResources = resources;// 赋值
            //......
            mContentResolver = new ApplicationContentResolver(this, mainThread, user);
    }

其中 packageInfo.getResources(mainThread)是指 LoadedApk.getResources():

 public Resources getResources(ActivityThread mainThread) {
        if (mResources == null) {
            // ActivityThread.getTopLevelResources()
            mResources = mainThread.getTopLevelResources(mResDir/*APK文件位置*/, mSplitResDirs, mOverlayDirs,
                    mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
        }
        return mResources;
    }

即而又调用到ActivityThread.getTopLevelResources():

 /**
     * Creates the top level resources for the given package.
     */
    Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
            String[] libDirs, int displayId, Configuration overrideConfiguration,
            LoadedApk pkgInfo) {
        return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
                displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo());
    }

mResourcesManager是ResourcesManager的实例,最后资源加载是交给ResourcesManager来完成。
ResourcesManager.getTopLevelResources:

Resources getTopLevelResources(String resDir, String[] splitResDirs,
            String[] overlayDirs, String[] libDirs, int displayId,
            Configuration overrideConfiguration, CompatibilityInfo compatInfo) {
        final float scale = compatInfo.applicationScale;
        Configuration overrideConfigCopy = (overrideConfiguration != null)
                ? new Configuration(overrideConfiguration) : null;
        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfigCopy, scale);
        Resources r;
        synchronized (this) {
            // Resources is app scale dependent.
            if (DEBUG) Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
            // Resources是以ResourcesKey为key以弱应用的方式保存在mActiveResources这个Map中
            WeakReference wr = mActiveResources.get(key);
            r = wr != null ? wr.get() : null;
            //if (r != null) Log.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
            if (r != null && r.getAssets().isUpToDate()) {/
                // 缓存里面有,并且是最新的
                if (DEBUG) Slog.w(TAG, "Returning cached resources " + r + " " + resDir
                        + ": appScale=" + r.getCompatibilityInfo().applicationScale
                        + " key=" + key + " overrideConfig=" + overrideConfiguration);
                return r;
            }
        }

        //if (r != null) {
        //    Log.w(TAG, "Throwing away out-of-date resources!!!! "
        //            + r + " " + resDir);
        //}
        // AssetManager创建
        AssetManager assets = new AssetManager();
        // resDir can be null if the 'android' package is creating a new Resources object.
        // This is fine, since each AssetManager automatically loads the 'android' package
        // already.

        //加载apk资源
        if (resDir != null) {
            if (assets.addAssetPath(resDir) == 0) {
                return null;
            }
        }

       ......

        if (libDirs != null) {
            for (String libDir : libDirs) {
                if (libDir.endsWith(".apk")) {
                    // Avoid opening files we know do not have resources,
                    // like code-only .jar files.
                    if (assets.addAssetPath(libDir) == 0) {
                        Log.w(TAG, "Asset path '" + libDir +
                                "' does not exist or contains no resources.");
                    }
                }
            }
        }

        //Log.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
        DisplayMetrics dm = getDisplayMetricsLocked(displayId);
        Configuration config;

        ......
          //config初始化赋值
        .....

        // 创建Resources
        r = new Resources(assets, dm, config, compatInfo);

           //缓存Resources
        synchronized (this) {
            // 可能其他线程已经创建好了,则直接返回
            WeakReference wr = mActiveResources.get(key);
            Resources existing = wr != null ? wr.get() : null;
            if (existing != null && existing.getAssets().isUpToDate()) {
                // Someone else already created the resources while we were
                // unlocked; go ahead and use theirs.
                r.getAssets().close();
                return existing;
            }

            // XXX need to remove entries when weak references go away
            // 把最新的对象保存到缓存中
            mActiveResources.put(key, new WeakReference<>(r));
            if (DEBUG) Slog.v(TAG, "mActiveResources.size()=" + mActiveResources.size());
            return r;
        }
    }

可以看到ResourcesManager先从缓存找已经加载好的资源Resource,如果没有就重新加载,通过初始化AssetManager和Resources来完成,并缓存。
其中关键函数就是AssetManager的addAssetPath(resDir)来完成加载并交给Resource来暴露接口。

先看下AssetManager初始化其,构造函数:

/**
     * Create a new AssetManager containing only the basic system assets.
     * Applications will not generally use this method, instead retrieving the
     * appropriate asset manager with {@link Resources#getAssets}.    Not for
     * use by applications.
     * {@hide}
     */
    public AssetManager() {
        synchronized (this) {
            //......
            init(false);
            // 确保有能够访问系统资源的AssetManager对象
            ensureSystemAssets();
        }
    }

init是个native方法,实现如下:
android_util_AssetManager.android_content_AssetManager_init()

static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem)
{
    if (isSystem) {// false
        verifySystemIdmaps();
    }
    AssetManager* am = new AssetManager();
    if (am == NULL) {
        jniThrowException(env, "java/lang/OutOfMemoryError", "");
        return;
    }

    am->addDefaultAssets();

    ALOGV("Created AssetManager %p for Java object %p\n", am, clazz);
    env->SetLongField(clazz, gAssetManagerOffsets.mObject, reinterpret_cast(am));
}

AssetManager.cpp:addDefaultAssets()是添加系统默认资源路径:/system/framework/framework-res.apk

bool AssetManager::addDefaultAssets()
{
    // root = /system/
    const char* root = getenv("ANDROID_ROOT");
    LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set");

    String8 path(root);
    // path = /system/framework/framework-res.apk
    path.appendPath(kSystemAssets);

    return addAssetPath(path, NULL);
}

再通过addAssetPath添加资源路径到mAssetPaths并加载openNonAssetInPathLocked:

bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
{
    AutoMutex _l(mLock);

    asset_path ap;

    String8 realPath(path);
    if (kAppZipName) {
        // 如果kAppZipName不为NULL(classes.jar),这里这个值是为NULL的
        realPath.appendPath(kAppZipName);
    }
    ap.type = ::getFileType(realPath.string());
    if (ap.type == kFileTypeRegular) {// kAppZipName不为NULL
        ap.path = realPath;
    } else {
    // kAppZipName为NULL
        ap.path = path;//ap.path指向APK文件
        ap.type = ::getFileType(path.string());
        if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) {
            ALOGW("Asset path %s is neither a directory nor file (type=%d).",
                 path.string(), (int)ap.type);
            return false;
        }
    }

    // Skip if we have it already.
    for (size_t i=0; iif (mAssetPaths[i].path == ap.path) {
            if (cookie) {
                *cookie = static_cast(i+1);
            }
            return true;
        }
    }

    ALOGV("In %p Asset %s path: %s", this,
         ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string());

    // Check that the path has an AndroidManifest.xml
    Asset* manifestAsset = const_cast(this)->openNonAssetInPathLocked(
            kAndroidManifest, Asset::ACCESS_BUFFER, ap);
    if (manifestAsset == NULL) {
        // This asset path does not contain any resources.
        delete manifestAsset;
        return false;
    }
    delete manifestAsset;

    mAssetPaths.add(ap);

    // new paths are always added at the end
    if (cookie) {
        *cookie = static_cast(mAssetPaths.size());
    }

#ifdef __ANDROID__
    // Load overlays, if any
    asset_path oap;
    for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) {
        mAssetPaths.add(oap);
    }
#endif

   ......

    return true;
}

初始化完成AssetManager之后就是初始Resource,其作用就是缓存mAssets,并暴露接口对外加载资源,实际都是通过AssetManager来完成的:

public class Resources {
.....
   public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config,
            CompatibilityInfo compatInfo) {
        mAssets = assets;
        mMetrics.setToDefaults();
        if (compatInfo != null) {
            mCompatibilityInfo = compatInfo;
        }
        // 设备相关配置信息更新处理
        updateConfiguration(config, metrics);
        // 创建字符串资源池
        assets.ensureStringBlocks();
    }

    //实际加载都是转给mAssets即AssetManager
    public CharSequence getText(@StringRes int id) throws NotFoundException {
        CharSequence res = mAssets.getResourceText(id);
        if (res != null) {
            return res;
        }
        throw new NotFoundException("String resource ID #0x"
                                    + Integer.toHexString(id));
    }
    //加载图片
     private Drawable loadDrawableForCookie(TypedValue value, int id, Theme theme) {

        final Drawable dr;

        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
        try {
            if (file.endsWith(".xml")) {
                final XmlResourceParser rp = loadXmlResourceParser(
                        file, id, value.assetCookie, "drawable");
                dr = Drawable.createFromXml(this, rp, theme);
                rp.close();
            } else {
                final InputStream is = mAssets.openNonAsset(
                        value.assetCookie, file, AssetManager.ACCESS_STREAMING);
                dr = Drawable.createFromResourceStream(this, value, is, file, null);
                is.close();
            }
        } catch (Exception e) {
            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
            final NotFoundException rnf = new NotFoundException(
                    "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
            rnf.initCause(e);
            throw rnf;
        }
        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

        return dr;
    }
    ......
}

从上那么多可以看到开发中不论是设啥资源都是如此AssetManager完成的,到这里就讲完了整个Apk资源加载。

总结

通过上文就发现资源apk(resDir)通过AssetManager.addAssetPath(resDir)来完成加载,并初始化resource来处理暴露资源加载的流程。那么我们就可以通过自定义资源的加载,使用AssetManager来加载我们的单独资源apk不就可以了么,请看我的另一篇文章:打造自己的框架-实现动态加载两种方式 以及Resource是如何暴露出资源加载的流程,系统如何加载显示res下资源。

参考文章:
http://www.jianshu.com/p/a5532ecc8377
http://blog.csdn.net/luoshengyang/article/details/6689748
http://blog.csdn.net/qian520ao/article/details/78156214

你可能感兴趣的:(Android源码分析学习,Android,UI开发)