现在的位置: 首页 > 综合 > 正文

简话设计模式之Proxy模式

2017年11月05日 ⁄ 综合 ⁄ 共 5625字 ⁄ 字号 评论关闭

学习一个设计模式我希望从它的形成原因上进行分析,而不仅仅是知道它的优缺点和如何使用它。

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接口函数中。



抱歉!评论已关闭.