问题
在Android中都知道context,activity,application,service都间接的继承于context。在他们内部都有一个context,那他们之间有什么区别吗,dialog创建时可以传入application的context吗?一开始看到这个问题的时候一脸懵逼,还是通过查看源码寻找一下答案吧。
各种context源码
Context源码
public abstract class Context {
*/
public abstract AssetManager getAssets();
/**
* Returns a Resources instance for the application's package.
* <p>
* <strong>Note:</strong> Implementations of this method should return
* a Resources instance that is consistent with the AssetManager instance
* returned by {@link #getAssets()}. For example, they should share the
* same {@link Configuration} object.
*
* @return a Resources instance for the application's package
* @see #getAssets()
*/
public abstract Resources getResources();
/** Return PackageManager instance to find global package information. */
public abstract PackageManager getPackageManager();
/** Return a ContentResolver instance for your application's package. */
public abstract ContentResolver getContentResolver();
public abstract Looper getMainLooper();
...只贴出部分源码
context中只显示部分源码,context中定义了很多的抽象方法。
ContextWrapper 源码
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
/**
* @return the base context as set by the constructor or setBaseContext
*/
public Context getBaseContext() {
return mBase;
}
@Override
public AssetManager getAssets() {
return mBase.getAssets();
}
@Override
public Resources getResources() {
return mBase.getResources();
}
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
@Override
public ContentResolver getContentResolver() {
return mBase.getContentResolver();
}
@Override
public Looper getMainLooper() {
return mBase.getMainLooper();
}
@Override
public Executor getMainExecutor() {
return mBase.getMainExecutor();
}
@Override
public Context getApplicationContext() {
return mBase.getApplicationContext();
}
ContextWrapper 中实现了context,并且重写了抽象方法并且调用mBase完成具体操作。mBase可以通过构造函数和attachBaseContext初始化,这个mbase具体是什么后面在分析
ContextImpl 源码
class ContextImpl extends Context {
@Override
public AssetManager getAssets() {
return getResources().getAssets();
}
@Override
public Resources getResources() {
return mResources;
}
@Override
public PackageManager getPackageManager() {
if (mPackageManager != null) {
return mPackageManager;
}
IPackageManager pm = ActivityThread.getPackageManager();
if (pm != null) {
// Doesn't matter if we make more than one instance.
return (mPackageManager = new ApplicationPackageManager(this, pm));
}
return null;
}
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
}
@Override
public Looper getMainLooper() {
return mMainThread.getLooper();
}
}
ContextImpl 继承context。
以activity为例分析
在activity的创建过程中会调用performLaunchActivity方法
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
ContextImpl appContext = createBaseContextForActivity(r);//1
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.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) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);//2
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
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, window, r.configCallback);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
}
r.setState(ON_CREATE);
mActivities.put(r.token, r);
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
方法中第1点通过createBaseContextForActivity创建了一个ContextImpl,然后通过调用activity.attach方法把ContextImpl传递给activity,activity调用attachBaseContext进行接收,这个attachBaseContext实际上是调用ContextWrapper的attachBaseContext方法给mBase赋值。那我们就可以知道了activity的context是contextImpl。
再看一下方法中的第二点
//ActivityThread
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
//LoadedApk
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
...
}
//Instrumentation
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
//Application
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
同样的创建application的时候也会创建一个ContextImpl ,最终调用Application.attach方法把ContextImpl传递过去给ContextWrapper的attachBaseContext,那从这里也就可以知道application的context是contextImpl
在Activity中调用startService,点进去发现实际上是调用mbase的startService
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
发现实际上也是调用ActivityManagerService的startService,具体流程就不再分析了,最终会进入ActivityThread的startService方法
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 = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} 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,
ActivityManager.getService());
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
可以看到是调用createAppContext创建了一个ContextImpl并传递给service.attach调用,最终调用ContextWrapper的attachBaseContext,从这里就可以知道service的context也是ContextImpl
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);
mThread = thread; // NOTE: unused - remove?
mClassName = className;
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}
Dialog创建的时候传递Application的context为什么会报错
首先试着用application的context创建一个dialog,启动后发现报错
java.lang.RuntimeException: Unable to start activity
android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2878)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2940)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1663)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:159)
接下来分析一下为什么会报错
从上面的分析知道activity创建的时候调用的是createActivityContext,而application创建的时候调用的是createAppContext
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
null);
context.setResources(packageInfo.getResources());
return context;
}
static ContextImpl createActivityContext(ActivityThread mainThread,
LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
Configuration overrideConfiguration) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
String[] splitDirs = packageInfo.getSplitResDirs();
ClassLoader classLoader = packageInfo.getClassLoader();
if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
try {
classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
} catch (NameNotFoundException e) {
// Nothing above us can handle a NameNotFoundException, better crash.
throw new RuntimeException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
activityToken, null, 0, classLoader);
...
return context;
}
从两个方法可以看到主要区别在于是否有activityToken。
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == ResourceId.ID_NULL) {
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
}
mContext = new ContextThemeWrapper(context, themeResId);
} else {
mContext = context;
}
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
dialog在创建的时候会先根据传入的context获取windowmanager,然后设置windowmanager,可以看到这里设置的apptoken为null。再看一下dialog的show方法
public void show() {
mDecor = mWindow.getDecorView();
if (mActionBar == null && mWindow.hasFeature(Window.FEATURE_ACTION_BAR)) {
final ApplicationInfo info = mContext.getApplicationInfo();
mWindow.setDefaultIcon(info.icon);
mWindow.setDefaultLogo(info.logo);
mActionBar = new WindowDecorActionBar(this);
}
WindowManager.LayoutParams l = mWindow.getAttributes();
boolean restoreSoftInputMode = false;
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
l.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
restoreSoftInputMode = true;
}
mWindowManager.addView(mDecor, l);
if (restoreSoftInputMode) {
l.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
}
mShowing = true;
sendShowMessage();
}
在dialog的show方法里会调用 mWindow.getAttributes()获取一下dialog的window属性,查看源码可以看到window的窗口属性是TYPE_APPLICATION
/**
* Window type: a normal application window. The {@link #token} must be
* an Activity token identifying who the window belongs to.
* In multiuser systems shows only on the owning user's window.
*/
public static final int TYPE_APPLICATION = 2;
查看其解释可以知道,这个属性是普通窗口属性,它要求窗口必须要有activity的token。
检查完属性之后会调用mWindowManager.addView(mDecor, l);这个mWindowManager从上面dialog的初始化知道是通过传入的context获取的。
还记得上面分析的activity和application创建context时调用方法的区别吗,就是activity会多一个activityToken。因此如果通过用Application或者Service的Context去获取这个WindowManager服务的话,会得到一个WindowManagerImpl的实例,这个实例里token也是空的。之后在Dialog的show方法中将Dialog的View添加到WindowManager时会给token设置默认值还是null。
用activity的context去创建的话使用的是activity的token就不会报错了。
总结
1.Activity集成于ContextThemeWrapper,ContextThemeWrapper继承于ContextWrapper。
2.Sercice继承于ContextWrapper
3.Application继承于ContextWrapper
4.ContextWrapper继承于Context
5.Activity,Sercice,Application的mBase都指向ContextImpl
6.ContextImpl继承于Context
7.通过查看源码可以发现ContextWrapper算是ContextImpl的代理类