Context

2021-10-13  本文已影响0人  Drew_MyINTYRE

谈谈你对 Android 中 Context 的理解?

Context 是应用程序和系统之间的桥梁,应用程序访问系统各种资源的接口。

context. 是从哪里来的?AMS 是系统级进程,拥有访问系统级操作的权利,应用程序的启动受 AMS 的调控,在程序启动的过程中,AMS 会把一个“凭证”通过跨进程通信给到我们的应用程序,我们的程序会把这个“凭证”封装成 context,并提供一系列的接口,这样我们的程序也就可以很方便地访问系统资源了。

系统可以对应用程序级的操作进行调控,限制各种情景下的权限,同时也可以防止恶意攻击。

ContextWrapper 继承自 Context,但是并没有真正实现 Context 中的抽象方法,而是把方法的实现都 delegateContextImpl,只需要把ContextImpl 对象赋值进去即可,ContextImplContext 抽象类的真正实现者,从 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 都来源于哪里?

// /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);
    }

如果当我们的代码长时间持有了 activitycontext,如静态引用或者单例类,那么会导致 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);
    }
    ...
}

Activitycontext 也是在 Activity 创建的过程中被创建的,这个就涉及到 Activity 的启动流程,这里涉及到三个流程:应用程序请求 AMSAMS 处理请求,应用程序响应 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();
上一篇下一篇

猜你喜欢

热点阅读