当前位置: 代码迷 >> Android >> android context小结
  详细解决方案

android context小结

热度:122   发布时间:2016-04-28 03:13:58.0
android context总结

Context类 ,说它熟悉,是应为我们在开发中时刻的在与它打交道,例如:Service、BroadcastReceiver、Activity等都会利用到Context的相关方法 ; 说它陌生,完全是因为我们真正的不懂Context的原理、类结构关系。一个简单的问题是,一个应用程序App中存在多少个Context实例对象呢?一个、两个? 在此先卖个关子吧。读了本文,相信您会豁然开朗的 。

 

      Context,中文直译为“上下文”,SDK中对其说明如下:

         Interface to global information about an application environment. This is an abstract class whose implementation

  is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls 

  for application-level operations such as launching activities, broadcasting and receiving intents, etc

 

    从上可知一下三点,即:

        1、它描述的是一个应用程序环境的信息,即上下文。

        2、该类是一个抽象(abstract class)类,Android提供了该抽象类的具体实现类(后面我们会讲到是ContextIml类)。

        3、通过它我们可以获取应用程序的资源和类,也包括一些应用级别操作,例如:启动一个Activity,发送广播,接受Intent

      信息 等。。

 

 

   于是,我们可以利用该Context对象去构建应用级别操作(application-level operations) 。

 

 一、Context相关类的继承关系

 

                         

 

  相关类介绍:

 

   Context类    路径: /frameworks/base/core/java/android/content/Context.java

            说明:  抽象类,提供了一组通用的API。

      源代码(部分)如下:   

[java] view plaincopyprint?
  1. public abstract class Context {  
  2.      ...  
  3.      public abstract Object getSystemService(String name);  //获得系统级服务  
  4.      public abstract void startActivity(Intent intent);     //通过一个Intent启动Activity  
  5.      public abstract ComponentName startService(Intent service);  //启动Service  
  6.      //根据文件名得到SharedPreferences对象  
  7.      public abstract SharedPreferences getSharedPreferences(String name,int mode);  
  8.      ...  
  9. }  

 

  ContextIml.java类  路径 :/frameworks/base/core/java/android/app/ContextImpl.java

          说明:该Context类的实现类为ContextIml,该类实现了Context类的功能。请注意,该函数的大部分功能都是直接调用

      其属性mPackageInfo去完成,这点我们后面会讲到。    

         源代码(部分)如下:

[java] view plaincopyprint?
  1. /** 
  2.  * Common implementation of Context API, which provides the base 
  3.  * context object for Activity and other application components. 
  4.  */  
  5. class ContextImpl extends Context{  
  6.     //所有Application程序公用一个mPackageInfo对象  
  7.     /*package*/ ActivityThread.PackageInfo mPackageInfo;  
  8.       
  9.     @Override  
  10.     public Object getSystemService(String name){  
  11.         ...  
  12.         else if (ACTIVITY_SERVICE.equals(name)) {  
  13.             return getActivityManager();  
  14.         }   
  15.         else if (INPUT_METHOD_SERVICE.equals(name)) {  
  16.             return InputMethodManager.getInstance(this);  
  17.         }  
  18.     }   
  19.     @Override  
  20.     public void startActivity(Intent intent) {  
  21.         ...  
  22.         //开始启动一个Activity  
  23.         mMainThread.getInstrumentation().execStartActivity(  
  24.             getOuterContext(), mMainThread.getApplicationThread(), nullnull, intent, -1);  
  25.     }  
  26. }  

 


 

  ContextWrapper类 路径 :\frameworks\base\core\java\android\content\ContextWrapper.java

        说明: 正如其名称一样,该类只是对Context类的一种包装,该类的构造函数包含了一个真正的Context引用,即ContextIml

       对象。    源代码(部分)如下:

[java] view plaincopyprint?
  1. public class ContextWrapper extends Context {  
  2.     Context mBase;  //该属性指向一个ContextIml实例,一般在创建Application、Service、Activity时赋值  
  3.       
  4.     //创建Application、Service、Activity,会调用该方法给mBase属性赋值  
  5.     protected void attachBaseContext(Context base) {  
  6.         if (mBase != null) {  
  7.             throw new IllegalStateException("Base context already set");  
  8.         }  
  9.         mBase = base;  
  10.     }  
  11.     @Override  
  12.     public void startActivity(Intent intent) {  
  13.         mBase.startActivity(intent);  //调用mBase实例方法  
  14.     }  
  15. }  

 


 

   ContextThemeWrapper类 路径:/frameworks/base/core/java/android/view/ContextThemeWrapper.java

      说明:该类内部包含了主题(Theme)相关的接口,即android:theme属性指定的。只有Activity需要主题,Service不需要主题,

   所以Service直接继承于ContextWrapper类。

      源代码(部分)如下:

