Context
谈谈你对 Android 中 Context 的理解?
Context
是应用程序和系统之间的桥梁,应用程序访问系统各种资源的接口。
那 context
. 是从哪里来的?AMS
是系统级进程,拥有访问系统级操作的权利,应用程序的启动受 AMS
的调控,在程序启动的过程中,AMS
会把一个“凭证”通过跨进程通信给到我们的应用程序,我们的程序会把这个“凭证”封装成 context
,并提供一系列的接口,这样我们的程序也就可以很方便地访问系统资源了。
系统可以对应用程序级的操作进行调控,限制各种情景下的权限,同时也可以防止恶意攻击。
ContextWrapper
继承自 Context
,但是并没有真正实现 Context
中的抽象方法,而是把方法的实现都 delegate
给 ContextImpl
,只需要把ContextImpl
对象赋值进去即可,ContextImpl
是Context
抽象类的真正实现者,从 AMS
拿来的“凭证”也是封装到了ContextImpl
中,然后赋值给 ContextWrapper
,这里运用到了一种模式:装饰者模式。运用装饰者模式,向外屏蔽 ContextImpl
的内部逻辑,同时当需要更改 ContextImpl
的逻辑实现,ContextWrapper
的逻辑几乎不需要更改。
class ContextImpl extends Context { ... }
public abstract class Context { ... }
public class ContextWrapper extends Context { ... }
public class ContextThemeWrapper extends ContextWrapper { ... }
public class Activity extends ContextThemeWrapper { ... }
为什么有 getApplication()
, 还要 getApplicationContext()
?
getApplication()
只能在 activity
中调用。而 getApplicationContext()
适用范围更广,任意一个 context
对象都可以调用它。
自定义 Application
的目的是在程序启动的时候做全局初始化工作,而不能拿来取代工具类,这严重违背谷歌设计 Application
的原则,也违背 Java
代码规范的单一职责原则。
四大组件里面的 Context
都来源于哪里?
-
Activity
继承自ContextThemeWrapper
,是一个拥有主题的context
对象。 -
Service
继承自ContextWrapper
,也可以和Activity
一样直接使用service.this
来使用context
。和activity
不同的是,Service
没有界面,所以也不需要主题。 -
ContextProvider
使用的是Application
的context
-
BroadcastReceiver
使用的是activity
的context
// /frameworks/base/core/java/android/app/Activity.java
// getBaseContext。这个是 ContextWrapper 中的 mBase 对象,也就是ContextImpl,也是 context 接口的真正逻辑实现
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
如果当我们的代码长时间持有了 activity
的 context
,如静态引用或者单例类,那么会导致 activity
无法被释放。如下面的代码:
// 单例类在应用持续的时间都会一直存在,这样 context 也就会被一直被持有, activity 无法被回收,导致内存泄露。
object MyClass {
lateinit var mContext : Context
fun showToast(context : Context){
mContext = context
}
}
什么时候可以使用 Application
?
不涉及 UI 以及启动 Activity 操作,没有界面的
context
,就不应该有操作界面的权利。使用Application
启动的Activity
必须指定task
以及标记为singleTask
,因为Application
是没有任务栈的,需要重新开一个新的任务栈。
Context
的创建过程
Application
是应用级别的 context
,是在应用被创建的时候被创建的,是第一个被创建的 context
,也是最后一个被销毁的 context
。因而追踪 Application
的创建需要从应用程序的启动流程看起。应用启动的源码流程如下:
ActivityThread.class (api29)
private void handleBindApplication(AppBindData data) {
...
// 创建LoadedApk对象
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
...
Application app;
...
try {
// 创建 Application, 通过 LoadApk 来创建 ContextImpl 以及 Application
app = data.info.makeApplication(data.restrictedBackupMode, null);
...
}
try {
...
// 回调Application的onCreate方法
mInstrumentation.callApplicationOnCreate(app);
}
...
}
Activity
的 context
也是在 Activity
创建的过程中被创建的,这个就涉及到 Activity
的启动流程,这里涉及到三个流程:应用程序请求 AMS
,AMS
处理请求,应用程序响应 Activity
创建事务:
Activity
的创建也是由 AMS
来控制的,AMS
向应用程序进程发送消息来执行具体的启动逻辑。最后会执行到 handleLaunchActivity
这个方法
ActivityThread.class(api29)
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
...
final Activity a = performLaunchActivity(r, customIntent);
...
return a;
}
四大组件的创建都会有这个过程:
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();