Android框架设计模式(四)——Adapter Method
一、适配器模式介绍
适配器在平常在生活中是经常会用到的,特别是电子产品。像手机、电脑、家用电器都会用到适配器来转换电压的大小,以提供合适的电压。适配器就是把原来不符合要求的电压、电路信号转换成合适的电压和信号。简单来说,它就是一个接口转换器。
我们使用适配器的本质原因是:当我们的系统已经确定了一个标准,但已有的资源与现有标准不兼容,而且又无法或者不便修改这个标准的时候,就需要用适配器来使得不兼容的被使用方包装成已有的标准供已有的系统使用。
什么是适配器模式?
定义:
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使得原本因接口不匹配(接口名、返回参数、输入参数等)而无法一起工作的两个类能够在一起工作。
分类:
在软件程序设计模式中,适配器模式分为两种:类适配器、对象适配器。
类适配器
概念:通过实现目标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