Android中Context的總結及其用法


  在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有兩種類型

androidcontext可以作很多操作,但是最主要的功能是加載和訪問資源。在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 view

setContentView(label);
}
}

  這段程序看起來很簡單,但是卻問題很大。當屏幕旋轉的時候會有leak(即gc沒法銷毀activity)。我們剛才說過,屏幕旋轉的時候系統會銷毀當前的activity。但是當drawable和view關聯后,drawable保存了view的 reference,即sBackground保存了label的引用,而label保存了activity的引用。既然drawable不能銷毀,它所引用和間接引用的都不能銷毀,這樣系統就沒有辦法銷毀當前的activity,於是造成了內存泄露。gc對這種類型的內存泄露是無能為力的。避免這種內存泄露的方法是避免activity中的任何對象的生命周期長過activity,避免由於對象對 activity的引用導致activity不能正常被銷毀。 

為了防止內存泄露,我們應該注意以下幾點:

    1. 不要讓生命周期長的對象引用activity context,即保證引用activity的對象要與activity本身生命周期是一樣的
    2. 對於生命周期長的對象,可以使用application context
    3. 避免非靜態的內部類,盡量使用靜態類,避免生命周期問題,注意內部類對外部對象引用導致的生命周期變化

 

 

什么時候創建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也有一個contextandroid會保證這個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();
    ...
  }
} 

 

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM