android中this、getAppliaction()、context的区别。
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
}
}
如上图,我们运行程序,在控制台看到的输出结果为:
这里需要注意的是:使用this(name);来表示调用一个参数的构造器时,该代码必须在此构造器方法的第一行,不然会报Constructor call must be the first statement in a constructor
的错误。
类名.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是一个抽象类,提供了关于应用环境全局信息的接口。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。
从上面的类图中我们可以看出,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,否则将会出错。
- 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报出的错误如下:
因为使用Toast第一个参数必须为一个Context对象,而这里传入的this,实际指的是onClick()方法中的参数View对象,所以报错。(对this不熟悉的可以看看文章开头所讲的this)
我们再看看后面两个Toast中的context,,看看其中的不同。
可以看出,一个是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
下一篇: 全面理解Android的Context
推荐阅读