Moosphan / Android-Daily-Interview

:pushpin:每工作日更新一道 Android 面试题,小聚成河,大聚成江,共勉之~

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

2019-03-27:对于 Context,你了解多少?

Moosphan opened this issue · comments

2019-03-27:对于 Context,你了解多少?

了解不多 只知道 application service activity 都是具体的context
初始化一些系统组件 如 dialog toast 要把这个context传入
具体由大佬补充

以上两位层主推荐的文章都比较详细的说明了 Context 的来龙去脉和应用场景,这里再简单概括一下:

Context 宏观来说是一个描述应用程序全局信息的场景,当然,本质上来说,这个“场景”其实是一个抽象类,它是一个应用程序的“灵魂人物”,从下面的图中就可以发现 ActivityServiceApplication 都是 Context 的子类:
20160519210912888
Context 虽然无所不在,但是某些特定场景下需要使用特定的 Context,如启动一个弹窗,必须是依赖于 Activity 的,所以,使用 Dialog 必须传入当前 Activity 场景下的 “context”,关于 Context 的作用域可以参考下图:

QkpfS
详细来龙去脉可参考上面两篇文章。
其他问题,如:

  • getApplication()和getApplicationContext()的区别?
  • Context 导致的内存泄漏问题?
  • 正确使用 Context 的姿势?

篇幅原因,先不做详细讲解,后面会一一通过答题方式来探索。
最后,再推荐一篇关于 Context 的深度好文:Mastering Android context

1.Context是个抽象类 Activity跟Application跟Service都间接继承于他
2.Context简称上下文 。Android不像java程序一样,随便创建一个类写个main方法就能跑了,它需要有一个完整的上下文环境,即context,像启动activity/broadcast receiver /service 都需要用到他。
3.context导致的内存泄漏多是长生命周期去持有了短生命周期的实例造成的。像工具类如果需要context的话,能传applicationCOntext 最好传它。
4.getApplication()跟getApplicationContext() 本质没有区别 后者就是把返回的application强转成context传回来而已。返回的都是同一个对象,只是前者只能在Activity跟Service中可以使用,作用域不同。
5.Context数量 = Activity数量 + Service数量 + 1 如果多个进程的话 后面就不是+1 而是加多个
6.ContextWrapper里面有一个attachBaseContext()方法,目的是将Context参数传给mBase,之后的调用系统方法如getPackageName()之类的都是委托mBase实例来做的,所以在调用这个方法之前 是无法调用系统方法的。
构造函数-->attachBaseContext()-->onCreate()
。所以在onCreate()中或者重写这个方法后调用都可以。

    @Override
	protected void attachBaseContext(Context base) {
		// 在这里调用Context的方法会崩溃
		super.attachBaseContext(base);
		// 在这里可以正常调用Context的方法
	}
commented
  1. 在 Android 平台上 , Context 是一个基本的概念,它在逻辑上表示一个运行期的“上下文”
  2. Context 体现到代码上来说,是个抽象类,其主要表达的行为有:使用系统提供的服务、访问资源 、信息
    存储相关以及AMS的交互
  3. 在 Android 平台上,Activity、Service 和 Application 在本质上都是个 Context
  4. Android 中上下文访问应用资源或系统服务的动作都被统一封装进 ContextImpl 类中.Activity、
    Service、Application 内部都含有自己的 ContextImpl,每当自己需要访问应用资源或系统服务时,就是
    把请求委托给内部的 ContextImpl
  5. ContextWrapper 为 Context 的包装类, 它在做和上下文相关的动作时,基本上都是委托给 ContextImpl
    去做
  6. ContextImpl 为一个上下文的核心部件,其负责和Android平台进行通信. 就以启动 activity 动作来说,最后
    会走到 ContextImpl 的 startActivity(),而这个函数内部大体上是进一步调用
    mMainThread.getInstrumentation().execStartActivity(),从而将语义发送给Android系统
  7. Context数量 = Activity数量 + Service数量 + 1
    上面的1表示 Application 数量,一个应用程序里面可以有多个 Application,可是在配置文件
    AndroidManifest.xml中只能注册一个, 只有注册的这个 Application 才是真正的 Application

更多请参考

