学习一个设计模式我希望从它的形成原因上进行分析,而不仅仅是知道它的优缺点和如何使用它。
Proxy,中文是代理的意思,我们考虑如下情境:
class A{
}
class B{
void functionB(){
}
}
类A想要调用类B中的某个接口functionB(),但是现实中有些情况是类A不能直接调用类B中的接口,比如类A和类B在两个进程中。此时我们可以如下处理:
class A{
}
class BProxy{
void functionB(){}
}
class B{
void functionB(){
}
}
我们使类A可以调用BProxy中的接口,而BProxy可以调用类B中的接口,BProxy和类B有着几乎相同的接口,这时我们调用BProxy中的接口仿佛就在调用类B中的接口。Proxy代理模式就是为了解决这一问题而出现的。如下图我们在Android源码中的实例:
如下图,我们甚至可以抽象出Proxy和mp3Player类中的相同部分成为接口,然后封装Proxy和mp3Player之间的跨进程调用,从而使得ac01对mp3Player中接口的使用更无违和感。
我们从上图来理解Proxy模式——ac01需要使用mp3Player中的接口,由于是跨进程的,所以不能直接调用,但是我们实现一个Proxy类可以跨进程调用mp3Player中的接口,然后让ac01调用这个Proxy中的接口,Proxy跨进程调用mp3Player中的接口从而实现ac01使用mp3Player中的接口的目的。
这里实际操作的关键点在于如何实现IPC跨进程调用,Android的跨进程调用常见于各种service和activity的交互,同时使用Binder机制进行跨进程调用,这里借助如下图所示的例子说明如何设计和使用代理。
这里先要说明PlayerProxy和mp3Player二者的关系,这样就能更好的理解Android是如何使用Binder机制实现跨进程调用的:
1.二者由同一开发者设计,也就是说mp3Player的功能就是提供给别的进程(Client)进行使用的。
2.二者提供相同的接口,二者属于同一类别体系。
解读上图,我们在设计mp3Player这个类的时候,已经知道了这个类的功能是要提供给客户端使用的,因此我们必须使用跨进程调用机制,在Android中,我们使用Binder机制实现(如图),同时使用了Adapter模式简化Binder和mp3Player交互的接口。同时为了在客户端用户使用mp3Player功能的方便,一并设计了PlayerProxy这一类,这一类有着和mp3Player完全相同的接口,并且它通过Binder跨进程调用mp3Player中相应的功能。这是Android实现跨进程调用的典型方法。
在Android中,需要进行跨进程调用的往往是Activity对service或service对service的调用。这也就是为什么Android Binder机制组件中还有一个ServiceManager类负责注册所有的服务的原因,也就是所有服务端(mp3Player)(往往是一个service)都需要向ServiceManager进行注册接口,而由ServiceManager把这些接口提供给客户端(比如一个activity)进行调用。
下面通过实例实现上图中的跨进程调用:
1.编写统一的IPlayer.java接口:
package com.sean.mp3player;
public interface IPlayer {
public void play();
public void stop();
public void getStatus();
}
2.编写服务端的mp3Player.java接口:
package com.sean.mp3player;
import android.content.Context;
public class Mp3Player implements IPlayer {
private Context context;// 因为这些类是与Activity,Service相关的,往往会提供一个context用以绑定到具体的组件中,以便获取组件资源
public Mp3Player(Context context) {
// TODO Auto-generated constructor stub
this.context = context;
}
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("---play()---");
}
@Override
public void stop() {
// TODO Auto-generated method stub
System.out.println("---stop()---");
}
@Override
public void getStatus() {
// TODO Auto-generated method stub
System.out.println("---getStatus()---");
}
}
3.编写PlayerAdapter类,该类继承自Binder接口:
package com.sean.mp3player;
import android.content.Context;
import android.os.Binder;
import android.os.Parcel;
import android.os.RemoteException;
public class PlayerAdapter extends Binder {
private IPlayer player;
public PlayerAdapter(Context context) {
player = new Mp3Player(context);//题外话,如何一步步通过构造函数传递参数context
}
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {//1.从Adapter模式来讲,它实现了三个接口转为一个接口的变化。//2.对IBinder机制,通过code,data,reply传递数据
// TODO Auto-generated method stub
switch (code) {
case 1:
player.play();
break;
case 2:
player.stop();
break;
case 3:
player.getStatus();
break;
}
return true;
}
}
4.编写服务端进程Service: Android系统对如何使用Service通过Binder传递数据
package com.sean.mp3player;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class PlayerService extends Service {
private IBinder binder;
@Override
public void onCreate() {
// TODO Auto-generated method stub
binder = new PlayerAdapter(getApplicationContext());
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return binder;
}
}
至此,服务端程序已经完全写好,下面我们开始客户端程序的编写:
5.编写Proxy代理类,这里我们关注代理类是如何通过Binder接口实现的跨进程对Mp3Player接口的控制:
package com.sean.mp3player;
import android.os.IBinder;
public class Mp3PlayerProxy implements IPlayer {
private IBinder binder;
public Mp3PlayerProxy(IBinder binder){
this.binder = binder;
}
@Override
public void play() {
// TODO Auto-generated method stub
try {
binder.transact(1, null, null, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void stop() {
// TODO Auto-generated method stub
try {
binder.transact(2, null, null, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void getStatus() {
// TODO Auto-generated method stub
try {
binder.transact(3, null, null, 0);
} catch (Exception e) {
e.printStackTrace();
}
}
}
6.编写客户端界面Activity:
package com.sean.mp3player;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
private Button play, stop, status;
private IPlayer player;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
play = (Button) this.findViewById(R.id.play);
stop = (Button) this.findViewById(R.id.stop);
status = (Button) this.findViewById(R.id.getstatus);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {//这里的IBinder形参正是Service类onBind()中返回的IBinder
// TODO Auto-generated method stub
player = new Mp3PlayerProxy(service);
}
};
Intent service = new Intent("com.sean.mp3player.PLAY_SERVICE");//Manifest中为service注册的intent
bindService(service, conn, Context.BIND_AUTO_CREATE);
play.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
player.play();
}
});
stop.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
player.stop();
}
});
status.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
player.getStatus();
}
});
}
}
仔细关擦5和6,我们想想客户端是如何使用服务端提供的功能的,首先通过Android系统提供的bindService()启动服务端并获取服务端传递的IBinder对象(关键是获取IBinder对象,也就是说客户端和服务端的IBinder对象是同一个对象)。然后调用transact()函数发送数据(这里最重要的是Binder机制的底层实现,它是通过Binder驱动实现的跨进程数据传递,这里我们不管,本篇我们关注的是Binder.java类和Proxy模式如何实现跨进程的调用),这个接口会反过来调用onTransact()函数。从而回到了客户端的mp3Player接口函数中。