[java] view plaincopyprint?
  1. public class ContextThemeWrapper extends ContextWrapper {  
  2.      //该属性指向一个ContextIml实例,一般在创建Application、Service、Activity时赋值  
  3.        
  4.      private Context mBase;  
  5.     //mBase赋值方式同样有一下两种  
  6.      public ContextThemeWrapper(Context base, int themeres) {  
  7.             super(base);  
  8.             mBase = base;  
  9.             mThemeResource = themeres;  
  10.      }  
  11.   
  12.      @Override  
  13.      protected void attachBaseContext(Context newBase) {  
  14.             super.attachBaseContext(newBase);  
  15.             mBase = newBase;  
  16.      }  
  17. }  

 

 

     Activity类 、Service类 、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类中 ,如下:

[java] view plaincopyprint?
  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.   
  9. public Application makeApplication(boolean forceDefaultAppClass, Instrumentation instrumentation) {  
  10.     ...  
  11.     try {  
  12.         java.lang.ClassLoader cl = getClassLoader();  
  13.         ContextImpl appContext = new ContextImpl();    //创建一个ContextImpl对象实例  
  14.         appContext.init(thisnull, mActivityThread);  //初始化该ContextIml实例的相关属性  
  15.         ///新建一个Application对象   
  16.         app = mActivityThread.mInstrumentation.newApplication(  
  17.                 cl, appClass, appContext);  
  18.        appContext.setOuterContext(app);  //将该Application实例传递给该ContextImpl实例           
  19.     }   
  20.     ...  
  21. }  

 

 

    2、创建Activity对象的时机

 

       通过startActivity()或startActivityForResult()请求启动一个Activity时,如果系统检测需要新建一个Activity对象时,就会

  回调handleLaunchActivity()方法,该方法继而调用performLaunchActivity()方法,去创建一个Activity实例,并且回调

 onCreate(),onStart()方法等, 函数都位于 ActivityThread.java类 ,如下:

[java] view plaincopyprint?
  1. //创建一个Activity实例时同时创建ContextIml实例  
  2. private final void handleLaunchActivity(ActivityRecord r, Intent customIntent) {  
  3.     ...  
  4.     Activity a = performLaunchActivity(r, customIntent);  //启动一个Activity  
  5. }  
  6. private final Activity performLaunchActivity(ActivityRecord r, Intent customIntent) {  
  7.     ...  
  8.     Activity activity = null;  
  9.     try {  
  10.         //创建一个Activity对象实例  
  11.         java.lang.ClassLoader cl = r.packageInfo.getClassLoader();  
  12.         activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);  
  13.     }  
  14.     if (activity != null) {  
  15.         ContextImpl appContext = new ContextImpl();      //创建一个Activity实例  
  16.         appContext.init(r.packageInfo, r.token, this);   //初始化该ContextIml实例的相关属性  
  17.         appContext.setOuterContext(activity);            //将该Activity信息传递给该ContextImpl实例  
  18.         ...  
  19.     }  
  20.     ...      
  21. }  

 

 

   3、创建Service对象的时机

 

       通过startService或者bindService时,如果系统检测到需要新创建一个Service实例,就会回调handleCreateService()方法,

 完成相关数据操作。handleCreateService()函数位于 ActivityThread.java类,如下:

