Android上下文Context的使用说明书

下图是sdk基于26时的源码 image.png

2.创建dialog

使用application的context创建Dialog dialog = new Dialog(getApplicationContext()); 会报这样的错误

image.png 这又是为什么呢? 首先看一下Activity的attach()方法:

//activity

final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);

//创建PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);

//设置windowMananger 其中第二个参数为token
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

}

activity被创建后,会创建PhoneWindow和并设置windowManager绑定token。 这里的 IBinder 对象 mToken 很重要。它是一个 Binder 对象,可以在 app 进程,system_server 进程之间进行传递。和我们通常所说的 Token 一样,这里也可以把它看做是一种特殊的令牌,用来标识 Window ,在对 Window 进行视图操作的时候就可以做一些校验工作。

所以,Activity 对应的 Window/WMS 都是持有这个 mToken 的。结合之前 Application 创建 Dialog 的报错信息,我们可以大胆猜测 Application Context 创建 Dialog 的过程中,并没有实例化类似的 token。

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {

//获取context中的windowManager
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//创建window 并设置windowmanager
final Window w = new PhoneWindow(mContext);
w.setWindowManager(mWindowManager, null, null);
}

再来看看dialog的构造函数,从传入的context中拿到windowManager并创建新的window。我们知道dialog本质上也是一个window,显示的时候也是要通过WindowManager的addView()方式显示的。 看一下dialog的show()方法

//dialog

public void show() {
//通过addView方法–>windowManagerImpl的addView–>windowManagerGlobal的addView–>viewRootImpl的setView…
mWindowManager.addView(mDecor, l);

sendShowMessage();
}

如果传入的 Context 是 Activity,返回的是在 Activity.attach() 方法中创建的 mWindowManager 对象,这个时候 mToken 也已经绑定。

如果传入的 Context 是 Application,最终调用的是父类 ContextImpl 的方法。并没有绑定token

所以,Android 不允许 Activity 以外的 Context 来创建和显示普通的 Dialog (Toast这样的除外)。

3.生命周期导致的内存泄漏

在项目过程中,我们存在很多工具类,这些工具类不限于网络请求及一些工具方法,这些工具方法大多数是需要访问资源的,这时就需要Context的参与

public class SingleInstanceTest {

private SingleInstanceTest(final Context context) {
this.mContext = context;
}

private Context mContext;

public static volatile SingleInstanceTest singleInstanceTest;

public static SingleInstanceTest getInstance() {
if (singleInstanceTest == null) {
synchronized (SingleInstanceTest.class) {
if (singleInstanceTest == null) {
singleInstanceTest = new SingleInstanceTest(context);
}
}
}
return singleInstanceTest;
}
}

一个简单的单例模式,内部持有一个context的引用,这么写是没有问题的,问题在于,这个Context哪来的我们不能确定,很大的可能性,你在某个Activity里面为了方便,直接传了个this;这样问题就来了,我们的这个类中的singleInstanceTest是一个static且强引用的,在其内部引用了一个Activity作为Context,也就是说,我们的这个Activity只要我们的项目活着,就没有办法进行内存回收。而我们的Activity的生命周期肯定没这么长,所以造成了内存泄漏。

如何解决呢?
public static SingleInstanceTest getInstance() {
if (singleInstanceTest == null) {
synchronized (SingleInstanceTest.class) {
if (singleInstanceTest == null) {
//使用ApplicationContext 因为ApplicationContext的生命周期跟app的周期一样长
singleInstanceTest = new SingleInstanceTest(context.getApplicationContext());
}
}
}
return singleInstanceTest;
}

最后

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

  • Android前沿技术大纲

  • 全套体系化高级架构视频

Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
实践技术,群内还有技术大牛一起讨论交流解决问题。**
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值