android中this、getAppliaction()、context的区别。

来源:互联网 发布:linux root权限不够 编辑:程序博客网 时间:2024/05/17 23:28

android中this、getAppliaction()、context的区别。

@(android中的小知识)[this|getApplication| context]

在日常的android开发中,我们经常会跟this、getApplication()、context、getApplicationContext()打交道,平时疏于理解,想什么就用什么,今天我们来深入探究,具体来分析分析它们之间的不同。

  • this
    同Java中的this用法一样,this一般常用的方式就是对当前对象的引用。
    当然,我们也可以用this来表示类的成员变量,从而跟我们定义的函数中参数同名的变量用以区分。
    this也常用在类的构造方法中来引用满足指定参数类型的构造器。
public class Count {    /**     * @param args     *      *      */      private String name;        private int number;   //这是类的成员变量        public Count(String name){        }        public Count(String name, int number) {            this(name);  //->表示调用一个参数的构造器。            this.number =number+6;  //this.number->类的成员变量。 number->构造器的参数变量            System.out.println("this.number ="+this.number+"---number="+number);        }    public static void main(String[] args) {        Count count = new Count("aa", 1);        // TODO Auto-generated method stub    }}

如上图,我们运行程序,在控制台看到的输出结果为:

Alt text
这里需要注意的是:使用this(name);来表示调用一个参数的构造器时,该代码必须在此构造器方法的第一行,不然会报Constructor call must be the first statement in a constructor的错误。
Alt text

  • 类名.this
    在android开发中,我们经常会使用如下的形式:
    我们在一个类的内部使用this,表示的是对该类的中当前对象的引用。而

  • context
    相信大家在日常的android代码编写过程中会经常的用到Contxt。context顾名思义就是上下文、场景的意思,我们用它来获取系统服务、加载资源、 获取内部文件路径等等。
    我们找到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. */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;    public static final int MODE_WORLD_WRITEABLE = 0x0002;    public static final int MODE_APPEND = 0x8000;    public static final int MODE_MULTI_PROCESS = 0x0004;    .    .    .    }

我们从上面源码的注释可以看出Context是一个抽象类,提供了关于应用环境全局信息的接口。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。
Alt text
从上面的类图中我们可以看出,Activity、Application、Service都是其的间接子类。
- Context的使用(getContext()和getApplicationContext())

TextView tv = new TextView(getContext());ListAdapter adapter = new SimpleCursorAdapter(getApplicationContext(), ...);AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);getApplicationContext().getSharedPreferences(name, mode);getApplicationContext().getContentResolver().query(uri, ...);getContext().getResources().getDisplayMetrics().widthPixels * 5 / 8;getContext().startActivity(intent);getContext().startService(intent);getContext().sendBroadcast(intent);

从上面的各种使用方式中我们可以看到Context的使用多种多样,但是它的使用还是要遵循一定的规则,不能想用什么就用什么。不然会造成意想不到的后果,这个我们在后面再说。
在绝大多数的场景下,Activity、Service以及Application这三种类型的Context对象都是可以通用的,不过有几种场景比较特殊,比如启动Activity,还有弹出Dialog。出于安全原因的考虑,Android是不允许Activity或Dialog凭空出现的,一个Activity的启动必须要建立在另一个Activity的基础之上,也就是以此形成的返回栈。而Dialog则必须在一个Activity上面弹出(除非是System Alert类型的Dialog),因此在这种场景下,我们只能使用Activity类型的Context,否则将会出错。
Alt text

  • Context的获取方式和作用域
    要想获取Context对象,主要有以下的四种方法:

1.View.getContext,返回当前View的Context对象,通常是当前正在展示的Activity对象。作用域为此Activity内。
2.Activity.getApplicationContext,获取当前Activity所在应用的Context对象。这个Context的作用域为整个应用程序内。
3.ContextWrapper.getBaseContext(),用来获取一个ContextWrapper进行装饰之前的Context,它的作用是在另一个Context中访问context.获得的是一个Activity的Context对象。不怎么常用。这里我们举个例子特别说明下(布局界面很简单,就一个Button这里再不列出xml布局):

