从Android系统的角度来理解:Context是一个场景,描述的是一个应用程序环境的信息,即上下文,表明与操做系统的交互的一种过程。html
从程序的角度上来理解:Context是个抽象类,而Activity、Service、Application等都是该类的一个实现。android
应用在三种状况下会建立Context对象(即一般说的context):
1> 建立Application 对象时,即第一次启动app时。 整个App共一个Application对象,(咱们只需谨记一点,Application全局只有一个,它自己就已是单例了)因此也只有一个Application 的Context,Application销毁,它也销毁;
2> 建立Activity对象时。Activity销毁,它也销毁;
3> 建立Service对象时。Service销毁,它也销毁。nginx
由此能够获得应用程序App能够建立的Context(Activity和Service没启动就不会建立)个数公式通常为:
总Context实例个数 = Service个数 + Activity个数 + 1(Application对应的Context对象) 设计模式
一般咱们想要获取Context对象,主要有如下四种方法
1:View.getContext,返回当前View对象的Context对象,一般是当前正在展现的Activity对象。
2:Activity.getApplicationContext,获取当前Activity所在的(应用)进程的Context对象,一般咱们使用Context对象时,要优先考虑这个全局的进程Context。
3:ContextWrapper.getBaseContext():用来获取一个ContextWrapper进行装饰以前的Context,可使用这个方法,这个方法在实际开发中使用并很少,也不建议使用。
4:Activity.this 返回当前的Activity实例,若是是UI控件须要使用Activity做为Context对象,可是默认的Toast实际上使用ApplicationContext也能够。app
public class MyActivity extends Activity { Context mContext; public void method() { mContext = this; //获取当前Activity的上下文,若是须要绑定Activity的生命周期,使用它 mContext=MyActivity.this;//获取当前MyActivity的上下文,不方便使用this的时候推荐使用这种方式 //调用Activity.getApplicationContext() mContext = getApplicationContext();//获取当前Application的上下文,若是须要绑定应用的生命周期,使用它 //Activity.getApplication() mContext = getApplication();//获取当前Application的上下文, //调用ContextWrapper.getBaseContext() mContext = getBaseContext();//从上下文A内上下文访问上下文A,不建议使用,若是须要,推荐使用XxxClass.this直接指出上下文 } } public class MyView extends View { Context mContext; public void method() { //调用View.getContext() mContext = getContext(); //获取这个View运行所在地的上下文 } }
getApplication()只能在Activity和Service里使用,指向的是Application对象,由于Application也是Context的一个子类,因此getApplication()能够被用来指向Context。框架
好比若是想要获取在应用清单文件中声明的类,最好不要使用getApplicationContext(),而且最好使用强制转换为本身自定义的Application,由于那样可能会得不到Application对象。ide
Log.i("dyl", "getApplication is = " + myApp);
Log.i("dyl", "getApplicationContext is = " + appContext);
经过上面的代码,打印得出二者的内存地址都是相同的,看来它们是同一个对象。其实这个结果也很好理解,由于前面已经说过了,Application自己就是一个Context,因此这里获取getApplicationContext()获得的结果就是Application自己的实例。那么问题来了,既然这两个方法获得的结果都是相同的,那么Android为何要提供两个功能重复的方法呢?实际上这两个方法在做用域上有比较大的区别。getApplication()方法的语义性很是强,一看就知道是用来获取Application实例的,可是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数状况下咱们都是在Activity或者Service中使用Application的,可是若是在一些其它的场景,好比BroadcastReceiver中也想得到Application的实例,这时就能够借助getApplicationContext()方法了。函数
1)Activity mActivity =new Activity()工具
这样写语法上没有任何错误,Android的应用程序开发采用JAVA语言,Activity本质上也是一个对象。可是,oop
2)你们在编写一些类时,例如工具类,可能会编写成单例的方式,这些工具类大多须要去访问资源,也就说须要Context的参与。
在这样的状况下,就须要注意Context的引用问题。
public class CustomManager { private static CustomManager sInstance; private Context mContext; private CustomManager(Context context) { this.mContext = context; } public static synchronized CustomManager getInstance(Context context) { if (sInstance == null) { sInstance = new CustomManager(context); } return sInstance; } }
对于上述的单例,你们应该都不陌生(请别计较getInstance的效率问题),内部保持了一个Context的引用;这么写是没有问题的,问题在于,这个Context哪来的咱们不能肯定,很大的可能性,你在某个Activity里面为了方便,直接传了个this;这样问题就来了,咱们的这个类中的sInstance是一个static引用,在其内部引用了一个Activity做为Context,也就是说,咱们的这个Activity只要咱们的应用活着,就没有办法进行内存回收。(直接传入Activity的引用,若是当前Activity退出了,但应用尚未退出,sInstance一直持有Activity的引用,MyActivity就不能被回收了)。而咱们的Activity的生命周期确定没这么长,因此形成了内存泄漏。那么,咱们如何才能避免这样的问题呢?有人会说,咱们能够软引用,嗯,软引用,假如被回收了,你不怕NullPointException么。把上述代码作下修改:
public static synchronized CustomManager getInstance(Context context) { if (sInstance == null) { sInstance = new CustomManager(context.getApplicationContext()); } return sInstance; }
这样,咱们就解决了内存泄漏的问题,由于咱们引用的是一个ApplicationContext,它的生命周期和咱们的单例对象一致。
或者直接传入ApplicationContext就能够了。单例模式的静态特性致使它的对象的生命周期是和应用同样的
SingleInstance singleInstance = SingleInstance.getInstance(getApplicationContext());
3)Intent也要求指出上下文,若是想启动一个新的Activity,就必须在Intent中使用Activity的上下文,这样新启动的Activity才能和当前Activity有关联(在activity栈);也可使用application的context,可是须要在Intent中添加 Intent.FLAG_ACTIVITY_NEW_TASK标志,看成一个新任务。ApplicationContext去启动一个LaunchMode为standard的Activity的时候会报错,非Activity类型的Context并无所谓的任务栈,因此待启动的Activity就找不到栈了。解决这个问题的方法就是为待启动的Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就为它建立一个新的任务栈,而此时Activity是以singleTask模式启动的。因此这种用Application启动Activity的方式不推荐使用,Service同Application。
public static void openActivity(Context context){ Intent intent = new Intent(context.getApplicationContext(), SecondActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.getApplicationContext().startActivity(intent); }
4)Context泄露:Handler&内部类
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } }
若是没有仔细观察,上面的代码可能致使严重的内存泄露。Android Lint会给出下面的警告:
In Android, Handler classes should be static or leaks might occur.
可是究竟是泄漏,如何发生的?让咱们肯定问题的根源,先写下咱们所知道的
一、当一个Android应用程序第一次启动时,Android框架为应用程序的主线程建立一个Looper对象。一个Looper实现了一个简单的消息队列,在一个循环中处理Message对象。全部主要的应用程序框架事件(如活动生命周期方法调用,单击按钮,等等)都包含在Message对象,它被添加到Looper的消息队列而后一个个被处理。主线程的Looper在应用程序的整个生命周期中存在。
二、当一个Handle在主线程被实例化,它就被关联到Looper的消息队列。被发送到消息队列的消息会持有一个Handler的引用,以便Android框架能够在Looper最终处理这个消息的时候,调用Handler#handleMessage(Message)。
三、在Java中,非静态的内部类和匿名类会隐式地持有一个他们外部类的引用。静态内部类则不会。
那么,究竟是内存泄漏?好像很难懂,让咱们如下面的代码做为一个例子:
public class SampleActivity extends Activity { private final Handler mLeakyHandler = new Handler() { @Override public void handleMessage(Message msg) { // ... } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 延时10分钟发送一个消息 mLeakyHandler.postDelayed(new Runnable() { @Override public void run() { } }, 60 * 10 * 1000); // 返回前一个Activity finish(); } }
当这个Activity被finished后,延时发送的消息会继续在主线程的消息队列中存活10分钟,直到他们被处理。这个消息持有这个Activity的Handler引用,这个Handler有隐式地持有他的外部类(在这个例子中是SampleActivity)。直到消息被处理前,这个引用都不会被释放。所以Activity不会被垃圾回收机制回收,泄露他所持有的应用程序资源。注意,第15行的匿名Runnable类也同样。匿名类的非静态实例持有一个隐式的外部类引用,所以context将被泄露。
为了解决这个问题,Handler的子类应该定义在一个新文件中或使用静态内部类。静态内部类不会隐式持有外部类的引用。因此不会致使它的Activity泄露。若是你须要在Handle内部调用外部Activity的方法,那么让Handler持有一个Activity的弱引用(WeakReference)以便你不会意外致使context泄露。为了解决咱们实例化匿名Runnable类可能致使的内存泄露,咱们将用一个静态变量来引用他(由于匿名类的静态实例不会隐式持有他们外部类的引用)。
public class SampleActivity extends Activity { /** * 匿名类的静态实例不会隐式持有他们外部类的引用 */ private static final Runnable sRunnable = new Runnable() { @Override public void run() { } }; private final MyHandler mHandler = new MyHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 延时10分钟发送一个消息. mHandler.postDelayed(sRunnable, 60 * 10 * 1000); // 返回前一个Activity finish(); } /** * 静态内部类的实例不会隐式持有他们外部类的引用。 */ private static class MyHandler extends Handler { private final WeakReference<SampleActivity> mActivity; public MyHandler(SampleActivity activity) { mActivity = new WeakReference<SampleActivity>(activity); } @Override public void handleMessage(Message msg) { SampleActivity activity = mActivity.get(); if (activity != null) { // ... } } } }
静态和非静态内部类的区别是比较难懂的,但每个Android开发人员都应该了解。开发中不能碰的雷区是什么?不在一个Activity中使用非静态内部类, 以防它的生命周期比Activity长。相反,尽可能使用持有Activity弱引用的静态内部类。