Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android框架設計模式(四)——Adapter Method

Android框架設計模式(四)——Adapter Method

編輯:關於Android編程

一、適配器模式介紹


適配器在平常在生活中是經常會用到的,特別是電子產品。像手機、電腦、家用電器都會用到適配器來轉換電壓的大小,以提供合適的電壓。適配器就是把原來不符合要求的電壓、電路信號轉換成合適的電壓和信號。簡單來說,它就是一個接口轉換器。

我們使用適配器的本質原因是:當我們的系統已經確定了一個標准,但已有的資源與現有標准不兼容,而且又無法或者不便修改這個標准的時候,就需要用適配器來使得不兼容的被使用方包裝成已有的標准供已有的系統使用


什麼是適配器模式?


定義:

適配器模式把一個類的接口變換成客戶端所期待的另一種接口,從而使得原本因接口不匹配(接口名、返回參數、輸入參數等)而無法一起工作的兩個類能夠在一起工作。

分類:

在軟件程序設計模式中,適配器模式分為兩種:類適配器、對象適配器。

類適配器

概念:通過實現目標Target接口以及繼承Adaptee(需要被適配的類)來實現接口轉換。把Adaptee的接口轉換成Target需要的接口。

UML圖

這裡寫圖片描述

對象適配濃ky"/kf/ware/vc/" target="_blank" class="keylink">vc8L3N0cm9uZz48L3A+DQo8cD48c3Ryb25nPrjFxO48L3N0cm9uZz6jutPrwODKysXkxvfSstK70fm1xMS/tcSjurDRQWRhcHRlZbXEvdO/2tequ7uzyVRhcmdldNDo0qq1xL3Tv9qho7Wryseyu82stcTKx6OsttTP88rKxeTG98rHyrnTw7T6wO252M+1wbS907W9QWRhcHRlZcDgo6y8tL2rQWRhcHRlZdf3zqpBZGFwdGVy1tC1xLPJ1LGjrNPJQWRhcHRlctf3zqq0+sDtwLTKtc/WQWRhcHRlZbXEuabE3KGjPC9wPg0KPHA+PHN0cm9uZz5VTUzNvDwvc3Ryb25nPjwvcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160405/20160405094328500.png" title="\" />


適配器應用於什麼場景?


1.系統需要使用現有的類,而此類的接口不符合系統的需要,即接口不兼容(最普通的適配器);

2.想要建立一個可以重復使用的類,用於將一些彼此之間關聯不大的一些類,包括一些可能在將來引進的類一起工作(Binder接口鏈接Activity與Service,Activity和Service兩者本就可以獨立運行,通過Binder接口,將Service端的服務適配成Activity端能夠識別的transact()方法)。

3.需要一個統一的輸出接口,而輸入端的類型不可預知(BaseAdapter就是將ListView、GridView等控件與多變的自定義View結合使用的適配器,輸入端類型為自定義多變的類型,而輸出端得到的總是View類型)


二、Android框架中的適配器模式應用


一般來說,現在都傾向於使用對象適配器,因為對象適配器能夠將被適配對象的方法隱藏,而如果使用類適配器的話,由於繼承的緣故,使得適配器也繼承了被適配對象的方法,這樣會暴露被適配對象。因此,Android中也是使用的對象適配器,通過代理的方法來實現適配。


范例一:ListView+BaseAdapter+自定義View


ListView與BaseAdapter的結合是適配器使用情景的第三種情況。即:需要一個統一的輸出接口,而輸入端的類型不可預知。
自定義View千變萬化,不同View接口又各異,因此通過適配器來提供統一的輸出接口,能夠使得ListView達到以【不變應萬變的效果】。

下面的UML圖,反應了一般的應用中Activity、ListView、BaseAdapter、自定義View之間的聯系。這裡的觀察者模型只是我自己為了簡化而使得BaseAdapter直接實現Observable(通知者接口),ListView實現Observer接口。實際上BaseAdapter與ListView還有更加深層次的繼承關系,而且觀察者模型是對象觀察者模型(即觀察者和通知者是作為類成員,通過代理實現的),而不是基於接口實現的觀察者模型。

通俗UML圖:

這裡寫圖片描述


關鍵代碼分析:

在BaseAdapter中,最重要的方法就是getView()。它是鏈接ListView容器和其中的ItemView的橋梁,getView(),是一個統一的接口,它固定返回的是View類型的參數,因此無論是哪種類型的自定義視圖,由於它們都是View的子類,因此ListView都能夠識別。這就是BaseAdapter中的適配方法,輸出是不變的,而輸入可以是變化的。