[java] view plaincopyprint?
  1. //创建一个Service实例时同时创建ContextIml实例  
  2. private final void handleCreateService(CreateServiceData data){  
  3.     ...  
  4.     //创建一个Service实例  
  5.     Service service = null;  
  6.     try {  
  7.         java.lang.ClassLoader cl = packageInfo.getClassLoader();  
  8.         service = (Service) cl.loadClass(data.info.name).newInstance();  
  9.     } catch (Exception e) {  
  10.     }  
  11.     ...  
  12.     ContextImpl context = new ContextImpl(); //创建一个ContextImpl对象实例  
  13.     context.init(packageInfo, nullthis);   //初始化该ContextIml实例的相关属性  
  14.     //获得我们之前创建的Application对象信息  
  15.     Application app = packageInfo.makeApplication(false, mInstrumentation);  
  16.     //将该Service信息传递给该ContextImpl实例  
  17.     context.setOuterContext(service);  
  18.     ...  
  19. }  


 

    另外,需要强调一点的是,通过对ContextImp的分析可知,其方法的大多数操作都是直接调用其属性mPackageInfo(该属性类

型为PackageInfo)的相关方法而来。这说明ContextImp是一种轻量级类,而PackageInfo才是真正重量级的类。而一个App里的

有ContextIml实例,都对应同一个packageInfo对象。

            

 

     最后给大家分析利用Context获取SharedPreferences类的使用方法,SharedPreferences类想必大家都使用过,其一般获取方

法就是通过调用getSharedPreferences()方法去根据相关信息获取SharedPreferences对象。具体流程如下:

 

    1 、调用  getSharedPreferences()获取对应的的文件,该函数实现功能如下:

 

[java] view plaincopyprint?
  1. //Context类静态数据集合,以键值对保存了所有读取该xml文件后所形成的数据集合  
  2. private static final HashMap<File, SharedPreferencesImpl> sSharedPrefs =   
  3.        new HashMap<File, SharedPreferencesImpl>();   
  4.   
  5. @Override  
  6. public SharedPreferences getSharedPreferences(String name, int mode){  
  7.      //其所对应的SharedPreferencesImpl对象 ,该对象已一个HashMap集合保存了我们对该文件序列化结果  
  8.      SharedPreferencesImpl sp;    
  9.      File f = getSharedPrefsFile(name);  //该包下是否存在对应的文件,不存在就新建一个  
  10.      synchronized (sSharedPrefs) {       //是否已经读取过该文件,是就直接返回该SharedPreferences对象  
  11.          sp = sSharedPrefs.get(f);  
  12.          if (sp != null && !sp.hasFileChanged()) {  
  13.              //Log.i(TAG, "Returning existing prefs " + name + ": " + sp);  
  14.              return sp;  
  15.          }  
  16.      }  
  17.      //以下为序列化该xml文件,同时将数据写到map集合中       
  18.      Map map = null;  
  19.      if (f.exists() && f.canRead()) {  
  20.          try {  
  21.              str = new FileInputStream(f);  
  22.              map = XmlUtils.readMapXml(str);  
  23.              str.close();  
  24.          }   
  25.          ...  
  26.      }  
  27.        
  28.      synchronized (sSharedPrefs) {  
  29.          if (sp != null) {  
  30.              //Log.i(TAG, "Updating existing prefs " + name + " " + sp + ": " + map);  
  31.              sp.replace(map);   //更新数据集合  
  32.          } else {  
  33.              sp = sSharedPrefs.get(f);  
  34.              if (sp == null) {    
  35.                  //新建一个SharedPreferencesImpl对象,并且设置其相关属性  
  36.                  sp = new SharedPreferencesImpl(f, mode, map);    
  37.                  sSharedPrefs.put(f, sp);  
  38.              }  
  39.          }  
  40.          return sp;  
  41.      }  
  42. }  

 

   2、 SharedPreferences 不过是个接口,它定义了一些操作xml文件的方法,其真正实现类为SharedPreferencesImpl ,该类是

    ContextIml的内部类,该类如下:

 

[java] view plaincopyprint?
  1. //soga,这种形式我们在分析Context ContextIml时接触过   
  2. //SharedPreferences只是一种接口,其真正实现类是SharedPreferencesImpl类  
  3. private static final class SharedPreferencesImpl implements SharedPreferences{  
  4.      private Map mMap;  //保存了该文件序列化结果后的操作, 键值对形式  
  5.        
  6.      //通过key值获取对应的value值  
  7.      public String getString(String key, String defValue) {  
  8.          synchronized (this) {  
  9.              String v = (String)mMap.get(key);  
  10.              return v != null ? v : defValue;  
  11.          }  
  12.      }  
  13.      ...  
  14.      //获得该SharedPreferencesImpl对象对应的Edito类,对数据进行操作  
  15.      public final class EditorImpl implements Editor {  
  16.          private final Map<String, Object> mModified = Maps.newHashMap(); //保存了对键值变化的集合  
  17.      }  
  18. }  

 



       基本上获取SharedPreferences 对象就是这么来的,关于Context里的更多方法请大家参照源代码认真学习吧。