1.Context本身是一个抽象类。
2.ContextWrapper和ContextImpl是它的直接子类,ContextWrapper进行功能的封装,内部委派ContextImpl去实现。
3.在attachBaseContext(context)方法中,对mBase进行赋值委派。
4.在android中,有3种context,分别是application、service、activity。通常情况下,这3种context的能力都是相同的,但是在有些场景下需要注意,比如启动activity和dialog;因为系统不允许凭空出现activity和dialog(一个启动另一个以此来形成返回栈),所以必须使用activity类型的context.
5.Context的数量=activity的数量+service的数量+1,一个应用只有一个application类型的context.
6.getApplication()和getApplicationContext()返回的都是Application对象本身,但是2者的作用域不一样。getApplication()的作用域比较小,一般只能在activity和service中调用。
7.getBaseContext()方法返回的是ContextImpl对象。
8.Application的方法调用顺序:构造函数->attachBaseContext->onCreate.因此在attachBaseContext方法之前调用context的方法会报空指针,因为这时候mBase==null.
9.不能直接new一个Application对象,因为它是系统组件,它需要系统去创建和维护,否则它不具备context的各种能力。

context是一个抽象类,Activity、Service、Application等都是该类的一个实现
context的数量 = activities+services+1(application)(一个程序中有多个服务和活动,但只有一个application)
ContextIml:Context类的实现类为,该类实现了Context类的功能
ContextWapper:该类只是对Context类的一种包装
ContextThemeWrapper:该类内部包含了主题(Theme)相关的接口,即android:theme属性指定的。只有Activity需要主题,Service不需要主题,所以Service直接继承于ContextWrapper类。
创建context的时机:创建Application时(程序第一次运行时会创建Application)、创建Activity对象是、创建service时

@Moosphan 我整理了一下这个问题的回答,并翻译了一下推荐的英文博客,文章不错,看不下去英文的小伙伴可以看一下这篇译文。

📌📌📌https://www.jianshu.com/p/fcd6341b09b6

Context 也叫上下文,是有关应用程序环境的全局信息的接口。这是一个抽象类, 它允许访问特定于应用程序的资源和类,以及对应用程序级操作的调用,比如启动活动,发送广播和接收意图等;

Activity, Service, Application 都是 Context 的子类。

Context 的具体实现类是 ContextImpl, 还有一个包装类 ContextWrapper, ContextWrapper 的子类有 Service,Application,ContextThemeWrapper, Activity 又是 ContextThemeWrapper 的子类,ContextThemeWrapper 也可以叫 UI Context,跟UI 操作相关的最好使用此类 Context。

ContextWrapper 中有个 mBase,这个 mBase 其实是 ContextImpl,它是在Activity, Service, Application 创建时通过 attachBaseContext() 方法将各自对对应 ContextImpl 赋值的。对 context 的操作,最终实现都是在 ContextImpl。

对于 startActivity操作
* 当为Activity Context则可直接使用;
* 当为其他Context, 则必须带上FLAG_ACTIVITY_NEW_TASK flags才能使用;因为非 Activity context 启动 Activity 没有 Activity 栈,则无法启动,因此需要加开启新的栈;
* 另外UI相关要Activity中使用.

getApplication()和getApplicationContext() 区别?

  1. 对于Activity/Service来说, getApplication()和getApplicationContext()的返回值完全相同; 除非厂商修改过接口;
  2. BroadcastReceiver在onReceive的过程, 能使用getBaseContext().getApplicationContext获取所在Application, 而无法使用getApplication;
  3. ContentProvider能使用getContext().getApplicationContext()获取所在Application. 绝大多数情况下没有问题, 但是有可能会出现空指针的问题, 情况如下:

当同一个进程有多个apk的情况下, 对于第二个apk是由provider方式拉起的, 前面介绍过provider创建过程并不会初始化所在application, 此时执行 getContext().getApplicationContext()返回的结果便是NULL. 所以对于这种情况要做好判空.

activity service application 都继承于context
startService BroadcastReceiver和dialog Toast都需要用context
getApplication和getApplicationContext本质上是没有什么区别的 getApplicationContext把getApplication强转成Context
getApplication只能在activty和service中使用 其他地方只能用getApplicationContext来获取Application

activity service application 都继承于context
startService BroadcastReceiver和dialog Toast都需要用context
getApplication和getApplicationContext本质上是没有什么区别的 getApplicationContext把getApplication强转成Context
getApplication只能在activty和service中使用 其他地方只能用getApplicationContext来获取Application

了解它的拼写