A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始


Context完全解析,Context细节精华全掌握,用Context不迷糊

涨薪机密——潮流新技术、新框架资源以后不定期更新,  


        上一篇说完了Context的类型与数量接下来深入说说Application Context的设计。

第二篇:Application Context的设计

        基本上每一个应用程序都会有一个自己的Application,并让它继承自系统的Application类,然后在自己的Application类中去封装一些通用的操作。其实这并不是Google所推荐的一种做法,因为这样我们只是把Application当成了一个通用工具类来使用的,而实际上使用一个简单的单例类也可以实现同样的功能。但是根据观察,有太多的项目都是这样使用Application的。当然这种做法也并没有什么副作用,只是说明还是有不少人对于Application理解的还有些欠缺。那么这里我们先来对Application的设计进行分析,讲一些大家所不知道的细节,然后再看一下平时使用Application的问题。
       首先新建一个MyApplication并让它继承自Application,然后在AndroidManifest.xml文件中对MyApplication进行指定,如下所示:

  1. <application  
  2.     android:name=".MyApplication"  
  3.     android:allowBackup="true"  
  4.     android:icon="@drawable/ic_launcher"  
  5.     android:label="@string/app_name"  
  6.     android:theme="@style/AppTheme" >  
  7.     ......  
  8. </application>
复制代码
       指定完成后,当我们的程序启动时Android系统就会创建一个MyApplication的实例,如果这里不指定的话就会默认创建一个Application的实例。
      前面提到过,现在很多的Application都是被当作通用工具类来使用的,那么既然作为一个通用工具类,我们要怎样才能获取到它的实例呢?如下所示:
  1. public class MainActivity extends Activity {
  2.         @Override
  3.         protected void onCreate(Bundle savedInstanceState) {
  4.                    super.onCreate(savedInstanceState);
  5.                    setContentView(R.layout.activity_main);
  6.                     MyApplication myApp = (MyApplication) getApplication();
  7.                    Log.d("TAG", "getApplication is " + myApp);
  8.             }
  9. }
复制代码
         可以看到,代码很简单,只需要调用getApplication()方法就能拿到我们自定义的Application的实例了,打印结果如下所示:


        那么除了getApplication()方法,其实还有一个getApplicationContext()方法,这两个方法看上去好像有点关联,那么它们的区别是什么呢?我们将代码修改一下:
  1. public class MainActivity extends Activity {  
  2.       
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         MyApplication myApp = (MyApplication) getApplication();  
  8.         Log.d("TAG", "getApplication is " + myApp);  
  9.         Context appContext = getApplicationContext();  
  10.         Log.d("TAG", "getApplicationContext is " + appContext);  
  11.     }  
  12. }
复制代码
       同样,我们把getApplicationContext()的结果打印了出来,现在重新运行代码,结果如下图所示:


      咦?好像打印出的结果是一样的呀,连后面的内存地址都是相同的,看来它们是同一个对象。其实这个结果也很好理解,因为前面已经说过了,Application本身就是一个Context,所以这里获取getApplicationContext()得到的结果就是MyApplication本身的实例。
      那么,既然这两个方法得到的结果都是相同的,那么Android为什么要提供两个功能重复的方法呢?实际上这两个方法在作用域上有比较大的区别。getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法了,如下所示:
  1. public class MyReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         MyApplication myApp = (MyApplication) context.getApplicationContext();  
  6.         Log.d("TAG", "myApp is " + myApp);  
  7.     }  
  8. }  
