在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)123456789101112
那么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())123
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<ProviderInfo> 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<String, IBinder> 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<Resources> 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<Resources> 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<jlong>(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; i<mAssetPaths.size(); i++) { if (mAssetPaths[i].path == ap.path) { if (cookie) { *cookie = static_cast<int32_t>(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<AssetManager*>(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<int32_t>(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下资源。
共同学习,写下你的评论
评论加载中...
作者其他优质文章