zoukankan      html  css  js  c++  java
  • Android中Context的总结及其用法

      在android中我们经常遇到这样的情况,在创建一个对象的时候往往需要传递一个this参数,比如:语句 MyView mView = new MyView(this),要求传递一个this参数,这个this究竟指的是什么东西呢? 其实这里的this指的就是当前的Activity.this,是这个语句所在的Activity的this。Activity.this取的是这个Activity的Context,那么这个Context究竟是什么东西呢?它起到什么作用呢?

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

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

    Context继承关系如下:

    Context有两种类型

    androidcontext可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是activity context。

    补充:

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

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

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

    (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实例的

    情况有如下几种情况:

      1、创建Application 对象时, 而且整个App共一个Application对象

      2、创建Service对象时

      3、创建Activity对象时

    因此应用程序App共有的Context数目公式为:

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

     具体创建Context的时机

     1、创建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 }

        2、创建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实例
            …
        }
        …
    }

       3、创建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();
        ...
      }
    } 

      

  • 相关阅读:
    个人不断学习的真正起因(值得收藏)——北漂18年(24)
    IPython基础使用_Round2
    IPython基础使用_Round2
    Mysql 创建查询用户
    8.11.3 Concurrent Inserts 并发插入:
    8.11.2 Table Locking Issues 表锁发生
    8.11.1 Internal Locking Methods
    Oracle timestamp
    报表引擎API开发入门— EJB程序数据源
    8.10.3 The MySQL Query Cache
  • 原文地址:https://www.cnblogs.com/changjl/p/4175422.html
Copyright © 2011-2022 走看看