Android中Context的总结及其用法

  在android中咱们常常遇到这样的状况,在建立一个对象的时候每每须要传递一个this参数,好比:语句 MyView mView = new MyView(this),要求传递一个this参数,这个this究竟指的是什么东西呢? 其实这里的this指的就是当前的Activity.this,是这个语句所在的Activity的this。Activity.this取的是这个Activity的Context,那么这个Context到底是什么东西呢?它起到什么做用呢?php

  Context 按照英文字面意思就是"上下文",它位于位于framework package的android.content.Context中,其实该类为LONG型,相似于句柄。不少方法须要经过 Context才能识别调用者的实例。html

Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它容许获取以应用为特征的资源和类型。同时启动应用级的操做,如启动一个Activity,发送广播,接受Intentjava

Context继承关系以下:android

Context有两种类型app

androidcontext能够做不少操做,可是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是activity context。ide

补充:函数

getApplicationContext() 返回应用的上下文,生命周期是整个应用,应用摧毁它才摧毁this

Activity.this的context 返回当前activity的上下文,属于activity ,activity 摧毁他就摧毁url

getBaseContext()  返回由构造函数指定或setBaseContext()设置的上下文,通常不经常使用。spa

 

(1)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的内存。

注释:为何GC没有办法回收相应的内存,我的感受是由于传递Context会增长对象指针的引用计数,因此基于智能指针技术的GC没法释放相应的内存。

当屏幕旋转的时候,系统会销毁当前的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不能正常被销毁。 

为了防止内存泄露,咱们应该注意如下几点:

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

 

 

何时建立Context实例

熟悉了Context的继承关系后,咱们接下来分析应用程序在什么状况须要建立Context对象的?应用程序建立Context实例的

状况有以下几种状况:

  一、建立Application 对象时, 并且整个App共一个Application对象

  二、建立Service对象时

  三、建立Activity对象时

所以应用程序App共有的Context数目公式为:

                     总Context实例个数 = Service个数 + Activity个数 + 1(Application对应的Context实例)

 具体建立Context的时机

 一、建立Application对象的时机

  每一个应用程序在第一次启动时,都会首先建立Application对象。若是对应用程序启动一个Activity(startActivity)流程比较

清楚的话,建立Application的时机在建立handleBindApplication()方法中,该函数位于 ActivityThread.java类中 ,以下:

 1 //建立Application时同时建立的ContextIml实例
 2 private final void handleBindApplication(AppBindData data){
 3  4     ///建立Application对象
 5     Application app = data.info.makeApplication(data.restrictedBackupMode, null);
 6  7 }
 8 public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {
 9 10     try {
11         java.lang.ClassLoader cl = getClassLoader();
12         ContextImpl appContext = new ContextImpl();    //建立一个ContextImpl对象实例
13         appContext.init(this, null, mActivityThread);  //初始化该ContextIml实例的相关属性
14         ///新建一个Application对象
15         app = mActivityThread.mInstrumentation.newApplication(
16                 cl, appClass, appContext);
17        appContext.setOuterContext(app);  //将该Application实例传递给该ContextImpl实例
18     }
19 20 }

    二、建立Activity对象的时机

        经过startActivity()或startActivityForResult()请求启动一个Activity时,若是系统检测须要新建一个Activity对象时,就会

  回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去建立一个Activity实例,而且回调

 onCreate(),onStart()方法等, 函数都位于 ActivityThread.java类 ,以下:

//建立一个Activity实例时同时建立ContextIml实例
private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {
    …
    Activity a = performLaunchActivity(r, customIntent);  //启动一个Activity
}
private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {
    …
    Activity activity = null;
    try {
        //建立一个Activity对象实例
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
    }
    if (activity != null) {
        ContextImpl appContext = new ContextImpl();      //建立一个Activity实例
        appContext.init(r.packageInfo, r.token, this);   //初始化该ContextIml实例的相关属性
        appContext.setOuterContext(activity);            //将该Activity信息传递给该ContextImpl实例
        …
    }
    …
}

   三、建立Service对象的时机

       经过startService或者bindService时,若是系统检测到须要新建立一个Service实例,就会回调handleCreateService()方法,

 完成相关数据操做。handleCreateService()函数位于 ActivityThread.java类,以下:

 //建立一个Service实例时同时建立ContextIml实例
private final void handleCreateService(CreateServiceData data){
    …
    //建立一个Service实例
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        service = (Service) cl.loadClass(data.info.name).newInstance();
    } catch (Exception e) {
    }
    …
    ContextImpl context = new ContextImpl(); //建立一个ContextImpl对象实例
    context.init(packageInfo, null, this);   //初始化该ContextIml实例的相关属性
    //得到咱们以前建立的Application对象信息
    Application app = packageInfo.makeApplication(false, mInstrumentation);
    //将该Service信息传递给该ContextImpl实例
    context.setOuterContext(service);
    …
}

   另外,须要强调一点的是,经过对ContextImp的分析可知,其方法的大多数操做都是直接调用其属性mPackageInfo(该属性类

型为PackageInfo)的相关方法而来。这说明ContextImp是一种轻量级类,而PackageInfo才是真正重量级的类。而一个App里的

全部ContextIml实例,都对应同一个packageInfo对象。

 

(2)application context

生命周期: application context生命周期比较长,伴随应用程序的存在而存在,与activity的生命周期无关。

获取: application context能够经过Context.getApplicationContext或者Activity.getApplication方法获取。

Java里面一般是用一个static的变量(例如singleton之类的)来同步activity之间(程序里面类之间)的状态。在android里面比较靠谱的作法是用application context来关联这些状态。

每一个activity都是context,里面包含了运行时的状态。一样application也有一个contextandroid会保证这个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();
    ...
  }
} 
相关文章
相关标签/搜索