0
点赞
收藏
分享

微信扫一扫

【达内课程】如何理解Context上下文对象


文章目录

  • ​​介绍​​
  • ​​Context 数量​​
  • ​​如何获取​​
  • ​​Context引起的内存泄露​​
  • ​​正确使用Context​​

介绍

Android 应用都是使用 Java 语言来编写的,思考一下,一个 Android 程序和一个 Java 程序,他们最大的区别在哪里?其实简单点分析,Android 程序不像 Java 程序一样,随便创建一个类,写个​​main()​​方法就能跑了,而是要有一个完整的 Android 工程环境,在这个环境下,我们有像 Activity、Service、BroadcastReceiver 等系统组件,而这些组件并不是像一个普通的 Java 对象 new 一下就能创建实例的了,而是要有它们各自的上下文环境,也就是我们这里讨论的Context。可以这样讲,Context 是维持 Android 程序中各组件能够正常工作的一个核心功能类。

Context的继承结构:
【达内课程】如何理解Context上下文对象_android
我们可以看到 Activity,Service、Application 都是 Context 的子类,所以用到 Context 的时候,我们就可以在类中写​​​this​

我们观察 Context.class,这是个抽象类,里边定义了很多常量,和抽象方法。ContextWrapper 是上下文功能的封装类,而 ContextImpl 则是上下文功能的实现类。

Context 能实现很多功能,例如:弹出Toast、启动Activity、启动Service、发送广播、操作数据库等等。我们可以把 Context 看成是一个工具,因为 Activity 继承了 Context 里边的抽象方法,所以可以直接使用,省去了我们自己实现。

不过有几种场景比较特殊,比如启动 Activity,还有弹出 Dialog。出于安全原因的考虑,Android 是不允许 Activity 或 Dialog 凭空出现的,一个 Activity 的启动必须要建立在另一个 Activity 的基础之上,也就是以此形成的返回栈。而 Dialog 则必须在一个 Activity 上面弹出(除非是 System Alert 类型的 Dialog),因此在这种场景下,我们只能使用 Activity 类型的 Context,否则将会出错。

Context 数量

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

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

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

如何获取

有以下四种方法:
1、​​​View.getContext​​​返回当前 View 对象的 Context 对象,通常是当前正在展示的 Activity 对象。
2、​​​Activity.getApplicationContext​​​获取当前 Activity 所在的(应用)进程的 Context 对象,通常我们使用 Context 对象时,要优先考虑这个全局的进程 Context。
3、​​​ContextWrapper.getBaseContext()​​​用来获取一个 ContextWrappe r进行装饰之前的 Context,可以使用这个方法,这个方法在实际开发中使用并不多,也不建议使用。
4、​​​Activity.this​​ 返回当前的Activity实例,如果是 UI 控件需要使用 Activity 作为 Context 对象,但是默认的 Toast 实际上使用 ApplicationContext 也可以。

Context引起的内存泄露

以下两种错误的引用方式可能引起内存泄漏
1、错误的单例模式

public class Singleton {
private static Singleton instance;
private Context mContext;

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

public static Singleton getInstance(Context context) {
if (instance == null) {
instance = new Singleton(context);
}
return instance;
}
}

这是一个非线程安全的单例模式,instance 作为静态对象,其生命周期要长于普通的对象,其中也包含 Activity,假如 Activity A 去 getInstance 获得i nstance 对象,传入 this,常驻内存的 Singleton 保存了你传入的 Activity A 对象,并一直持有,即使 Activity 被销毁掉,但因为它的引用还存在于一个 Singleton 中,就不可能被 GC掉,这样就导致了内存泄漏。

2、View 持有 Activity 引用

public class MainActivity extends Activity {
private static Drawable mDrawable;

@Override
protected void onCreate(Bundle saveInstanceState) {
super.onCreate(saveInstanceState);
setContentView(R.layout.activity_main);
ImageView iv = new ImageView(this);
mDrawable = getResources().getDrawable(R.drawable.ic_launcher);
iv.setImageDrawable(mDrawable);
}
}

有一个静态的 Drawable 对象当 ImageView 设置这个 Drawable 时,ImageView 保存了 mDrawable 的引用,而 ImageView 传入的 this 是 MainActivity 的 mContext,因为被 static 修饰的 mDrawable 是常驻内存的,MainActivity 是它的间接引用, MainActivity 被销毁时,也不能被 GC 掉,所以造成内存泄漏。

正确使用Context

一般 Context 造成的内存泄漏,几乎都是当 Context 销毁的时候,却因为被引用导致销毁失败, 而Application 的 Context 对象可以理解为随着进程存在的,所以我们总结出使用 Context 的正确姿势:
1、当 Application 的 Context 能搞定的情况下,并且生命周期长的对象,优先使用 Application 的 Context。
2、不要让生命周期长于 Activity 的对象持有到 Activity 的引用。
3:尽量不要在 Activity 中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

参考:
​​​Android Context完全解析,你所不知道的Context的各种细节​​​​Context都没弄明白,还怎么做Android开发?​​

举报

相关推荐

0 条评论