package com.example.yyh.test;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.Toast;public class MainActivity extends AppCompatActivity {    private Button btn;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        btn = (Button) findViewById(R.id.btn);        btn.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {               // Toast.makeText(this,"this的toast",Toast.LENGTH_LONG).show(); //---》错误,不能使用this              Toast.makeText(getBaseContext(),"getBaseContext的toast"+getBaseContext(),Toast.LENGTH_LONG).show();//->正确                Toast.makeText(getApplicationContext(),"getApplicationContext的toast"+getApplicationContext(),Toast.LENGTH_LONG).show();//--》正确            }        });    }}

第一个Toast报出的错误如下:
Alt text
因为使用Toast第一个参数必须为一个Context对象,而这里传入的this,实际指的是onClick()方法中的参数View对象,所以报错。(对this不熟悉的可以看看文章开头所讲的this)
我们再看看后面两个Toast中的context,,看看其中的不同。
Alt text
Alt text
可以看出,一个是ContextImpl中的Context,返回的是Activity中的Context;一个是Application中的Context。返回的是应用程序的Context。从上面我们分析的Context类图中可以看到,Context抽象类有两个具体的实现类。geBaseContext()是ContextImpl类中的方法,而getApplicationContext()是ContextWrapper的子类Application中的方法。

4.使用this(Activity.this)获得是Acitvity中的context,它的作用域是Activity.

  • Context使用隐患
    这么多能获得Context对象的方式,我们要慎重选择,因为如果没有考虑清楚,很可能会引起内存泄露的问题。
    例如我们自定义的一个MyActivityManager:
public class MyActivityManager{//定义的一个单例activity管理类    private static MyActivityManager sInstance;  private Context mContext;    public static MyActivityManager getInstance(Context context) {        if (sInstance == null) {            synchronized(MyActivity.class){                if(sInstance == null){                      // 这个类拥有了一个静态的context引用               sInstance = new MyActivityManager(context);                }            }        }        return sInstance;    }     private MyActivityManager(Context context) {        mContext = context;    }}

然后我们在Activity中应用它:

public class MyActivity extends Activity{      private MyActivityManager manager = null;      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          //...layout 初始化          manager = MyActivityManager.getInstance(this);      }  }  

由于MyActivityManager是单例模式,这个类的 生命周期属于整个应用程序,如果这个时候我们关闭了MyActivity,android进行GC操作,但是由于manager仍然引用了MyActivity(在sInstance = new MyActivityManager(context);),从而导致MyActivity不能被系统回收,造成内存泄露。解决的方式有两种:
1.使用正确的Context

public class MyActivityManager{//定义的一个单例activity管理类    private static MyActivityManager sInstance;  private Context mContext;    public static MyActivityManager getInstance(Context context) {        if (sInstance == null) {            synchronized(MyActivity.class){                if(sInstance == null){                      // 这个类拥有了一个静态的context引用               /**             *这里我们进行一些改变,将context转变为context.getApplicationContext()             *             */                 sInstance = new MyActivityManager(context.getApplicationContext());                }            }        }        return sInstance;    }     private MyActivityManager(Context context) {        mContext = context;    }}

还有可以在MyActivity中进行改变:

public class MyActivity extends Activity{      private MyActivityManager manager = null;      @Override      protected void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          //...layout 初始化           /**             *这里我们进行一些改变,将context转变为getApplicationContext()             *             */             manager = MyActivityManager.getInstance(getApplicationContext());      }  }  

2.使用弱引用
重新改造单例Activity管理类:

public class MyActivityManager{//定义的一个单例activity管理类    private static MyActivityManager sInstance;  private WeakReference<Context> mWRContext;    public static MyActivityManager getInstance(Context context) {        if (sInstance == null) {            synchronized(MyActivity.class){                if(sInstance == null){                      // 这个类拥有了一个静态的context引用               sInstance = new MyActivityManager(context);                }            }        }        return sInstance;    }     private MyActivityManager(Context context) {        mWRContext = new WeakRefence<Context>(context);    }}

总之,使用Context,我们要注意以下几点:
1:当Application的Context能搞定的情况下,并且生命周期长的对象,优先使用Application的Context。
2:不要让生命周期长于Activity的对象持有到Activity的引用。
3:尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用,如果使用静态内部类,将外部实例引用作为弱引用持有。

参考
- [Context都没弄明白,还怎么做Android开发?]:http://www.jianshu.com/p/94e0f9ab3f1d

阅读全文
0 0