从源码角度看各种context

该博客仅因手机阅读不方便而创建,感兴趣同学可以微信搜索小专栏平台,订阅从源码角度看Android,支持作者原创

简介:

做应用开发的对Context的熟悉度应该是仅次于Activity和Service的。Context,英文名上下文场景,代表着对当前运行场景下的各种信息的一种封装。例如,需要调用四大组件进行工作都要调用到Context,同时,通过Context也可以获取到Resource, Display, Theme, AssetManager, WallPaperManager这些资源对象。因为Context的使用方法实在太普及,实在可以说的上是安卓的第一课,这篇就不给实例了。

 

Context创建方法

 

想要知道都有哪几种不同的Context,从ContextImpl代码中寻找是最好的办法,以下列举了各种不同的Context创建接口及其调用场景:

接口调用场景
createActivityContextstartActivity时和新创建的Activity绑定
createAppContext

startService和bindApplication时和新创建的service和application绑定

createApplicationContext远程widget和notification时会用到
createConfigurationContext用来获取或设置Resources, Theme等资源
createPackageContextinstallProvider时和新创建的ContentProvider绑定
createSystemContextsystem_server启动时被初始化,传入PackageManagerService中
createDisplayContext不常用

上面的表格已经概括出各种类别的Context, 但是我准备按照四大组件的角度去看看不同组件的Context是如何被初始化的以及区别是什么。

 

类图

Activity继承自ContextThemeWrapper; Service、Application继承自ContextWrapper。ContextWrapper的操作是聚合了Context的实例来实现的。真正的功能是在ContextImpl中进行实现。BroadcastReceiver与ContentProvider有些特殊,其实BroadcastReceiver是在调用onReceive方法时传入了Context对象,而ContentProvider聚合了Context对象并没有直接继承ContextWrapper。

 

Activity

 

Activity是最常用也是在四大组件中最复杂的一个

ActivityThread.java

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
…
    // 通过反射创建一个Activity实例
     Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        …
    }

    try {
        // 获取Aapplication实例
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (activity != null) {
            // 创建一个ContextImpl实例
            Context appContext = createBaseContextForActivity(r, activity);
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);

            // 将Activity, Application, ContextImpl三者绑定
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor);
         }
    }
…

    private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        …

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

        …
        return baseContext;
    }
}
ContextImpl.java

static ContextImpl createActivityContext(ActivityThread mainThread,
        LoadedApk packageInfo, int displayId, Configuration overrideConfiguration) {
    if (packageInfo == null) throw new IllegalArgumentException(“packageInfo”);
    return new ContextImpl(null, mainThread, packageInfo, null, null, false,
            null, overrideConfiguration, displayId);
}
Application

ActivityThread.java

ContextImpl instrContext = ContextImpl.createAppContext(this, pi);

try {
    java.lang.ClassLoader cl = instrContext.getClassLoader();
    mInstrumentation = (Instrumentation)
        cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
    throw new RuntimeException(
        “Unable to instantiate instrumentation “
        + data.instrumentationName + “: “ + e.toString(), e);
}

// 初始化instrumentation,绑定Context
mInstrumentation.init(this, instrContext, appContext,
       new ComponentName(ii.packageName, ii.name), data.instrumentationWatcher,
       data.instrumentationUiAutomationConnection);

…

Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;

…

try {
    mInstrumentation.onCreate(data.instrumentationArgs);
} catch (Exception e) {
    throw new RuntimeException(
        “Exception thrown in onCreate() of “
        + data.instrumentationName + “: “ + e.toString(), e);
}

try {
    // 创建Application并调用onCreate方法
    mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
    if (!mInstrumentation.onException(app, e)) {
        throw new RuntimeException(
            “Unable to create application “ + app.getClass().getName()
            + “: “ + e.toString(), e);
    }
}
ContextImpl.java

static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
    if (packageInfo == null) throw new IllegalArgumentException(“packageInfo”);
    return new ContextImpl(null, mainThread,
            packageInfo, null, null, false, null, null, Display.INVALID_DISPLAY);
}
Service

