Android Context

俗话说得好,Context都没弄明白,怎么做Android开发。

  1. 谈谈你对Activity的Context的认识?⭐⭐⭐⭐⭐

  1. Application和Activity,Context的区别?⭐⭐⭐⭐⭐

  1. getApplication()和getApplicationContext()的区别?⭐⭐⭐⭐

  1. context错误用法有哪些?⭐⭐⭐

  1. 如何正确使用Context?⭐⭐⭐⭐

目录

  • 1、什么是Context,能干什么?

  • 2、一个应用程序有几个Context?

  • 3、如何获取Context

  • 4、Context的错误用法和正确使用方法

  • 4.1 错误使用静态方法

  • 4.2 错误使用静态View对象

  • 4.3 如何正确使用Context

1、 什么是Context,能干什么?

Context直译过来是“语境”,“上下文”,“环境”的意思。以前在学习嵌入式Linux的时候,也经常说到进程上下文,中断上下文。而在“安卓上下文”中,我们需要先明白安卓的应用模型是基于组件的应用设计模式,比如Activity和Service这些组件在运行的时候,都需要一个完整的Android工程环境。那么在代码里,这个“环境”由谁提供?那自然就是Context类。 作为Android代码里出镜率最高的Context,除了负责四大组件的交互场景外,还有很多的场景都需要用到Context,如:

  • 获取系统属性,系统资源(color、string、drawable等)场景;

  • 数据存储场景,如使用文件,SharedPreference,数据库的场景;

我们来看看源码里Context类系列:

Context如下代码,本身是一个纯abstract类,那么自然有对应的实现子类:ContextImpl和ContextWrapper,其中ContextImpl是Context真正的实现类,ContextWrapper类则和其名字一样,只是一个封装类,并沒有真正的实现,真正的实现是其包含了一个mBase变量,是通过attachBaseContext() 方法来设置的,本质上是 ContextImpl对象。

public abstract class Context {
    /**
     * File creation mode: the default mode, where the created file can only
     * be accessed by the calling application (or all applications sharing the
     * same user ID).
     * @see #MODE_WORLD_READABLE
     * @see #MODE_WORLD_WRITEABLE
     */
    public static final int MODE_PRIVATE = 0x0000;

    ...
    }

接着,ContextThemeWrapper又是继承于ContextWrapper,正如其名,ContextThemeWrapper相对于ContextWrapper多包含了与主题(Theme)相关的接口,这个主题就是AndroidManitest.xml里面application元素或者Activity元素通过android:theme指定的主题。如下面的theme,虽然在application元素里指定,但只在Activity界面才会使用到。

<application
    android:name=".MyApplication"
    ...
    android:theme="@style/Theme.XrTest">

正因为只有Activity需要指定主题,而Service和Application是不需要使用主题的。因此才有Actvity继承ContextThemeWrapper,而Service和Application直接继承ContextWrapper。Application、Activity、Service通过attach()调用父类ContextWrapper的attachBaseContext(), 从而设置父类成员变量 mBase 为 ContextImpl 对象。

2、一个应用程序有几个Context?

这个问题现在就很好回答了,一个Application的基础还是有安卓四大组件构成的,既:

Application = Activity + Service + Broadcast Receiver + Content Provider

从第1节的图,我们知道Application和Activity和Service都继承与Context,而Broadcast Receiver和Content Provider虽然也需要context,但不是继承于Context,而是由外部传进入。因此一个应用的context个数就由其包含了多少个Activity和Service决定,并且最后再加上自己本身持有的context,即

应用Context数量 = Activity数 + Service数 + 1;

3、如何获取Context

既然绝大多数场景都需要在安卓完整的工程环境实现,那么我们如何能获取到context呢?主要有以下方法:

  • Activity.this:很多情况下要求传入context,此时发现我们可以直接传入activity本身,这是因为activity只是继承与context的;

  • getApplication()和getApplicationContext():这个方法也很常见,因为应用本省就是一个context,所以获取应用实例即可。不过说明一下这两个方法返回的结果确实是同样的结果,但getApplication()是获取应用实例,只有在Activity和Service中可以调用,而getApplicationContext()含义是为了获取的是整个应用程序的工程环境,可以在Broadcast Receiver和Content Provider中调用;

  • view.getContext:每个view都需要在安卓的完整工程环境下才能正常显示,操作,因此每个view本身都持有context对象,这个context对象一般是该view所在的Activity实例;

4、Context的错误用法和正确使用方法

4.1 错误使用静态方法

下面是一个非线程安全的单例模式,instance作为静态对象,生命周期会高于普通对象。因此如果传入某个应用的实例,会导致XuruiEnvelopeImpl一直持有XrActivity对象,当XrActivity被销毁时,因为它还有一个引用被别人持有,因此无法被垃圾回收机制回收,造成了内存泄露。

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

    private XuruiEnvelopeImpl(Context context) {
        this.mContext = context;
    }
    public static XuruiEnvelopeImpl getInstance(Context context){
        if(mEnvelopeImpl == null){
            mEnvelopeImpl = new XuruiEnvelopeImpl(context);
        }
        return mEnvelopeImpl;
    }
}

//使用
XuruiEnvelopeImpl.getInstance(XrActivity.this);

4.2 错误使用静态View对象

public class MainActivity extends Activity {
    private static Drawable mDrawable; //1

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

注释1定义了一个静态的mDrawable,在注释2传入非静态ImageView对象里,第3节有说过,一个view对象都会持有所在的activity的引用,因此静态的mDrawable会一直持有非静态ImageView对象所在的activity引用,该引用跟着静态的mDrawable会一直保存在内存,因此导致activity销毁时,垃圾回收机制发现该activity还有一个引用被别人持有,将无法进行垃圾回收,造成内存泄露。

4.3 如何正确使用Context

  • 因为Application和activity都有context对象,如果可以使用Application的context则优先使用;

  • 注意别让生命周期可能长于actvity的对象持有activity的引用;

  • 尽量避免在activity里使用非静态内部类,这一点在Handler那一节就着重说明了,因为非静态内部类会持有外部类的引用。推荐使用静态内部类,并将外部类的引用改为弱引用。

PS:什么情况必须用Activity的Context呢?

答:启动新的Activity或者弹出一个Dialog,只能用Activity的Context,不能用Application的Context,因为启动Activity需要任务栈,而非Activity的Context是没有的。同时,Dialog必须基于Activity上弹出。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值