如需转载请评论或简信,并注明出处,未经允许不得转载
目录
前言
在Android应用进程的创建 — Activity的启动流程中我们发现,Application
和Activity
都是由系统创建的,它们并不能像其他java类一样,由应用层通过new操作创建出对象,且它们都继承自Context
,那么大家有没有想过,这个Context
到底有什么作用呢?它在Android应用中扮演了一个怎么样的角色呢?
Context继承关系
这里整理了一张Context
的继承关系类图,从这个图中可以看出,Context
是一个接口,ContextImp
和ContextWrapper
都是其实现类,我们常用的Activity
、Service
、Application
都直接或间接继承自ContextWrapper
通过这张图,我们可以整理出几个问题:
一个应用程序有几个
Context
?为什么
Activity
、Service
、Application
都继承自Context
,Context
的作用是什么呢?为什么
Activity
需要继承自ContextThemeWrapper
,而Service
和Application
直接继承自ContextWrapper
呢?为什么
ContextWrapper
中存在一个ContextImp
类型的变量mBase
,且同时它又实现了Context
呢?
问题分析
问题一
一个应用程序有几个Context?
Application
,Activity
,Service
都继承自Context
,而应用有几个进程,就会存在几个Application对象
Context个数 = Activity个数 + Service个数 + 进程个数
问题二
为什么Activity、Service、Application都继承自Context,Context的作用是什么呢?
Context是一个接口,所以要想知道Context的作用,其实就是看它有哪些接口,这些接口的功能是什么
/**
* Interface to global information about an application environment. This is
* an abstract class whose implementation is provided by
* the Android system. It
* allows access to application-specific resources and classes, as well as
* up-calls for application-level operations such as launching activities,
* broadcasting and receiving intents, etc.
*/
public abstract class Context {
// 四大组件相关
public abstract void startActivity(@RequiresPermission Intent intent);
public abstract void sendBroadcast(@RequiresPermission Intent intent);
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,
IntentFilter filter);
public abstract void unregisterReceiver(BroadcastReceiver receiver);
public abstract ComponentName startService(Intent service);
public abstract boolean stopService(Intent service);
public abstract boolean bindService(@RequiresPermission Intent service,
@NonNull ServiceConnection conn, @BindServiceFlags int flags);
public abstract void unbindService(@NonNull ServiceConnection conn);
public abstract ContentResolver getContentResolver();
// 获取系统/应用资源
public abstract AssetManager getAssets();
public abstract Resources getResources();
public abstract PackageManager getPackageManager();
public abstract Context getApplicationContext();
public abstract ClassLoader getClassLoader();
public final @Nullable T getSystemService(@NonNull Class serviceClass) { ... }
public final String getString(@StringRes int resId) { ... }
public final int getColor(@ColorRes int id) { ... }
public final Drawable getDrawable(@DrawableRes int id) { ... }
public abstract Resources.Theme getTheme();
public abstract void setTheme(@StyleRes int resid);
public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { ... }
// 获取应用相关信息
public abstract ApplicationInfo getApplicationInfo();
public abstract String getPackageName();
public abstract Looper getMainLooper();
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
// 文件相关
public abstract File getSharedPreferencesPath(String name);
public abstract File getDataDir();
public abstract boolean deleteFile(String name);
public abstract File getExternalFilesDir(@Nullable String type);
public abstract File getCacheDir();
...
public abstract SharedPreferences getSharedPreferences(String name, @PreferencesMode int mode);
public abstract boolean deleteSharedPreferences(String name);
// 数据库相关
public abstract SQLiteDatabase openOrCreateDatabase(...);
public abstract boolean deleteDatabase(String name);
public abstract File getDatabasePath(String name);
...
// 其它
public void registerComponentCallbacks(ComponentCallbacks callback) { ... }
public void unregisterComponentCallbacks(ComponentCallbacks callback) { ... }
...
}
public interface ComponentCallbacks {
void onConfigurationChanged(Configuration newConfig);
void onLowMemory();
}
结合代码可以看出,Context
就像是应用的大管家,正是因为有了Context
,各种应用组件才有意义,他们才能访问系统服务,系统资源
Context
的作用总结为如下几个方面:
- 四大组件的交互,包括启动
Activity
、Broadcast
、Service
,获取ContentResolver
等 - 获取系统/应用资源,包括
AssetManager
、PackageManager
、Resources
、System Service
以及color
、string
、drawable
等 - 文件,包括获取缓存文件夹、删除文件、
SharedPreference
相关等 - 数据库(
SQLite
)相关,包括打开数据库、删除数据库、获取数据库路径等 - 其它辅助功能,比如设置
ComponentCallbacks
,即监听配置信息改变、内存不足等事件的发生
问题三
为什么Activity需要继承自ContextThemeWrapper,而Service和Application直接继承自ContextWrapper呢?
下面来看一下ContextThemeWrapper
的源码
public class ContextThemeWrapper extends ContextWrapper {
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
private Configuration mOverrideConfiguration;
private Resources mResources;
public ContextThemeWrapper() {
super(null);
}
public ContextThemeWrapper(Context base, @StyleRes int themeResId) {
super(base);
mThemeResource = themeResId;
}
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
}
public void applyOverrideConfiguration(Configuration overrideConfiguration) {...}
public Configuration getOverrideConfiguration() {...}
@Override
public AssetManager getAssets() {...}
@Override
public Resources getResources() {...}
private Resources getResourcesInternal() {...}
@Override
public void setTheme(int resid) {...}
@Override
public int getThemeResId() {...}
@Override
public Resources.Theme getTheme() {...}
@Override
public Object getSystemService(String name) {...}
protected void onApplyThemeResource(Resources.Theme theme, int resId, boolean first) {
private void initializeTheme() {...}
}
ContextThemeWrapper
类,从它的命名就可以看出,其内部包含了与Theme
相关的接口,当然,只有Activity才需要主题,Service
和Application
是不需要主题的,因为Service
是没有界面的后台场景,所以Service
和Application
直接继承于ContextWrapper
问题四
为什么ContextWrapper中存在一个ContextImp类型的变量mBase,且同时它又实现了ContextWrapper呢?
下面来看ContextWrapper
的代码
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
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();
}
....
}
很显然,ContextWrapper
只是一个Context
静态代理类,所有的操作都是通过内部成员 mBase
完成的,而mBase
就是ContextImp
对象。为什么要这么设计呢?如果Application
直接继承ContextImp
会不会有什么问题呢?
一般情况下,使用代理而不直接使用某个对象,目的可能有两个:
- 定制自己的行为
- 不影响原对象
其中 Servcie
和 Application
的父类 ContextWrapper 完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper
则自定义了 Resource
以及 Theme
的相关行为,因此:
- 对于
Service
和Application
而言,不直接继承ContextImp
,是担心用户修改了ContextImp
而导致错误的发生 - 对于 Activity 而言,除了担心用户的修改之外,
ContextImp
和Activity
本身对于Reource
以及Theme
的相关行为是不同的
其他使用Context注意点
- 在单例模式中使用Context要注意内存泄漏问题
我们知道,单例模式的生命周期是和应用的生命周期保持一致的,所以在单例模式中使用Context
,不能使用Activity Context
,需要使用Application Context
- 创建dialog需要使用Activtiy Context
果我们使用Application的Context,或者说Token可以不是Activity的Token,那么用户可能已经跳转到别的应用的Activity界面了,但我们却可以在别人的界面上弹出我们的Dialog,想想就觉得很危险
具体可以参考:https://www.jianshu.com/p/628ac6b68c15
- Activity的this和getBaseContext()有什么区别?
Activity
就是继承Context
的,所以this
是返回Activity
自己
getBaseContext()
返回的是ContextWrapper
里面的mBase
- getApplication()和getApplicationContext()有什么区别?
都是返回Applicatoin
对象,但是他们的作用域不一样
getApplicatoin()
是Activity
和Service
里面特有的,其他地方不能用
BroadcastReceiver
的onReceive()
中的第一个参数,拿到的Context对象是不能调用getApplication()
的只能调getApplicationContext()
- Application构造方法中调用父类方法会发生闪退(如getResource()等)
Application
的创建过程主要是下面三步
//1.创建ContextImpl
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
//2.创建Application
Application app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
//3.调用Application#onCreate()
app.onCreate();
Instrumentation#newApplication()
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#attach()
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
Application#attachBaseContext()
protected void attachBaseContext(Context base) {
if (mBase != null) {
throw new IllegalStateException("Base context already set");
}
mBase = base;
}
从上面的代码中可以看出,在Application
对象刚被创建的时候,其内部的mBase
变量是空的,直到执行attachBaseContext()
后,mBase
才会有值,之后才会调用Application#onCreate()
。所以在Application
中使用Context
接口中的相关方法,可以在onCreate()
里面调用,而不能在构造方法中调用
总结
Context
是一个抽象类,我们开发过程中几乎每天都要和它打交道,但是我相信很多人都说不出他是个什么东西,很多只是知道它叫做上下文。其实当我们觉得一个东西很抽象很难理解的时候,无外乎就是看一下它的创建过程以及它具备哪些方法
这里做一个比喻,对于我们应用层开发来说,Activity
就像是一个”皇帝“,Activity
可以做很多很多的事情,但是Context
就像是他手中的权利,如果没有Context
,Activity
其实只是一个”普通人“(普通java类)而已
我们很多人往往把Activity理解成它继承了Context,是的没错,它确实继承自Context,但我认为,把Activity理解成它代理了Context,会更贴合实际意义一些