复制代码

       也就是说,getApplicationContext()方法的作用域会更广一些,任何一个Context的实例,只要调用getApplicationContext()方法都可以拿到我们的Application对象。
       那么更加细心的朋友会发现,除了这两个方法之外,其实还有一个getBaseContext()方法,这个baseContext又是什么东西呢?我们还是通过打印的方式来验证一下:

        这次得到的是不同的对象了,getBaseContext()方法得到的是一个ContextImpl对象。这个ContextImpl是不是感觉有点似曾相识?回去看一下Context的继承结构图吧,ContextImpl正是上下文功能的实现类。也就是说像Application、Activity这样的类其实并不会去具体实现Context的功能,而仅仅是做了一层接口封装而已,Context的具体功能都是由ContextImpl类去完成的。那么这样的设计到底是怎么实现的呢?我们还是来看一下源码吧。因为Application、Activity、Service都是直接或间接继承自ContextWrapper的,我们就直接看ContextWrapper的源码,如下所示:
  1. /**
  2. * Proxying implementation of Context that simply delegates all of its calls to
  3. * another Context.  Can be subclassed to modify behavior without changing
  4. * the original Context.
  5. */  
  6. public class ContextWrapper extends Context {  
  7.     Context mBase;  
  8.       
  9.     /**
  10.      * Set the base context for this ContextWrapper.  All calls will then be
  11.      * delegated to the base context.  Throws
  12.      * IllegalStateException if a base context has already been set.
  13.      *  
  14.      * @param base The new base context for this wrapper.
  15.      */  
  16.     protected void attachBaseContext(Context base) {  
  17.         if (mBase != null) {  
  18.             throw new IllegalStateException("Base context already set");  
  19.         }  
  20.         mBase = base;  
  21.     }  
  22.   
  23.     /**
  24.      * @return the base context as set by the constructor or setBaseContext
  25.      */  
  26.     public Context getBaseContext() {  
  27.         return mBase;  
  28.     }  
  29.   
  30.     @Override  
  31.     public AssetManager getAssets() {  
  32.         return mBase.getAssets();  
  33.     }  
  34.   
  35.     @Override  
  36.     public Resources getResources() {  
  37.         return mBase.getResources();  
  38.     }  
  39.   
  40.     @Override  
  41.     public ContentResolver getContentResolver() {  
  42.         return mBase.getContentResolver();  
  43.     }  
  44.   
  45.     @Override  
  46.     public Looper getMainLooper() {  
  47.         return mBase.getMainLooper();  
  48.     }  
  49.       
  50.     @Override  
  51.     public Context getApplicationContext() {  
  52.         return mBase.getApplicationContext();  
  53.     }  
  54.   
  55.     @Override  
  56.     public String getPackageName() {  
  57.         return mBase.getPackageName();  
  58.     }  
  59.   
  60.     @Override  
  61.     public void startActivity(Intent intent) {  
  62.         mBase.startActivity(intent);  
  63.     }  
  64.       
  65.     @Override  
  66.     public void sendBroadcast(Intent intent) {  
  67.         mBase.sendBroadcast(intent);  
  68.     }  
  69.   
  70.     @Override  
  71.     public Intent registerReceiver(  
  72.         BroadcastReceiver receiver, IntentFilter filter) {  
  73.         return mBase.registerReceiver(receiver, filter);  
  74.     }  
  75.   
  76.     @Override  
  77.     public void unregisterReceiver(BroadcastReceiver receiver) {  
  78.         mBase.unregisterReceiver(receiver);  
  79.     }  
  80.   
  81.     @Override  
  82.     public ComponentName startService(Intent service) {  
  83.         return mBase.startService(service);  
  84.     }  
  85.   
  86.     @Override  
  87.     public boolean stopService(Intent name) {  
  88.         return mBase.stopService(name);  
  89.     }  
  90.   
  91.     @Override  
  92.     public boolean bindService(Intent service, ServiceConnection conn,  
  93.             int flags) {  
  94.         return mBase.bindService(service, conn, flags);  
  95.     }  
  96.   
  97.     @Override  
  98.     public void unbindService(ServiceConnection conn) {  
  99.         mBase.unbindService(conn);  
  100.     }  
  101.   
  102.     @Override  
  103.     public Object getSystemService(String name) {  
  104.         return mBase.getSystemService(name);  
  105.     }  
  106.   
  107.     ......  
  108. }  
复制代码
       由于ContextWrapper中的方法还是非常多的,我就进行了一些筛选,只贴出来了部分方法。那么上面的这些方法相信大家都是非常熟悉的,getResources()、getPackageName()、getSystemService()等等都是我们经常要用到的方法。那么所有这些方法的实现又是什么样的呢?其实所有ContextWrapper中方法的实现都非常统一,就是调用了mBase对象中对应当前方法名的方法。

       那么这个mBase对象又是什么呢?来看第16行的attachBaseContext()方法,这个方法中传入了一个base参数,并把这个参数赋值给了mBase对象。而attachBaseContext()方法其实是由系统来调用的,它会把ContextImpl对象作为参数传递到attachBaseContext()方法当中,从而赋值给mBase对象,之后ContextWrapper中的所有方法其实都是通过这种委托的机制交由ContextImpl去具体实现的,所以说ContextImpl是上下文功能的实现类是非常准确的。
      那么另外再看一下我们刚刚打印的getBaseContext()方法,在第26行。这个方法只有一行代码,就是返回了mBase对象而已,而mBase对象其实就是ContextImpl对象,因此刚才的打印结果也得到了印证。


       上一篇:Context的类型与数量
       下一篇:使用Application的问题

其他精华资源推荐:

93 个回复

正序浏览
wujianming 来自手机 注册黑马 2016-12-10 21:06:19
94#
楼主厉害,说的好明确。
回复 使用道具 举报
dhml 中级黑马 2016-12-10 16:47:33
93#
666666666666到飞起
回复 使用道具 举报
解释的 太完美了!
回复 使用道具 举报
收下了,谢谢啦
回复 使用道具 举报
绝壁的大牛啊~
回复 使用道具 举报
{:2_30:}
回复 使用道具 举报
真的很好
回复 使用道具 举报
非常有用。谢谢分享。
回复 使用道具 举报
高大上
回复 使用道具 举报
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈
回复 使用道具 举报
阿拉啦啦啦阿拉啦啦啦啦啦啦啦
回复 使用道具 举报
谢谢楼主分享
回复 使用道具 举报
顶一个,很好用
回复 使用道具 举报
未来肯定会用到的
回复 使用道具 举报
这是从哪里来的分享呢?
回复 使用道具 举报
一看就是大神 受教了
回复 使用道具 举报
确实有用啊谢谢
回复 使用道具 举报
总结的不错,赞赞赞
回复 使用道具 举报
向大神学习 坚持,再苦再累也要扛下去.
回复 使用道具 举报
12345下一页
您需要登录后才可以回帖 登录 | 加入黑马