ActivityThread.java

private void handleCreateService(CreateServiceData data) {
…
    // 通过反射创建Service
    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实例,如果没有被实例化过则会创建一个
        Application app = packageInfo.makeApplication(false, mInstrumentation);
        // 将Context, Application与Service绑定
        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) {
…
    }
}
BroadcastReceiver

static final class ReceiverDispatcher {

    final BroadcastReceiver mReceiver;

    ReceiverDispatcher(BroadcastReceiver receiver, Context context,
            Handler activityThread, Instrumentation instrumentation,
            boolean registered) {
…
        mReceiver = receiver;
…
    }

    final class Args extends BroadcastReceiver.PendingResult implements Runnable {
…

        public void run() {
            final BroadcastReceiver receiver = mReceiver;
    …
            try {
                // 通过反射调用BroadcastReceiver的onReceive并传入Context对象
                ClassLoader cl =  mReceiver.getClass().getClassLoader();
                intent.setExtrasClassLoader(cl);
                setExtrasClassLoader(cl);
                receiver.setPendingResult(this);
                receiver.onReceive(mContext, intent);
            } catch (Exception e) {

    …
            }

    …
        }
    }
}
ContextImpl.java

private ContextImpl(ContextImpl container, ActivityThread mainThread,
        LoadedApk packageInfo, IBinder activityToken, UserHandle user, boolean restricted,
        Display display, Configuration overrideConfiguration, int createDisplayWithId) {
    mOuterContext = this;
    …
}

public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
        String receiverPermission, int appOp, Bundle options, BroadcastReceiver resultReceiver,
        Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
    IIntentReceiver rd = null;
    if (resultReceiver != null) {
        if (mPackageInfo != null) {
            …
        } else {
            …

            // 初始化ReceiverDispatcher, 通过getOuterContext获取Context实例
            rd = new LoadedApk.ReceiverDispatcher(resultReceiver, getOuterContext(),
                    scheduler, null, false).getIIntentReceiver();
        }
    }
…
}

final Context getOuterContext() {
    return mOuterContext;
}

由代码可知,ReceiverDispatcher的初始化在它的构造器阶段完成,而如果没有调用过setOuterContext, getOutContext返回的是这个ContextImpl实例本身。由此可以判断,BroadcastReceiver的Context是根据调用者的Context来决定的。

ContentProvider

ActivityThread.java

private IActivityManager.ContentProviderHolder installProvider(Context context,
        IActivityManager.ContentProviderHolder holder, ProviderInfo info,
        boolean noisy, boolean noReleaseNeeded, boolean stable) {
    ContentProvider localProvider = null;
    IContentProvider provider;
    if (holder == null || holder.provider == null) {
        if (DEBUG_PROVIDER || noisy) {
            Slog.d(TAG, “Loading provider “ + info.authority + “: “
                    + info.name);
        }
        Context c = null;
        ApplicationInfo ai = info.applicationInfo;
        // 如果传进来的Context和ContentProvider所在进程一致,则不再需要重新创建
        if (context.getPackageName().equals(ai.packageName)) {
            c = context;
        } else if (mInitialApplication != null &&
                mInitialApplication.getPackageName().equals(ai.packageName)) {
            c = mInitialApplication;
        } else {
            try {
                // 创建一个ContextImpl实例
                c = context.createPackageContext(ai.packageName,
                        Context.CONTEXT_INCLUDE_CODE);
            } catch (PackageManager.NameNotFoundException e) {
                // Ignore
            }
        }
        if (c == null) {
            return null;
        }
        try {
            // 反射创建ContentProvider
            final java.lang.ClassLoader cl = c.getClassLoader();
            localProvider = (ContentProvider)cl.
                loadClass(info.name).newInstance();
            provider = localProvider.getIContentProvider();
…
              // Context与ContentProvider绑定
            localProvider.attachInfo(c, info);
        } catch (java.lang.Exception e) {
            return null;
        }
    } 
…
}
几点区别

