Application Context的设计
基本上每一个应用程序都会有一个自己的Application,并让它继承自系统的Application类,然后在自己的Application类中去封装一些通用的操作。其实这并不是Google所推荐的一种做法,因为这样我们只是把Application当成了一个通用工具类来使用的,而实际上使用一个简单的单例类也可以实现同样的功能。但是根据观察,有太多的项目都是这样使用Application的。当然这种做法也并没有什么副作用,只是说明还是有不少人对于Application理解的还有些欠缺。那么这里我们先来对Application的设计进行分析,讲一些大家所不知道的细节,然后再看一下平时使用Application的问题。
<application android:name=".MyApplication" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > ...... </application
指定完成后,当我们的程序启动时Android系统就会创建一个MyApplication的实例,如果这里不指定的话就会默认创建一个Application的实例。
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyApplication myApp = (MyApplication) getApplication(); Log.d("TAG", "getApplication is " + myApp); } }
可以看到,代码很简单,只需要调用getApplication()方法就能拿到我们自定义的Application的实例了,打印结果如下所示:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyApplication myApp = (MyApplication) getApplication(); Log.d("TAG", "getApplication is " + myApp); Context appContext = getApplicationContext(); Log.d("TAG", "getApplicationContext is " + appContext); } }
同样,我们把getApplicationContext()的结果打印了出来,现在重新运行代码,结果如下图所示:
那么,既然这两个方法得到的结果都是相同的,那么Android为什么要提供两个功能重复的方法呢?实际上这两个方法在作用域上有比较大的区别。getApplication()方法的语义性非常强,一看就知道是用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法了,如下所示:
public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { MyApplication myApp = (MyApplication) context.getApplicationContext(); Log.d("TAG", "myApp is " + myApp); } }
也就是说,getApplicationContext()方法的作用域会更广一些,任何一个Context的实例,只要调用getApplicationContext()方法都可以拿到我们的Application对象。
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是上下文功能的实现类是非常准确的。