详细理解context(context访问资源的唯一性分析)

最近买了一本android内核剖析,讲的很好,也是正在看,看到理解context这章,写一个博客复习复习。

我也是边看书边看网上讲解学的。


网上讲解的很好,这里分享几个很好的链接 http://blog.csdn.net/lmj623565791/article/details/40481055

http://www.2cto.com/kf/201505/403099.html


每个activity,service使用的context调用的方法,都是在contextImpl这个类中,继承结构

这里写图片描述

一个app的context数量 = activity数量 + service数量 + 1

这个1是application。

我们现在从源码里查看为什么activity和service调用context类的方法,这些方法的实现都是来自contextImpl这个类呢?


我们先看activity的源码(我使用的是21版本的)。

activity——》contextWrapper ——》context。context类的本身是一个纯abstract类,而contextWrapper是一个包装的类,同时它提供了attachBaseContext()方法用于指定真正的context对象。
我们现在看activity对应的context如何创建的。
每一个app都有从activityThread类开始的。启动activity时,ams会通过ipc调用到activityThread的scheduleLaunchActivity方法。在方法中会创建一个ActivityClientRecord一个对象,这是一个本地数据类,里面定义了一个packageinfo。然后会发送一个消息sendMessage(H.LAUNCH_ACTIVITY, r),将ActivityClientRecord实例传递过去。然后在handleMessage方法中找到对应H.LAUNCH_ACTIVITY 的执行


public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
                IVoiceInteractor voiceInteractor, int procState, Bundle state,
                PersistableBundle persistentState, List<ResultInfo> pendingResults,
                List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }


 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;.......}   
 
getPackageInfo() synchronized (mResourcesManager) {
            WeakReference<LoadedApk> ref;
            if (includeCode) {
                ref = mPackages.get(aInfo.packageName);
            } else {
                ref = mResourcePackages.get(aInfo.packageName);
            }
            LoadedApk packageInfo = ref != null ? ref.get() : null;
            if (packageInfo == null || (packageInfo.mResources != null
                    && !packageInfo.mResources.getAssets().isUpToDate())) {
                if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                        : "Loading resource-only package ") + aInfo.packageName
                        + " (in " + (mBoundApplication != null
                                ? mBoundApplication.processName : null)
                        + ")");
                packageInfo =
                    new LoadedApk(this, aInfo, compatInfo, baseLoader,
                            securityViolation, includeCode &&
                            (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
                if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                }
            }
            return packageInfo;
        }
    }


我们可以发现先是从mPackages这个全局变量里以包名为key获取packageInfo,如果没有就创建,然后以包名为key,存放在mPackages里面。mpackages:

final ArrayMap<String, WeakReference<LoadedApk>> mPackages
        = new ArrayMap<String, WeakReference<LoadedApk>>();
 
 
然后返回到这个方法中 
handleLaunchActivity 

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        if (localLOGV) Slog.v(
            TAG, "Handling launch of " + r);

        Activity a = performLaunchActivity(r, customIntent);
........
}


再次进入到 performLaunchActivity方法中
 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        
.........      if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }

        .......

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
........

            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);  Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);<span style="font-family: Arial, Helvetica, sans-serif;"> activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.voiceInteractor);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                ........
        return activity;
    }
 
 
 
 


我们再去看看activity的attach方法

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, IVoiceInteractor voiceInteractor) {
// 这个方法最后会把context对象传递给contextwrapper类的mBase,而这个mbase就是contextImpl对象,所以我们activity使用context的方法,这些方法其实都是contextimpl实现的。
        attachBaseContext(context);

        mFragments.attachActivity(this, mContainer, null);

        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
            mWindow.setSoftInputMode(info.softInputMode);
        }
        if (info.uiOptions != 0) {
            mWindow.setUiOptions(info.uiOptions);
        }
        .......
    }


然后service对应的context如何创建我就不说了,和activity创建方式差不多。从 scheduleCreateService这个方法开始的,有兴趣的可以去看看。


然后我们看为什么context的访问资源的唯一性。

前面说了使用的context都是contextimpl里面实现的方法,我们去看看contextimpl的源码。我们看getResources方法。


 @Override
    public Resources getResources() {
        return mResources;
    }

返回全局变量mResources。


我们找一找mResources在哪里赋值。

 private ContextImpl(ContextImpl container, ActivityThread mainThread,
            LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
            Display display, Configuration overrideConfiguration) {
       .........

        mPackageInfo = packageInfo;
        mResourcesManager = ResourcesManager.getInstance();
        .............

        Resources resources = packageInfo.getResources(mainThread);
        if (resources != null) {
            if (activityToken != null
                    || 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, activityToken);
            }
        }
        mResources = resources;

        ..........
    }


 
可以看到 
Resources resources = packageInfo.getResources(mainThread); 
是这样创建的,而packageinfo是传递进来的,上面我们说的activity对应的context创建,传入的packageInfo全局是唯一的(忘记的可以去看,activitythread类的 
getPackageInfo方法 
)。 

mResourcesManager.getTopLevelResources()mResourcesManager的创建是一个单例模式:Resources resources = packageInfo.getResources(mainThread);


由此可以看出全局使用的资源的唯一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值