What is Context, id card?

Context is used all over Android, so what is Context? What is the point of its existence?

So the conclusion, Context is like a real life proof of identity. The id is issued to everyone to help them identify themselves, record basic information, and do things quickly with the id.

Context in real code is an abstract class that defines some abstract functions, and those abstract functions are the things that we can do with this id card.

Birth certificate

If it is a proof of identity, it means that the moment of birth will be processed. Where is the moment of birth? Look at some code with that in mind.

  • To better understand this concept, I will try to post a few code details, the source path used in this article.
frameworks/base/core/java/android/app/ActivityThread.java
frameworks/base/core/java/android/app/LoadedApk.java
frameworks/base/core/java/android/app/ContextImpl.java
Copy the code

Application

  • Starting a process is a very complex process that would probably take more than the length of this article to get started.

We simply understand that every Android application is an APK, then start an application to load the APK in. LoadedApk is simply the object that APK loads into memory.

Here is the LoadedApk constructor

    public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
            CompatibilityInfo compatInfo, ClassLoader baseLoader,
            boolean securityViolation, boolean includeCode, boolean registerPackage) {
        final intmyUid = Process.myUid(); aInfo = adjustNativeLibraryPaths(aInfo); mActivityThread = activityThread; mApplicationInfo = aInfo; mPackageName = aInfo.packageName; mAppDir = aInfo.sourceDir; mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir; mSplitAppDirs = aInfo.splitSourceDirs; mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs; mOverlayDirs = aInfo.resourceDirs; mSharedLibraries = aInfo.sharedLibraryFiles; mDataDir = aInfo.dataDir; mDataDirFile = mDataDir ! =null ? new File(mDataDir) : null;
        mLibDir = aInfo.nativeLibraryDir;
        mBaseClassLoader = baseLoader;
        mSecurityViolation = securityViolation;
        mIncludeCode = includeCode;
        mRegisterPackage = registerPackage;
        mDisplayAdjustments.setCompatibilityInfo(compatInfo);
    }
Copy the code

And you can see these assignments, a lot of them are things that we normally do through Context. Referenced information.

Create the LoadedApk object below.

 private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
            ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
            boolean registerPackage) {...// This is where it was created.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 (mSystemThread && "android".equals(aInfo.packageName)) {
                    packageInfo.installSystemApplicationInfo(aInfo,
                            getSystemContext().mPackageInfo.getClassLoader());
                }

                if (differentUser) {
                    // Caching not supported across users
                } else if (includeCode) {
                    mPackages.put(aInfo.packageName,
                            new WeakReference<LoadedApk>(packageInfo));
                } else {
                    mResourcePackages.put(aInfo.packageName,
                            newWeakReference<LoadedApk>(packageInfo)); }}returnpackageInfo; }}Copy the code

We can see that the LoadedApk object is returned in the portal code, and we still don’t find the Context spider, so let’s take a closer look at where getPackageInfo() is called

private void handleBindApplication(AppBindData data) {
        mBoundApplication = data;
        mConfiguration = new Configuration(data.config);
        mCompatConfiguration = new Configuration(data.config);

        mProfiler = new Profiler();
        if(data.initProfilerInfo ! =null) { mProfiler.profileFile = data.initProfilerInfo.profileFile; mProfiler.profileFd = data.initProfilerInfo.profileFd; mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval; mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler; }...// ContextImpl is created here
        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
        if(! Process.isIsolated()) {final File cacheDir = appContext.getCacheDir();

            if(cacheDir ! =null) {
                // Provide a usable directory for temporary files
                System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
            } else {
                Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property due to missing cache directory");
            }

            // Use codeCacheDir to store generated/compiled graphics code
            final File codeCacheDir = appContext.getCodeCacheDir();
            if(codeCacheDir ! =null) {
                setupGraphicsSupport(data.info, codeCacheDir);
            } else {
                Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory"); }}...try{...if(! data.restrictedBackupMode) { List<ProviderInfo> providers = data.providers;if(providers ! =null) {
                    // Important, remember this, use later!!
                    installContentProviders(app, providers);
                    // For process that contains content providers, we want to
                    // ensure that the JIT is enabled "at some point".
                    mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000); }}... }Copy the code

