編輯:關於Android編程
如果說Android源碼中哪個地方裝飾模式應用的最明顯的話,那肯定是非ContextWrapper莫屬了,ContextWrapper是一個透明的經典的裝飾模式。本文將通過裝飾器模式分析Context源碼結構。本文基於的Android源碼是(android 5.0.0)。首先先介紹一下裝飾模式。
裝飾模式動態地給對象添加額外的職責,就添加功能來說,它比子類的方式更加靈活。
class Component{
public void operate1(){
//do operate1
System.out.println("operate1");
}
}
class ComponentDecorator extends Component{
private Component mComponent;
public ComponentDecorator(Component component){
mComponent = component;
}
public void operate1(){
// 添加相關的職責
System.out.println("print before");
mComponent.operate1();
}
}
class ComponentChild{
public void operate1(){
//do operate1
System.out.println("child operate1");
}
}
public static void main(String [] args){
ComponentChild component = new ComponentChild();
ComponentDecorator decorator = new ComponnentDecorator(component);
decorator.operate1();
}
上面的代碼就簡單實現了裝飾模式,通過把被裝飾的對象作為裝飾器的成員變量,當調用裝飾器的操作的時候,裝飾器還是會調用被裝飾對象的操作,但是裝飾器可以給對應的操作添加相關功能。裝飾器透明的實現就是跟被裝飾對象的接口一致,不添加新的接口,但是現在更多的實現方式是半透明的。裝飾模式與子類實現方式比,它能夠裝飾被裝飾對象的各種子類,甚至可以裝飾裝飾器對象(裝飾器之間互相裝飾),這樣比子類化添加功能更加靈活。如果用子類的方式,將會導致非常多的子類。
下面介紹一下Android源碼中裝飾器的使用:ContextWrapper(裝飾器有個別名Wrapper)。先看UML圖。
Context是一個全局應用環境接口,具體的實現由Android系統完成。它能訪問應用資源,並且能夠啟動Activity,Service,接收broadcast。
它是一個抽象類,裡面包含了各種方法的聲明,應該將它作為一個接口類來看待。
這是一個Context的包裝器,裡面包含了一個mBase,ContextWrapper就是包裝了mBase。而
ContextThemeWrapper這個是包含主題的裝飾器,而Activity是它的子類。Application, Service, ReceiverRestrictedContext都是ContextWrapper的子類。每一個應用都會有一個Application。Android源碼中已經有的Application有EmailApplication,LauncherApplication, Browser等等。
這個是真正實現Context的類,Context是為應用環境類,它包含了跟環境相關的各種操作:getResource(資源管理,包含了getAssets,layout, string,drawable等等), getPackageManager(包管理器), getContentResolver(用於獲取內容模型,比如訪問ContentProvider), startActivity(啟動Activity), ***Service(包含一系列Service相關操作,start,bind,unbind,stop), registerBroadcast/unregisterBroadcast/sendBroadcast(Broadcast相關)
這裡簡單介紹一下這幾個關鍵方法的實現方式
這是獲取資源的接口,得到Resource對象。ContextImpl裡面有個mResource成員變量,android裡面由mResource去讀取資源。mResource由ResourceManager生成。現在很多Android資源動態加載是直接通過下面這種方式實現的。
AssetManager assets = AssetManager.class.newInstance();
try{
Method method = AssetManager.class.getMethod("addAssetPath",String.class);
method.invoke(assets,dexPath); //dexPath 表示動態dex包路徑
}catch(Exception e){
e.printStackTrace
}
r = new Resources(assets,getResources.getDisplayMetrics(),getResources.getConfiguration());
不過assets的addAssetPath是一個隱藏函數,需要通過反射去調用。
其實Android的資源框架大致就是如下過程:
- 根據layout,values,drawable等生成對應的ID,保存在R.id文件下面,而Android自帶的資源在com.android.R.id裡面。assets不會賦予id。
- Android用不同的資源目錄後綴來適配不同的語言環境,屏幕大小,比如說drawable-en-w360。Android有一個對應的表,按照順序匹配,對於drawable,本著不超過對應手機的參數的最大值來匹配。Android在打包的時候會將根據這些參數建立一個索引。
- 打包到resources.arsc文件中
運行的時候應用讀取過程
- 先去掉跟手機環境完全不匹配的資源目錄,比如現在是中文環境,則去掉en目錄
- assets由AssetsManager來訪問。其他對應的資源根據MMC表的順序,按照索引表一個一個的維度來匹配,篩選,直到找到對應的資源。如果沒有找到則拋出異常。
上述過程如果想要詳細了解可參見羅升陽的博客
返回包管理器,包管理器可以管理安裝在系統中的應用程序包相關的各種信息。可以添加權限,可以查看對應包名的包信息,指定組件的組件信息,安裝應用包等等。Android裡面不同的應用程序報名絕對不同相同,包就對應了應用程序。所以從這裡去理解PackageManager,他就是管理涉及到包管理(應用級相關的信息處理)的管理器。
返回一個ContentResolver,ContentResolver是用於獲取Content模型的,像ContentProvider就是通過ContentResolver來訪問。
Context包含了Activity的啟動操作,Activity的啟動過程簡單來說就是經過下面幾個步驟:
1. mMainThread.instrumentation.execStartActivity
mMainThread是ActivityThread類型,它是管理著應用程序主線程的執行,像調度Activity,Broadcast等,instrumentation是一個Instrumentation類型,他是Android測試框架,在這裡是為了監控Activity的活動,裡面會有一個ActivityMonitor去監控被調用的Activity,用ActivityMonitor也可以等待一個指定的Activity啟動。也可以阻止一個Activity的啟動。
通過這種方式啟動Activity主要就是為了監控Activity的啟動活動,在測試中將會大有用處。啟動完後,也可以檢查啟動後的返回組件信息。
2. 在execStartActivity的函數中,進行完監控後,就是通過ActivityManagerNative來啟動指定的Activity。
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
看到Native其實就可以想到它是通過一種IPC機制來訪問的了,具體是ActivityManagerProxy通過Binder進行IPC。
由Binder機制來訪問ActivityManagerService,ActivityManagerService是進行Activity,Service, Broadcast管理的核心服務。
由ActivityManagerService.startActivity 會經過mStackSupervisor 以及 ActivityStack 進行相關處理
然後經過ApplicationThread, 最後到主線程的Handler(H)啟動Activity
Service除了包含startService,stopService,bindService,unbindService。Service最終也是在ActivityManagerService中管理,但是Service不同的,在ActivityManagerService裡面管理的是是ActiveServices。但是最終控制Service的流程還是會到達Handler中
Context中有sendBroadcast, registerReceiver, unregisterReceiver。對應的都會通過ActivityManagerNative調用ActivityManagerService的相關函數。broadcastIntent是處理sendBroadcast的, ActivityManagerService中也有registerReceiver, unregisterReceiver。ActivityManagerService裡面保存有一個Broadcast的ReceiverList,注冊的時候將Receiver保存起來,發送廣播的時候,根據保存的IntentFilter來找到對應Receiver,另外BroadCastReceiver還有sticky,是注冊的時候如果符合已有的sticky廣播,則立刻發送給Receiver。
對於Activity,Service啟動流程,可以去參考羅升陽的博客,他的博客是基於早期的Android版本的,其實很多地方還是有很多變化了。所以如果想了解最新的源碼,建議參考博客,自己去閱讀源碼。下面討論一下Context這部分用裝飾模式的優勢
其實看了Context的源碼結構,知道他是使用了裝飾模式,但是為什麼這個部分使用裝飾模式呢?在這裡裝飾模式能夠很好地添加職責,比如遇到Receiver的時候,ReceiverRestrictedContext就能夠限制Receiver調用startService,讓應用程序的Receiver不能夠啟動Service。通過實現裝飾器,能夠提供各種各樣的裝飾,並且也為Android應用程序組件提供了應用環境。
我們似乎可以考慮用繼承去實現Context結構,但是如果用繼承的方式給Context添加功能,則將Application, ThemeContext,Service等等直接集成自ContextImpl,假如我們需要一種新的Context實現方式,則會變得非常麻煩,新的ContextImpl實現了後,Application又得去繼承新的ContextImpl,一方面麻煩,另外一方面導致子類樹集成非常龐大。現在用裝飾模式,直接將ContextWrapper裝飾的對象替換掉就可以了。
我們的Context是需要不斷在外面添加新的職責裝飾的,而策略模式是將系統的內部替換掉,假如說我們需要的是多種Context的實現,則可以采用策略模式。Context是Android應用環境,在Android中,Android的應用環境是確定了的,它在對不同的應用程序,Android的應用環境是不會變化的。
不過話說回來,裝飾模式可以添加不同的實現,Android裡面也有MockContext,但那是用來測試的,這個地方也可以看作是用了策略的思想,但正常運行的時候是主要是側重於不同的裝飾。這其實也是裝飾的好處。
縱然傷心,也不要愁眉不展,因為你不知是誰會愛上你的笑容 –泰戈爾《飛鳥集》
Manifest 文件 詳解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/20899281
微博開發遇到很多bug 總結一下 我遇到BUG (1) :sso package or singn error 出現這個問題 是我沒有在 博客中填寫正確的包
在Android開發中,我們不可避免的會做到注冊功能,而現在的注冊大多數都是用手機去注冊的,那麼注冊的時候都會要求用獲取驗證碼的方式去驗證,我們接下來就來實戰一下自定義
QQ厘米秀是騰訊手機QQ推出的全新功能玩法,厘米秀添加了讓人眼前一亮的人物動作互動功能,用戶可以通過手機QQ厘米秀的窗口與好友互動。還有一個故事卡線索功能,