在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(); ... } }