Android源碼適配器模式---Activity類結構

來源:互聯網
上載者:User

標籤:

  其實適配器模式在Android源碼中非常多,而從整體的源碼角度上來看Activity的結構就是一種適配器模式。從這個角度上面看Activity,對Activity和應用程式層架構會有更加深入的理解。

  適配器模式

  意圖

  將一個介面轉換為使用者需要的另外一個介面,適配器模式使得原本由於介面不相容不能一起工作的那些類可以一起工作。

  UML圖

  適配器模式有兩種模式,UML分別如下:

  

 

  

 

  第一種是直接繼承已經有的介面適配目標介面,而第二種是引用已有的介面適配目標介面。

  範例程式碼

  interface Target{

  void request();

  }

  class Adaptee{

  public void specialRequest(){

  System.out.println("special from adaptee");

  }

  }

  class Adapter extends Adaptee implements Target{

  public void request(){

  //do something to implements request

  specialRequest();

  }

  }

  public static final void main(String args[]){

  Target target = new Adapter();

  target.request();

  }123456789101112131415161718192021123456789101112131415161718192021

  上面是第一種適配器模式的簡單程式碼範例,通過繼承已有的類來適配,另外一種組合的方式如下:

  interface Target{

  void request();

  }

  class Adaptee{

  public void specialRequest(){

  System.out.println("special from adaptee");

  }

  }

  class Adapter implements Target{

  private Adaptee adaptee ;

  public void Adapter(Adaptee adaptee){

  this.adaptee = adaptee;

  }

  public void request(){

  //do something to implements request

  adaptee.specialRequest();

  }

  }

  public static final void main(String args[]){

  Target target = new Adapter(new Adaptee());

  target.request();

  }1234567891011121314151617181920212223242512345678910111213141516171819202122232425

  兩種方式類適配器和對象適配器。

  Activity與適配器模式

  Activity是Android的核心組件,它是負責應用UI的組件,可以說是Android四大組件中最重要,使用最多,最複雜的組建。它的源碼也相當地龐大。從適配器的角度上來看,Activity適配了多個介面,先看一下它的類結構圖:

  

 

  將Activity看成是適配器模式初看可能會有點牽強。但是ContextThemeWrapper是表示主題的環境類,Context可以翻譯為應用環境,但是對於需要顯示UI的一個應用組建除了應用環境外,還需要適應其他的內容資訊,比如Window,比如KeyEvent等等。

  拿視窗系統舉例。Android中有Window管理系統,但是視窗系統需要與的Window.Callback介面,但是現在是有了Context,組建需要Window.Callback介面,這樣建立Activity(這個是Adapter)實現Window.Callback介面,並且繼承ContextThemeWrapper,將ContextWrapper與Window.Callback協作,讓Context與Window一起工作。Window.Callback只是Activity適配的其中一個介面,下面分別介紹類結構的每一個部分。

  ContextThemeWrapper

  這是一個包含主題的Context裝飾器,本身ContextWrapper是一個裝飾器模式,在Android中,四大組建都是ContextWrapper的子類,四大組建都需要應用環境。關於這部分可以看我這篇文章Android源碼裝飾模式—ContextWrapper。需要理解的是Context是一個應用環境類型,Context包含了各種跟應用環境相關的資訊,可以用來與應用系統打交道的。

  Window.Callback, Window.OnWindowDismissedCallback

  Window.Callback 這個介面包含了很多介面函數,上面的UML圖中只包含了部分介面,全部的介面類可見下面的Outline:

  

 

  這個介面是視窗的回調介面,主要分為螢幕事件觸發,按鍵事件觸發,Panel相關的View建立與Prepare,Menu的回調,Window的變化回調,SearchRequest的回調,以及ActionMode的回調。

  Window.OnWindowDismissedCallback是一個hide類,是無法通過API調用的,是當視窗消失(Window系統移除)的時候的回調介面。Activity的實現也很簡單,直接finish掉自己。

  /**

  * Called when the main window associated with the activity has been dismissed.

  * @hide

  */

  @Override

  public void onWindowDismissed() {

  finish();

  }1234567812345678

  Callback,OnWindowDismissedCallback是Window與Activity互動的回調介面。

  初始部分代碼為:

  //Activity.java

  final void attach(Context context, ActivityThread aThread,

  Instrumentation instr, IBinder token, int ident,

  Application application, Intent intent, ActivityInfo info,

  CharSequence title, Activity parent, String id,

  NonConfigurationInstances lastNonConfigurationInstances,

  Configuration config, String referrer, IVoiceInteractor voiceInteractor) {

  attachBaseContext(context);

  mFragments.attachHost(null /*parent*/);

  mWindow = new PhoneWindow(this);

  mWindow.setCallback(this);

  mWindow.setOnWindowDismissedCallback(this);

  mWindow.getLayoutInflater().setPrivateFactory(this);

  ...

  12345678910111213141516171234567891011121314151617

  但實際上一個Window並不是只和一個Activity關聯,而是一個Window和一個Callback關聯,Activity也是Context,Android中Dialog裡面也包含了Window,Dialog也實現了Callback介面。一個應用環境中(Context)可能包含多個Window,也就會有多個Callback,只是Activity這種應用環境本身就實現了Callback介面。

  KeyEvent.Callback

  對應著Key事件的回調介面,當按下按鍵的時候,會回調該介面。主要是為了適配輸入系統。

  ComponentCallbacks2

  它是ComponentCallbacks的子介面,CompoentCallbacks包含下面兩個介面:

  void onConfigurationChanged(Configuration newConfig);

  void onLowMemory();123123

  ComponentCallbacks2新增了onTrimMemory介面。

  ComponentCallbacks是專門為Android組件使用的回調介面,Android組件都會實現該介面(目前變成了ConponentCallbacks2),當配置資訊變化,記憶體變化的時候,這些介面會被調用。調用這些介面的是ActivityThread(訊息迴圈中,收到變化訊息時),ViewRootImpl(在Window有變化的時候,ViewRootImpl負責與WindowManagerService通訊)等。該介面是為了適配系統資訊管理部分。

  這裡有兩個跟記憶體相關的介面,這其實是為幫應用應對Android記憶體滿負荷,提醒應用程式做一些釋放記憶體處理,如果佔用記憶體過大,應用將會更容易被殺死。具體可以看LowMemoryKiller的介紹。

  OnCreateContextMenuListener

  Android操作功能表: 當給一個View註冊了操作功能表後,對這個View長按2秒,會彈出一個浮動的菜單。OnCreateContextMenuListener 它只有一個介面函數:

  public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {

  }1212

  當View的Context menu被建立的時候,該介面的會被調用,用於擷取Menu(作為實現改介面的Activity來講,是設定Menu)。在Activity中,與這個介面函數對應的函數是onContextItemSelected,而該函數是繼承自Window.Callback介面的onMenuItemSelected函數:

  public boolean onMenuItemSelected(int featureId, MenuItem item) {

  CharSequence titleCondensed = item.getTitleCondensed();

  switch (featureId) {

  case Window.FEATURE_OPTIONS_PANEL:

  if(titleCondensed != null) {

  EventLog.writeEvent(50000, 0, titleCondensed.toString());

  }

  if (onOptionsItemSelected(item)) {

  return true;

  }

  ...

  case Window.FEATURE_CONTEXT_MENU:

  if(titleCondensed != null) {

  EventLog.writeEvent(50000, 1, titleCondensed.toString());

  }

  if (onContextItemSelected(item)) { //這裡

  return true;

  }

  return mFragments.dispatchContextItemSelected(item);

  default:

  return false;

  }

  }1234567891011121314151617181920212223242512345678910111213141516171819202122232425

  我們平時監聽普通的Menu的函數onOptionsItemSelected也是由onMenuItemSelected調用的。

  另外一邊View中顯示ContextMenu的函數是showContextMenu:

  public boolean showContextMenu() {

  return getParent().showContextMenuForChild(this);

  }123123

  ViewGroup的showContextMenuForChild為:

  public boolean showContextMenuForChild(View originalView) {

  return mParent != null && mParent.showContextMenuForChild(originalView);

  }123123

  getParent()最終會到DecorView,DecorView中建立了ContextMenu。然後調用View的createContextMenu方法,最終使用mOnCreateContextMenuListener擷取Menu:

  public void createContextMenu(ContextMenu menu) {

  ContextMenuInfo menuInfo = getContextMenuInfo();

  // Sets the current menu info so all items added to menu will have

  // my extra info set.

  ((MenuBuilder)menu).setCurrentMenuInfo(menuInfo);

  onCreateContextMenu(menu);

  ListenerInfo li = mListenerInfo;

  if (li != null && li.mOnCreateContextMenuListener != null) {

  li.mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo);

  }

  // Clear the extra information so subsequent items that aren‘t mine don‘t

  // have my extra info.

  ((MenuBuilder)menu).setCurrentMenuInfo(null);

  if (mParent != null) {

  mParent.createContextMenu(menu);

  }

  }123456789101112131415161718192021123456789101112131415161718192021

  DecorView在PhoneWindow中,Menu其實會由Window統一管理,響應Item的點擊事件的介面是一致的(Window.Callback.onMenuItemSelected),另外ContextMenu實際上顯示出來的就是一個Dialog。但由於ContextMenu是跟View對應的,所以有了OnCreateContextMenuListener介面,它是用於當View需要建立ContextMenu的時候,方便指定ContextMenu的內容。

  LayoutInflater.Factory2

  這個介面只有一個介面函數:

  public View onCreateView(View parent, String name, Context context, AttributeSet attrs);11

  它繼承自Factory:

  public interface Factory {

  /**

  * Hook you can supply that is called when inflating from a LayoutInflater.

  * You can use this to customize the tag names available in your XML

  * layout files.

  *

  *

 

  * Note that it is good practice to prefix these custom names with your

  * package (i.e., com.coolcompany.apps) to avoid conflicts with system

  * names.

  */

  public View onCreateView(String name, Context context, AttributeSet attrs);

  }1234567891011121312345678910111213

  用於跟LayoutInflater系統互動,為了適配LayoutInflater系統。實現改介面,可以在Inflater的時候,解析XML中自訂的Tag。該介面為LayoutInflater調用,而LayoutInflater的實現為PhoneLayoutInflater。對於Window和LayoutInflater結構可以看這篇Android源碼抽象工廠—IPolicy。

  除了Activity外,Application和Service都實現了ComponnentCallbacks介面,繼承了ContextWrapper,其實都可以用類適配器模式看待。

  設計思考

  本身應用組件都應該是一種應用環境(Context),但是又需要滿足Window等系統的回調需求,我們平時可能直接單獨實現Window.Callback介面,但是將Activity實現Window.Callback介面,那麼Activity會更加具有整體性,不過設計意圖在這裡思考過多感覺有點太牽強。

  總結

  從Android應用程式層源碼整理來看,Activity的類結構完全可以看成是一種適配器模式,在基於應用環境(Context)的情況下,去滿足LayoutInflater系統(LayoutInflater.Factory2),Window系統(Window.Callback,Window.OnWindowDismissedCallback),輸入系統(KeyEvent.Callback)的介面需求,另外ComponnentCallbacks更是ActivityThread和ViewRootImpl需要的介面。通過適配器模式來看Activity,對於Activity,對於Activity與其他部分的互動,對於應用程式層架構會有更好的理解。另外再有裝飾模式看Context,對於整個應用程式層結構會更清晰。

Android源碼適配器模式---Activity類結構

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.