码迷,mamicode.com
首页 > 移动开发 > 详细

Android框架设计模式(四)——Adapter Method

时间:2016-04-04 01:36:59      阅读:253      评论:0      收藏:0      [点我收藏+]

标签:


一、适配器模式介绍


适配器在平常在生活中是经常会用到的,特别是电子产品。像手机、电脑、家用电器都会用到适配器来转换电压的大小,以提供合适的电压。适配器就是把原来不符合要求的电压、电路信号转换成合适的电压和信号。简单来说,它就是一个接口转换器。

什么是适配器模式?


定义:

适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使得原本因接口不匹配(接口名、返回参数、输入参数等)而无法一起工作的两个类能够在一起工作。

分类:

在软件程序设计模式中,适配器模式分为两种:类适配器、对象适配器。

  • 类适配器

    • 概念:通过实现目标Target接口以及继承Adaptee(需要被适配的类)来实现接口转换。把Adaptee的接口转换成Target需要的接口。

    • UML图

      技术分享

  • 对象适配器

    • 概念:与类适配器也一样的目的:把Adaptee的接口转换成Target需要的接口。但是不同的是,对象适配器是使用代理关系链接到Adaptee类,即将Adaptee作为Adapter中的成员,由Adapter作为代理来实现Adaptee的功能。

    • UML图

    技术分享


适配器应用于什么场景?


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工程师都再熟悉不过的了,不过在使用适配器的过程中,我们需要明白适配器模式与其他模式混合使用的情况,同时也需要明白适配器存在的原因,什么时候需要用适配器,什么时候不需要使用适配器。而且,同样的一个适配器模式的应用中,可能会存在其他不同的设计模式思想,设计模式之间并不是独立的,而是相互渗透相互依赖的。

Android框架设计模式(四)——Adapter Method

标签:

原文地址:http://blog.csdn.net/woshimalingyi/article/details/51052814

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!