美文网首页爱上Android
【Android组件核心面试题】介绍一下Android中的Con

【Android组件核心面试题】介绍一下Android中的Con

作者: 小城哇哇 | 来源:发表于2023-10-13 15:57 被阅读0次

    介绍一下Android中的Context?

    这道题想考察什么?
    1. 是否了解Context的相关知识?
    考察的知识点
    1. Context是什么
    2. Context的结构
    3. Context的注意事项
    考生应该如何回答
    一、Context是什么

    Context 是 Android 中用的十分常见的一种概念,常被翻译成上下文,这个概念在其他的技术中也有运用。Android 官方对它的解释,可以理解为应用程序环境中全局信息的接口,它整合了相当多系统级的服务,可以用来得到应用中的类、资源,以及可以进行应用程序级的调起操作,比如启动 Activity、Service等等,而且 Context 这个类是 抽象abstract 的,不含有具体的函数实现。

    二、Context结构

    Context 是维持 Android 程序中各组件能够正常工作的一个核心功能类。

    Context 本身是一个抽象类,其主要实现类为 ContextImpl,另有直系子类两个:

    • ContextWrapper
    • ContextThemeWrapper

    这两个子类是 Context 的代理类,它们继承关系如下:

    context.jpg
    ContextImpl类介绍

    ContextImpl 是 Context API 的十分常见实现,它为 Activity 和其他应用程序组件提供基本上下文对象,说白了就是 ContextImpl 实现了抽象类的方法,我们在使用 Context 的时候的方法就是它实现的。

    ContextWrapper类介绍

    ContextWrapper 类代理 Context 的实现,将其所有调用简单地代理给另一个 Context 对象(ContextImpl),可以被分类为修饰行为而不更改原始 Context 的类,其实就 Context 类的修饰类。真正的实现类是 ContextImpl,ContextWrapper 里面的方法执行也是执行 ContextImpl 里面的方法。

    ContextThemeWrapper

    就是一个带有主题的封装类,比 ContextWrapper 多了主题,它的一个直接子类就是 Activity。

    通过Context的继承关系图结合我们几个开发中比较常见的类,Activity、Service、Application,所以Context 一共有三种类型,分别是 Application、Activity 和Service,他们分别承担不同的责任,都属于 Context,而他们具有 Context 的功能则是由ContextImpl 类实现的。

    三、Context的数量

    其实根据上面的 Context 类型我们就已经可以得出答案了。Context 一共有 Application、Activity 和 Service 三种类型对象,因此一个应用程序中Context 数量的计算公式就可以这样写:

    Context数量 = Activity数量 + Service数量 + 1

    上面的1代表着 Application 的数量,因为一个应用程序中可以有多个 Activity 和多个 Service,但是只能有一个 Application。

    四、Context注意事项

    Context 如果使用不恰当很容易引起内存泄露问题。

    最简单的例子比如说使用了 Context 的错误的单例模式:

    public class Singleton {
        private static Singleton instance;
        private Context mContext;
    
        private Singleton(Context context) {
            this.mContext = context;
        }
    
        public static Synchronized Singleton getInstance(Context context) {
            if (instance == null) {
                instance = new Singleton(context);
            }
            return instance;
        }
    }
    

    上述代码中,我们使得了一个静态对象持有 Context 对象,而静态数据的生命一般是长于普通对象的,因此当 Context 被销毁(例如假设这里持有的是 Activity 的上下文对象,当 Activity 被销毁的时候),因为 instance 仍然持有 Context 的引用,导致 Context 虽然被销毁了但是却无法被GC机制回收,因为造成内存泄露问题。

    而一般因为Context所造成的内存泄漏,基本上都是 Context 已经被销毁后,却因为被引用导致GC回收失败。但是 Application 的 Context 对象却会随着当前进程而一直存在,所以使用 Context 是应当注意:

    • 当 Application 的 Context 能完成需要的情况下,并且生命周期长的对象,优先使用 Application 的 Context。

    • 不要让生命周期长于 Activity 的对象持有到 Activity 的引用。

    • 尽量不在 Activity 中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

    五、如何正确回复以上面试题
    1. 面试官:Android 中有多少类型的 Context,它们有什么区别?

    回答总共有 Activity 、Service、Application 这些 Context 。

    共同点:它们都是 ContextWrapper 的派生类,而 ContextWrapper 的成员变量 mBase 可以用来存放系统实现的 ContextImpl,这样我们在执行如 Activity 的 Context 方法时,都是通过静态代理的方式最终执行到 ContextImpl 的方法。我们调用 ContextWrapper 的 getBaseContext 方法就能得到 ContextImpl 的实例。

    不同点:它们有各自不同的生命周期;在功能上,只有 Activity 显示界面,正因为如此,Activity 继承的是 ContextThemeWrapper 提供一些关于主题、界面显示的能力,间接继承了 ContextWrapper ;而 Applicaiton 、Service 都是直接继承 ContextWrapper ,所以我们注意一点,但凡跟 UI 有关的,都应该用 Activity 作为 Context 来处理,不然要么会报错,要么 UI 会使用系统默认的主题。

    1. 面试官:一个APP应用里有几个 Context 呢?

    Context 一共有 Application 、Activity 和 Service 三种类型,因此一个应用程序中 Context 数量的计算公式就可以这样写:

    Context 数量 = Activity 数量 + Service 数量 + 1

    上面的1代表着 Application 的数量,因为一个App中可以有多个Activity和多个 Service,但是只能有一个 Application。

    1. 面试官:Android 开发过程中,Context 有什么用?

    Context 就等于 Application 的大管家,主要负责:

    • 四大组件的信息交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等。
    • 获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等。
    • 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等。
    • 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等。

    其它辅助功能,比如配置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

    1. 面试官:ContextImpl 实例是什么时候生成的,在 Activity 的 onCreate 里能拿到这个实例吗?

    可以。我们开发的时候,经常会在 onCreate 里拿到 Application,如果用 getApplicationContext 取,最终调用的就是 ContextImpl 的 getApplicationContext 方法,如果调用的是 getApplication 方法,虽然没调用到 ContextImpl ,但是返回 Activity 的成员变量 mApplication 和 ContextImpl 的初始化时机是一样的。 再说下它的原理,Activity 真正开始启动是从 ActivityThread.performLaunchActivity 开始的,这个方法做了这些事:

    • 通过 ClassLoader 去加载目标 Activity 的类,从而创建 对象。
    • 从 packageInfo 里获取 Application 对象。
    • 调用 createBaseContextForActivity 方法去创建 ContextImpl。
    • 调用 activity.attach ( contextImpl , application) 这个方法就把 Activity 和 Application 以及 ContextImpl 关联起来了,就是上面结论里说的时机一样。
    • 最后调用 activity.onCreate 生命周期回调。

    通过以上的分析,我们知道了 Activity 是先创建类,再初始化 Context ,最后调用 onCreate , 从而得出问题的答案。不仅 Activity 是这样, Application 、Service 里的 Context 初始化也都是这样的。

    1. 面试官:ContextImpl 、ContextWrapper、ContextThemeWrapper 有什么区别?
    • ContextWrapper、ContextThemeWrapper 都是 Context 的代理类,二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化。
    • ContextImpl 是 Context 的主要实现类,Activity、Service 和 Application 的 Base Context 都是由它建立的,即 ContextWrapper 代理的就是 ContextImpl 对象本身。
    • ContextImpl 和 ContextThemeWrapper 的主要区别是, ContextThemeWrapper 有 Configuration 对象,Resource 可以根据这个对象来初始化。
    • Service 和 Application 使用同一个 Recource,和 Activity 使用的 Resource 不同。
    1. 面试官:Activity Context、Service Context、Application Context、Base Context 有什么区别?
    • Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类 。
    • Service 和 Application 使用相同的Recource,和 Activity 使用的 Resource 不同。
    • getApplicationContext 返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application。
    1. 面试官:为什么不推荐使用 BaseContext?
    • 对于 Service 和 Application 来说,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致出现错误。
    • 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能出现无法预期的现象。
    1. 面试官:ContentProvider 里的 Context 是什么时候初始化的呢?

    ContentProvider 不是 Context ,但是它有一个成员属性 mContext ,是通过构造函数传入的。那么这个问题就变成了,ContentProvider 什么时候创建。应用创建 Application 是通过执行 ActivityThread.handleBindApplication 方法,这个方法的相关流程有:

    • 创建 Application
    • 初始化 Application 的 Context
    • 执行 installContentProviders 并传入刚创建好的 Application 来创建 ContentProvider
    • 执行 Application.onCreate

    得出结论,ContentProvider 的 Context 是在 Applicaiton 创建之后,但是 onCreate 方法调用之前初始化的。

    1. 面试官:BroadcastReceiver 里的 Context是哪来的?

    广播接收器,分动态注册和静态注册。

    • 动态注册很简单,在调用 Context.registerReceiver 动态注册 BroadcastReceiver 时,会生成一个 ReceiverDispatcher 会持有这个 Context ,这样当有广播分发到它时,执行 onReceiver 方法就可以把 Context 传递过去了。当然,这也是为什么不用的时候要 unregisterReceiver 取消注册,不然这个 Context 就泄漏了哦。
    • 静态注册时,在分发的时候最终执行的是 ActivityThread.handleReceiver ,这个方法直接通过 ClassLoader 去创建一个 BroadcastReceiver 的对象,而传递给 onReceiver 方法的 Context 则是通过 context.getReceiverRestrictedContext() 生成的一个以 Application 为 mBase 的 ContextWrapper。注意这边的 Context 不是 Application。

    最后

    有需要面试题的朋友可以关注一下哇哇,以上都可以分享!!!

    相关文章

      网友评论

        本文标题:【Android组件核心面试题】介绍一下Android中的Con

        本文链接:https://www.haomeiwen.com/subject/oerabdtx.html