转自:http://blog.csdn.net/qinjuning/article/details/7310620


Context字面意思上下文,位于framework package的android.content.Context中,其实该类为LONG型,类似Win32中的Handle句柄,很多方法需要通过 Context才能识别调用者的实例,比如说Toast的第一个参数就是Context,一般在Activity中我们直接用this代替,代表调用者的 实例为Activity,而到了一个button的onClick(View view)等方法时,我们用this时就会报错,所以我们可能使用ActivityName.this来解决,主要原因是因为实现Context的类主要有Android特有的几个模型,Activity、Service以及BroadcastReceiver。

Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型。同时启动应用级的操作,如启动Activity,broadcasting和接收intents。

 
两种类型的Context
在android中context可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是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. 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化
application context
我们可以使用application context。application context伴随application的一生,与activity的生命周期无关。application context可以通过Context.getApplicationContext或者Activity.getApplication方法获取。
而制造Application context的方法在这里可以找到
http://stackoverflow.com/questions/708012/android-how-to-declare-global-variables/708317#708317
 
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();
    ...
  }

下面介绍Context的一些get方法,通过这些get方法可以获取应用环境全局信息:

1.public abstract Context getApplicationContext ()

Return the context of the single, global Application object of the current process.

2.public abstract ApplicationInfo getApplicationInfo ()

Return the full application info for this context's package.

3.public abstract ContentResolver getContentResolver ()

Return a ContentResolver instance for your application's package.

4.public abstract PackageManager getPackageManager ()

Return PackageManager instance to find global package information.

5.public abstract String getPackageName ()

Return the name of this application's package.

6.public abstract Resources getResources ()

Return a Resources instance for your application's package.

7.public abstract SharedPreferences getSharedPreferences (String name, int mode)

Retrieve and hold the contents of the preferences file 'name', returning a SharedPreferences through which you can retrieve and modify its values. Only one instance of the SharedPreferences object is returned to any callers for the same name, meaning they will see each other's edits as soon as they are made.

8.public final String getString (int resId)

Return a localized string from the application's package's default string table.

9.public abstract Object getSystemService (String name)

Return the handle to a system-level service by name. The class of the returned object varies by the requested name. Currently available names are:

参考博客:http://blog.chinaunix.net/space.php?uid=17102734&do=blog&id=2830227

              http://blog.csdn.net/zhangqijie001/article/details/5891682

转载:http://blog.csdn.net/janronehoo/article/details/7348566

在android中常常会遇到与context有关的内容

浅论一下context : 在语句 AlertDialog.Builder builder = new AlertDialog.Builder(this); 中,要求传递的 参数就是一个context,在这里我们传入的是this,那么这个this究竟指的是什么东东呢? 这里的this指的是Activity.this,是这个语句所在的Activity的this,是这个Activity 的上下文。网上有很多朋友在这里传入this.getApplicationContext(),这是不对的。 AlertDialog对象是依赖于一个View的,而View是和一个Activity对应的。 于是,这里涉及到一个生命周期的问题,this.getApplicationContext()取的是这个应 用程序的Context,Activity.this取的是这个Activity的Context,这两者的生命周期是不同 的,前者的生命周期是整个应用,后者的生命周期只是它所在的Activity。而AlertDialog应 该是属于一个Activity的,在Activity销毁的时候它也就销毁了,不会再存在;但是,如果传 入this.getApplicationContext(),就表示它的生命周期是整个应用程序,这显然超过了它 的生命周期了。 所以,在这里我们只能使用Activity的this。

new AlertDialog.Builder(getApplicationContext())时发生错误:

E/AndroidRuntime(5844): android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application

于是查了查:

getApplicationContext() 生命周期是整个应用,应用摧毁它才摧毁 Activity.this的context属于activity ,activity 摧毁他就摧毁

activity.this要返回一个activity,而getApplicationContext()就不一定返回一个activity


getApplicationContext() 返回应用的上下文,生命周期是整个应用,应用摧毁它才摧毁
Activity.this的context 返回当前activity的上下文,属于activity ,activity 摧毁他就摧毁

getBaseContext()  返回由构造函数指定或setBaseContext()设置的上下文



  相关解决方案