//getView()方法將自定義View進行適配,對各個子View進行裝配,最
//後將其裝入一個統一的View之中,返回給ListView。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder = null;
            if (convertView == null){
                holder = new ViewHolder();
                convertView = View.inflate(getBaseContext(),R.layout.activity_audiocable,null);
                holder.mImageCover = convertView.findViewById(R.id.img_cover);
                holder.mTextTitle = convertView.findViewById(R.id.txt_title);
                convertView.setTag(holder);
            }
            holder = (ViewHolder) convertView.getTag();
            holder.mTextTitle.setText((String) getItem(position).getTitle());
            holder.mImageCover.setImageResource((String) getItem(position).getCoverRes());
            return convertView;
 }

  class ViewHolder{
            TextView mTextTitle;
            ImageView mImageCover;
        }

范例二:Activity+Binder+MediaPlayer


Activity+Binder+Mediaplayer中,Binder充當適配器。這是適配器使用的第二種情況:想要建立一個可以重復使用的類,用於將一些彼此之間關聯不大的一些類,包括一些可能在將來引進的類一起工作。
我們啟動服務的過程中,Binder就是一個適配器,它提供了transact()方法給框架調用,onTransact()給應用類別實現,而後面的onTransact()方法就是適配方法,通過onTransact方法與不同的後台服務實現對接(多媒體控制、任務下載)。

通俗UML圖:

這裡寫圖片描述


關鍵代碼分析:

我們來看一看適配方法onTransact(),順便提一下,如果從框架的角度來看,則onTransact()方法是一個hook方法。onTransact()方法的任務就是負責與多媒體、以及後台任務進行對接。在onTransact()方法中就調用多媒體的控制方法(任務控制方法),從而為前端提供服務。
注意:Activity只知道通過Binder調用transact()方法,其余的都是通過onTransact()進行適配。

public class mp3Player_Adapter extends Binder{  
   private MediaPlayer mPlayer = null;  
   private Context ctx;    
   public mp3Player_Adapter(Context cx){  ctx= cx; }

//通過onTransact()方法完成調用play()和stop(),對MP3進行播放和
//停止的控制。
 @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws android.os.RemoteException { 

   reply.writeString(data.readString()+ " mp3");           
   if(code == 1)  
     this.play();        
   else if(code == 2)  
     this.stop();        
   return true;  
}  
//play()、stop()都是不能被客戶端識別的方法,需要通過onTransact()來進行適配
public void play(){   
  if(mPlayer != null) 
    return;   
 mPlayer = MediaPlayer.create(ctx, R.raw.test_cbr);   
 try { 
   mPlayer.start();   
 } catch (Exception e) {  
    Log.e("StartPlay", "error: " + e.getMessage(), e); 
 }  
}  
public void stop(){   
  if (mPlayer != null) {  
   mPlayer.stop(); 
   mPlayer = null; 
  } 
 }
} 

public class ac01 extends Activity implements OnClickListener {  
   private final int WC = 
   LinearLayout.LayoutParams.WRAP_CONTENT;  
   private final int FP = LinearLayout.LayoutParams.FILL_PARENT;  
   private Button btn, btn2, btn3;  
   public TextView tv;  
   private IBinder ib = null; 

public void onCreate(Bundle icicle) { 
 //......初始化
 startService(in);   //啟動服務
 //綁定服務
 bindService(in, mConnection, Context.BIND_AUTO_CREATE);  }

 //服務連接,用於通知Activity服務連接的情況,同時返回IBinder接口  
 private ServiceConnection mConnection = new ServiceConnection() {     
 public void onServiceConnected(ComponentName className, IBinder ibinder)  {  
   ib = ibinder;  
  }      
 public void onServiceDisconnected(ComponentName className) {}   
};

 public void onClick(View v) {    
 switch (v.getId()) {   
   case 101:    
    Parcel pc = Parcel.obtain();    
    Parcel pc_reply = Parcel.obtain();        
    pc.writeString("playing");    
    try { 
    //調用適配器方法操作MP3播放     
      ib.transact(1, pc, pc_reply, 0);  
      tv.setText(pc_reply.readString());    
    } catch (Exception e) {  
      e.printStackTrace(); 
      }    
  break;   
  case 102:    
   pc = Parcel.obtain();    
   pc_reply = Parcel.obtain();    
   pc.writeString("stop");    
   try { 
   //調用適配器方法操作MP3停止
     ib.transact(2, pc, pc_reply, 0);     
     tv.setText(pc_reply.readString());    
   } catch (Exception e) { 
    e.printStackTrace(); 
   }    
 break;   
 case 103: 
    finish();  
 break;   
 } 
}} 

三、適配器模式與其他模式的配合


