在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
在android中context能够做不少操做,可是最主要的功能是加载和访问资源。在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) { //传递context给view control |
把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不能正常被销毁。
为了防止内存泄露,咱们应该注意如下几点:
熟悉了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也有一个context,android会保证这个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(); ... } }