標籤:
在語句 android中我們經常遇到這樣的情況,在建立一個對象的時候往往需要傳遞一個this參數,比如:語句 MyView mView = new MyView(this),要求傳遞一個this參數,這個this究竟指的是什麼東西呢? 其實這裡的this指的就是當前的Activity.this,是這個語句所在的Activity的this。Activity.this取的是這個Activity的Context,那麼這個Context究竟是什麼東西呢?它起到什麼作用呢?
Context 按照英文字面意思就是"上下文",它位於位於framework package的android.content.Context中,其實該類為LONG型,類似於控制代碼。很多方法需要通過 Context才能識別調用者的執行個體。
Context提供了關於應用環境全域資訊的介面。它是一個抽象類別,它的執行被Android系統所提供。它允許擷取以應用為特徵的資源和類型。同時啟動應用級的操作,如啟動一個Activity,發送廣播,接受Intent
Context繼承關係如下:
Context有兩種類型
在android中context可以作很多操作,但是最主要的功能是載入和訪問資源。在android中有兩種context,一種是 application context,一種是activity context。
補充:
getApplicationContext() 返回應用的上下文,生命週期是整個應用,應用摧毀它才摧毀
Activity.this的context 返回當前activity的上下文,屬於activity ,activity 摧毀他就摧毀
getBaseContext() 返回由建構函式指定或setBaseContext()設定的上下文,一般不常用。
(1)activity context
通常我們在各種類和方法間傳遞的是activity context。比如一個activity的onCreate
protected void onCreate(Bundle state) { super.onCreate(state); TextView label = new TextView(this); //傳遞context給view control
label.setText("Leaks are bad"); setContentView(label); } |
把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 viewsetContentView(label);}}
這段程式看起來很簡單,但是卻問題很大。當旋轉螢幕的時候會有leak(即gc沒法銷毀activity)。我們剛才說過,旋轉螢幕的時候系統會銷毀當前的activity。但是當drawable和view關聯後,drawable儲存了view的 reference,即sBackground儲存了label的引用,而label儲存了activity的引用。既然drawable不能銷毀,它所引用和間接引用的都不能銷毀,這樣系統就沒有辦法銷毀當前的activity,於是造成了記憶體泄露。gc對這種類型的記憶體泄露是無能為力的。避免這種記憶體泄露的方法是避免activity中的任何對象的生命週期長過activity,避免由於對象對 activity的引用導致activity不能正常被銷毀。
為了防止記憶體泄露,我們應該注意以下幾點:
- 不要讓生命週期長的對象引用activity context,即保證引用activity的對象要與activity本身生命週期是一樣的
- 對於生命週期長的對象,可以使用application context
- 避免非靜態內部類,盡量使用靜態類,避免生命週期問題,注意內部類對外部對象引用導致的生命週期變化
什麼時候建立Context執行個體
熟悉了Context的繼承關係後,我們接下來分析應用程式在什麼情況需要建立Context對象的?應用程式建立Context執行個體的
情況有如下幾種情況:
1、建立Application 對象時, 而且整個App共一個Application對象
2、建立Service對象時
3、建立Activity對象時
因此應用程式App共有的Context數目公式為:
總Context執行個體個數 = Service個數 + Activity個數 + 1(Application對應的Context執行個體)
具體建立Context的時機
1、建立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 }
2、建立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執行個體 … } …}
3、建立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(); ... }}
Android中Context的總結及其用法