So if you look at this code, you see a ContextImpl thing. It looks like it’s related to the Context.

    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    app = mActivityThread.mInstrumentation.newApplication(
                    cl, appClass, appContext);
    appContext.setOuterContext(app);
    
Copy the code

That’s what the relationship is. Let’s talk about the relationship first, and then expand. So if the Context is a proof of identity, ContextImpl is a public processing center for information, the identity that we normally use to do things, where the actual work is actually done, the whole process is a black box for us, but the information from the moment the application was created is in there.

  • Android,ContextImplinheritanceContextThis abstract class, it’s actually implemented in hereContextAn abstract function defined.

Context is an important proof of identity for the four components. At this time, there are not four components. An Application is an Application at best. Continue with the question

Activity

The Activity startup process is enough to write an article may not be able to clarify, here not to expand, directly paste the destination code.

        private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        int displayId = Display.DEFAULT_DISPLAY;
        try {
            displayId = ActivityManagerNative.getDefault().getActivityDisplayId(r.token);
        } catch (RemoteException e) {
        }

        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, displayId, r.overrideConfig);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;

        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
        // For debugging purposes, if the activity's package name contains the value of
        // the "debug.use-second-display" system property as a substring, then show
        // its content on a secondary display if there is one.
        String pkgName = SystemProperties.get("debug.second-display.pkg");
        if(pkgName ! =null && !pkgName.isEmpty()
                && r.packageInfo.mPackageName.contains(pkgName)) {
            for (int id : dm.getDisplayIds()) {
                if(id ! = Display.DEFAULT_DISPLAY) { Display display = dm.getCompatibleDisplay(id, appContext.getDisplayAdjustments(id)); baseContext = appContext.createDisplayContext(display);break; }}}return baseContext;
    }
Copy the code

So we’ve got ContextImpl, but if you look at it, the class is a class, the constructor has changed, Suggesting that handle id card and hu kou book process is different, the above is ContextImpl createAppContext, here ContextImpl. CreateActivityContext

  • Add the second pointContextImplThe constructor is private and cannot be passed externallynewTake the object directly, and it provides three static create functionscreateSystemContext.createAppContext.createActivityContext.
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, displayId, r.overrideConfig);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;
Copy the code

Service

    private void handleCreateService(CreateServiceData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            service = (Service) cl.loadClass(data.info.name).newInstance();
        } catch (Exception e) {
            if(! mInstrumentation.onException(service, e)) {throw new RuntimeException(
                    "Unable to instantiate service " + data.info.name
                    + ":"+ e.toString(), e); }}try {
            if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
            mServices.put(data.token, service);
            try {
                ActivityManagerNative.getDefault().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
            } catch (RemoteException e) {
                // nothing to do.}}catch (Exception e) {
            if(! mInstrumentation.onException(service, e)) {throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ":"+ e.toString(), e); }}}Copy the code
  • Key code,Service uses the sameContextImpl.createAppContext
            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
            context.setOuterContext(service);

            Application app = packageInfo.makeApplication(false, mInstrumentation);
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManagerNative.getDefault());
            service.onCreate();
Copy the code

BroadcastReceiver

Broadcast is a more complex registration process, there are dynamic registration and static registration, in the high version of the basic can only be dynamic registration, because this article is not the analysis of the startup process, just a simple paste code, pay attention to the whole process please focus on the process code.


    private void handleReceiver(ReceiverData data) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();

        String component = data.intent.getComponent().getClassName();

        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);

        IActivityManager mgr = ActivityManagerNative.getDefault();

        BroadcastReceiver receiver;
        try {
            java.lang.ClassLoader cl = packageInfo.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.setExtrasClassLoader(cl);
            receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
        } catch(Exception e) {... }try {
            Application app = packageInfo.makeApplication(false, mInstrumentation); . ContextImpl context = (ContextImpl)app.getBaseContext(); sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); receiver.onReceive(context.getReceiverRestrictedContext(), data.intent); }catch(Exception e) { ... }}finally {
            sCurrentBroadcastIntent.set(null);
        }

        if(receiver.getPendingResult() ! =null) { data.finish(); }}Copy the code
  • The key code

Here call packageInfo. MakeApplication (false, MInstrumentation) is simply understood as handleBindApplication, except that it returns the current mApplication if it exists and creates it if it doesn’t

    Application app = packageInfo.makeApplication(false, mInstrumentation); . ContextImpl context = (ContextImpl)app.getBaseContext(); sCurrentBroadcastIntent.set(data.intent); receiver.setPendingResult(data); receiver.onReceive(context.getReceiverRestrictedContext(),data.intent);Copy the code

ContentProvider

 private IActivityManager.ContentProviderHolder installProvider(Context context,
            IActivityManager.ContentProviderHolder holder, ProviderInfo info,
            boolean noisy, boolean noReleaseNeeded, boolean stable) {
        ContentProvider localProvider = null; IContentProvider provider; .return retHolder;
    }
Copy the code

This is a strange one. We found that we had already passed a Context from the outside during installation. What is this? HandleBindApplication (AppBindData data) -> installContentProviders(app, providers) In the Application); The Application starts the ContentProviders when the Application starts.

The role of Context

Context has three static constructors. CreateSystemContext is a special constructor. I won’t say that for the moment.

  • The Activity creates its own Context using createActivityContext
  • Application.Service. BroadcastReceiverIt’s all about use from the sourcecreateAppContextTo create your ownContext(Some useApplicationCreated).

As you can see from the following figure, there are many differences between the Activity Context and others. Clear everyone with their own questions to travel in the source code.

  • Net map. I don’t know where it came from.

conclusion

The above code confirms the problem that every Context we actually use is backed up with a ContextImpl with the corresponding information, package name, etc. The time when the ContextImpl is created is when the system records it for us during the initialization of each component created.

So if we go back to the code, we’ll see that when an Activity inherits it doesn’t directly inherit the Context or have a ContextImpl object, where that’s implemented, isn’t that a little confusing?

We understand it this way

  • Context – defines what we can do with it, what function properties we can reference. It’s like using something you can do with your id first.
  • ContextImpl – inherits the Context, which actually does a lot of the work behind it, and is not transparent to the public.
  • ContextWrapper – inherits the Context, one defines it, one does something behind it, and we go to the counter and we’re ContextWrapper, and it’s directly ContextWrapperServiceandApplicationInheritance. (BroadcastReceiverandContentProviderCall each object actively at creation timeattachInfo()The Context is passed to the corresponding object, which is a special certificate and can be issued directly without going through the office hall.
  • ContextThemeWrapper inherits ContextWrapper and implements some special functions for the Activity, which is equivalent to a real-life police station. Inherited by the Activity.

There is also a slight difference between the whole process and the real world. In the code, after the ContextImpl is created, the ContextImpl is calledattach()Function – >AttachBaseContext ()Synchronization toContextWrapperThe flow of the program is black and white.

In reality certainly won’t you take your proof to say who you are, you are who, the specific service department also has to confirm the information. Reality is more complicated than procedure.

So what’s the use of analyzing this other than just understanding the Context correctly?

1: When we write code, old hands always tell us not to use the Application Context to do messy things. This is the thing that you always take your household register this to do your ID card to be able to solve in reality, big just small use. It violates the single blame rule in programming. Another unusual situation is that some frameworks need to pass a Context object to bind the corresponding lifecycle. If you accidentally use an Application Context, these objects will be created repeatedly and recycled only when the Application destroys them.

2: There are also memory leaks caused by Context. It’s the equivalent of someone dying, your ID card should be recycled, but you’re still doing your job. You can imagine the consequences. (Use Context out of life cycle, causing memory not to be reclaimed)

supplement

  • 1 the source code is 6.0 source code.
  • 2. This paper only briefly analyzes the design idea of Context, so as to make people understand it vividly. Please don’t see the wood for the trees.
  • 3. There may be some places in the article that are not thorough enough, or the sentence is not smooth enough. I hope you can tell the author if you have any suggestions after reading it. Kowtow!