Activity, Application/Service ContextImpl 成员变量

|Type|Activity|Application/Service|ContentProvider|
|—|—|—|—|—|
|ContextImpl|||√|
|ActivityThread|√|√|√|
|LoadedApk|√|√|√|
|IBinder|||√|
|UserHandle|||√|
|boolean(restricted)|false|false|√|
|Display|||√|
|Configuration|√|||
|int(createDisplayWithId)|√|||

 

一般我们开发期间实际到的Context一般就两类,一类是Application/Service的Context,另一类是Activity的Context

 

两种Context常用功能的区别

 

两种Context的区别主要在于能否显示dialog,因为要进行ui上的操作需要有显示dialog的window,所以application context不能实现。

 

如果尝试去使用getApplicationContext去显示dialog会报出以下错误:

 

Attempted to add window with non-application token WindowToken{4067a268 token=null}.  Aborting.

D/AndroidRuntime( 1923): Shutting down VM

W/dalvikvm( 1923): threadid=1: thread exiting with uncaught exception (group=0x40015560)

E/AndroidRuntime( 1923): FATAL EXCEPTION: main

E/AndroidRuntime( 1923): java.lang.RuntimeException: Unable to create application 

com.test.shrenik.ControlApplication: android.view.WindowManager$BadTokenException: 

Unable to add window — token null is not for an application

深入代码层去解析:


Dialog.java

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
…
    final Window w = new PhoneWindow(mContext);
    mWindow = w;
…
}

public void show() {
    // 获取window的params
    WindowManager.LayoutParams l = mWindow.getAttributes();
    if ((l.softInputMode
            & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
        WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
        // copyFrom
        nl.copyFrom(l);
        nl.softInputMode |=
                WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
        l = nl;
    }

    try {
        // 加入到mDecorView中
        mWindowManager.addView(mDecor, l);
        mShowing = true;

        sendShowMessage();
    } finally {
    }
}
WindowManager.java

public final int copyFrom(LayoutParams o) {
    if (token == null) {
        // NOTE: token only copied if the recipient doesn’t
        // already have one.
        token = o.token;
    }
}
WindowManagerImpl.java

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}
WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params,
        Display display, Window parentWindow) {
…
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    if (parentWindow != null) {
        parentWindow.adjustLayoutParamsForSubWindow(wparams);
    } else {
        final Context context = view.getContext();
        if (context != null
                && (context.getApplicationInfo().flags
                        & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
            wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        }
    }

    ViewRootImpl root;
    View panelParentView = null;

…
    try {
        root.setView(view, wparams, panelParentView);
    } catch (RuntimeException e) {
        // BadTokenException or InvalidDisplayException, clean up.
        synchronized (mLock) {
            final int index = findViewLocked(view, false);
            if (index >= 0) {
                removeViewLocked(index, true);
            }
        }
        throw e;
    }
}
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
             int res; /* = WindowManagerImpl.ADD_OKAY; */
…
            try {
…
                   // 将LayoutParams加入到window session中
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                        getHostVisibility(), mDisplay.getDisplayId(),
                        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                        mAttachInfo.mOutsets, mInputChannel);
            } catch (RemoteException e) {
…
            } finally {
                if (restore) {
                    attrs.restore();
                }
            }

            if (res < WindowManagerGlobal.ADD_OKAY) {
…
                switch (res) {
…
                        throw new WindowManager.BadTokenException(
                                “Unable to add window — token “ + attrs.token
                                + “ is not for an application”);
…
                }
            }
        }
Session.java

    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
WindowManagerService.java

    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
            WindowToken token = mTokenMap.get(attrs.token);
            if (token == null) {
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                    Slog.w(TAG, “Attempted to add application window with unknown token “
                          + attrs.token + “.  Aborting.”);
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            }
            …
            token = new WindowToken(this, attrs.token, -1, false);
            addToken = true;

            if (addToken) {
                mTokenMap.put(attrs.token, token);
            }
     }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值