Android中的Context理解

1、sdk当中关于Context的介绍

Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

http://developer.android.com/reference/android/content/Context.html

Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的由整个Android应用程序所实现。它允许获取应用当中特定的资源和类,同时允许调用应用级的操作,如启动Activity,broadcasting和接收intents等等。

从以上的描述当中,我们基本获得不了什么信息,更无法理性的认识Context。

我们再来看看Context是子类和部分方法如下:

                

其中Application、Activity、Service等我们在开发当中常用的类,都是其子类,我们在这里也无法认识到它的作用。右图,我们可以看到,我们平时用到的startActivty();startService();等方法都在这里,在这里,我们可以看到Context的第一个作用:

2、第一个作用

Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system.

他提供一个全局的接口,并且这些接口由Android应用程序当中特定的类实现。

我们再来看看Activity的源码:

 /**
     * Launch a new activity.  You will not receive any information about when
     * the activity exits.  This implementation overrides the base version,
     * providing information about
     * the activity performing the launch.  Because of this additional
     * information, the {@link Intent#FLAG_ACTIVITY_NEW_TASK} launch flag is not
     * required; if not specified, the new activity will be added to the
     * task of the caller.
     * 
     * <p>This method throws {@link android.content.ActivityNotFoundException}
     * if there was no Activity found to run the given Intent.
     * 
     * @param intent The intent to start. 
     * 
     * @throws android.content.ActivityNotFoundException
     * 
     * @see #startActivityForResult 
     */
    @Override
    public void startActivity(Intent intent) {
        startActivityForResult(intent, -1);
    }
  public void startActivityForResult(Intent intent, int requestCode) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }
        } else {
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
 

这里,startActivity()的实现调用了Context中没有的startActivityForResult方法得以实现开启新的窗体的Activity的功能。

我们再看看我们平时用到的startService方法,非常遗憾我们没有在Activity当中找到startService的实现,但是我们继续往上找,找到了ContentWrapper时,我们找到了startService的实现

 @Override
    public ComponentName startService(Intent service) {
        return mBase.startService(service);
    }
类似的Application,service等,都可以找到其相应的函数的实现。Context功能之一就是将常用的公共方法抽取出来,然后由各个组件实现,这真是第一句话的意思。

3、第二个作用

It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc.

它允许获取应用当中特定的资源和类,同时允许调用应用级的操作,如启动Activity,broadcasting和接收intents等等。其实该类为LONG型,类似Win32中的Handle句柄很多方法需要通过 Context才能识别调用者的实例,比如说Toast的第一个参数就是Context,一般在Activity中我们直接用this代替,代表调用者的 实例为Activity,而到了一个button的onClick(View view)等方法时,我们用this时就会报错,所以我们可能使用ActivityName.this来解决,主要原因是因为实现Context的类主要有Android特有的几个模型,Activity、Service以及BroadcastReceiver。

对于以上几句话的理解,我们可以看看TextView的构造函数


可以看到起参数为Context但是我们在实际开发当中

TextView text1 = new TextView(this);
TextView text2 = new TextView(getApplication());
TextView text3 = new TextView(getApplicationContext());
这两种定义都可以实现。this表示这个类的某个实例,而getApplicationContext()得到的是Context(),两个风马牛不相及的事物为什么能放在TextView的构造函数里,而不报错呢,这就是Context的第二个作用,句柄。 标识应用程序中的不同对象和同类对象中的不同的实例。

4、两种不同的Context

正如上段代码,getApplication();getApplicationContext();前者是在Activity当中定义的,而后者是在Context当中定义的。
两者的说明分别为:
这里,我们看到了两种不同的Context:Activity所拥有的Context和全局的Context。

这是两种不同的context,也是最常见的两种。第一种中context的生命周期与Application的生命周期相关的,context随着Application的销毁而销毁,伴随application的一生,与activity的生命周期无关。第二种中的context跟Activity的生命周期是相关的,但是对一个Application来说,Activity可以销毁几次,那么属于Activity的context就会销毁多次。至于用哪种context,得看应用场景,个人感觉用Activity的context好一点,不过也有的时候必须使用Application的context。application context可以通过Context.getApplicationContext或者Activity.getApplication方法获取。

5、内存泄露

在android中context可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是activity context,通常我们在各种类和方法间传递的是activity context。比如一个activity的onCreate:
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this); //传递context给view control
label.setText("Leaks are bad");
setContentView(label);
}
把activity context传递给view,意味着view拥有一个指向activity的引用,进而引用activity占有的资源:view hierachy, resource等。这样如果context发生内存泄露的话,就会泄露很多内存。这里泄露的意思是gc没有办法回收activity的内存。Leaking an entire activity是很容易的一件事。
当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息,再创建一个新的。
比如我们写了一个应用程序,它需要加载一个很大的图片,我们不希望每次旋转屏 幕的时候都销毁这个图片,重新加载。实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity 类创建销毁它始终保存在内存中。实现类似:
public class myactivity extends Activity {
private static Drawable sBackground;
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);//drawable attached to a view
setContentView(label);
}
}
这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有leak(即gc没法销毁activity)。我们刚才说过,屏幕旋转的时候系统会销毁当前的activity。但是当drawable和view关联后,drawable保存了view的 reference,即sBackground保存了label的引用,而label保存了activity的引用。既然drawable不能销毁,它所 引用和间接引用的都不能销毁,这样系统就没有办法销毁当前的activity,于是造成了内存泄露。gc对这种类型的内存泄露是无能为力的。避免这种内存泄露的方法是避免activity中的任何对象的生命周期长过activity,避免由于对象对 activity的引用导致activity不能正常被销毁。我们可以使用application context。application context伴随application的一生,与activity的生命周期无关。application context可以通过Context.getApplicationContext或者Activity.getApplication方法获取。

6、避免context相关的内存泄露,记住以下几点:

  1. 不要让生命周期长的对象引用activity context,即保证引用activity的对象要与activity本身生命周期是一样的
  2. 对于生命周期长的对象,可以使用application context
  3. 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化

7、制造Application context的方法

Java里面通常是用一个static的变量(例如singleton之类的)来同步activity之间(程序里面类之间)的状态。在android里面比较靠谱的做法是用application context来关联这些状态。
每个activity都是context,里面包含了运行时的状态。同样application也有一个context,android会保证这个context是唯一的实例。
做一个你自己的application context需要继承android.app.Application,然后在app的manifest里面说明这个类。android会自动帮你创建你这个类的实例,接着你用Context.getApplicationContext()方法就能在各个activity里面获得这个application context了。
class MyApp extends Application {
 
  private String myState;
 
  public String getState(){
    return myState;
  }
  public void setState(String s){
    myState = s;
  }
}
 
class Blah extends Activity {
 
  @Override
  public void onCreate(Bundle b){
    ...
    MyApp appState = ((MyApp)getApplicationContext());
    String state = appState.getState();
    ...
  }
}

小文章,边看边写,欢迎纠错。


  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值