在安卓的組件使用過程中,每一個組件都不止是使用一種設計模式而已,它們都是結合了許多設計模式而形成的。單獨的設計模式是無法設計出實用性、拓展性很好的組件。上面所舉的兩個Android適配器模式的應用也是包含了許多其他設計模式的,只是我們從不同的角度分析而已。下面是我對上面兩個例子進行的分析,因為沒有分析的很深(源代碼的繼承鏈和關系很復雜,只分析表面的幾層),就列出一些模式的組合,當然還有其他的模式,這裡我深究不來,也暫時不願深究。


情景一:適配器+觀察者+模板+策略+組合 = BaseAdapter+ListView+自定義View


整體UML圖

這裡寫圖片描述


模式分析(不同的視角決定)

適配器模式
從兼容的角度來看,那麼BaseAdapter中的四個方法(getItemId()、getItem()、getView()、getCount())都是適配器方法,他們鏈接了ListView和ListView鑲嵌的那些不確定的多變的自定義View對象。ListView的裝配和初始化只識別這四個方法,適配器將裝入的View對象按照這四個方法分別進行適配,輸出符合ListView要求的接口和參數。

觀察者模式
從數據更新的角度來看,那麼BaseAdapter與ListView之間存在著通知者/觀察者的聯系。BaseAdapter是一個通知者,而ListView是一個觀察者。BaseAdapter中存放著ListView的數據集合,每當數據集合有更新的時候,就會調用notifyDataChanged()方法,通知ListView進行更新。同時,在ListView更新完畢之後,也可以調用相應的方法反饋給BaseAdapter。

模板模式
從框架變與不變的分離的角度上來看,BaseAdapter、ListView的繼承鏈上都存在著模板模式。即他們都有框架父類,然而框架父類定義了【不變】的部分,然後應用程序(程序員繼承的類別)部分實現【變化】的部分。

策略模式
單從變化的分離來看,那麼BaseAdapter中還存在著策略模式的應用。getView()即是一個策略方法,對於不同的界面getView()會有不同的裝配策略,但是他們的結果和參數都一樣,它承擔著變化的部分。我們實現不同的界面就會繼承BaseAdapter實現不同的getView(),正好就是策略模式的體現。

組合模式
從自定義View本身的布局來看,自定義View是一個由不同布局和控件組合而形成的,它是組合模式的經典呈現,將系統提供給我們的(或者我們自己實現的一個View)組合成一個新的絢麗的視圖。從這個角度上來看,它便存在著組合模式的應用。


情景二:適配器+觀察者+模板 = Service + Activity + 自定義服務


整體UML圖

這裡寫圖片描述


模式分析(不同的視角決定)

適配器模式
從適配器模式的角度分析,Binder就是一個適配器類別,它通過一個transact()接口對多媒體(MP3、MP4)、下載任務等不同的對象進行適配,客戶端只需要調用transact(),接口的不兼容問題已經由Binder類別進行了適配。

觀察者模式
從Activity與Service通信的角度來看,Service與Activity還存在著通知者/觀察者關系。Service是通知者,Activity是觀察者。這點可以通過綁定服務體現,我們調用了bindService()後,當服務綁定了之後,Service會調用onBind方法返回一個IBinder接口,然後通過調用ServiceConnection中的onServiceConnect()方法返回一個IBinder接口給Activity客戶端。完成Activity與Service的通信。

模板模式
從框架中的變與不變分離的角度來看,Service和Activity中各自都存在著模板模式。Service中transact()是模板方法,而onTransact()是hook(卡隼)方法;Activity中callApplicationOnCreate是模板方法,而onCreate()是hook(卡隼)方法。我們程序實現的部分就是卡隼方法,而程序框架部分就是模板方法。

工廠模式
從對象的產生角度來看,Service與Activity中也各自都存在著工廠模式的應用。對於Activity來說,onCreate()就是一個工廠方法,它創建了系統運行需要的對象;對於Service來說,onCreate()也是一個工廠方法,它產生了Binder對象,然後再返回給客戶端Activity。


四、總結


適配器模式是一種在系統已經存在且比較大的情況下,突然增加某些模塊之間的聯系,而原有的接口又不符合的情況下才使用的。在Android中,適配器模式的使用是每個Android工程師都再熟悉不過的了,不過在使用適配器的過程中,我們需要明白適配器模式與其他模式混合使用的情況,同時也需要明白適配器存在的原因,什麼時候需要用適配器,什麼時候不需要使用適配器。而且,同樣的一個適配器模式的應用中,可能會存在其他不同的設計模式思想,設計模式之間並不是獨立的,而是相互滲透相互依賴的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved