android基础知识05:四大组件之service 02:远程调用

迷途d书童 发布于 2012/03/23 18:39
阅读 345
收藏 0

         本文主要介绍service相关内容。包括两篇文章:

        android基础知识05:四大组件之service 01

        android基础知识05:四大组件之service 02:远程调用

       android基础知识05:四大组件之service 03:实现机制


         参考资料最牛网 《Android Service学习之AIDL, Parcelable和远程服务

         实例程序地址:http://download.csdn.net/detail/xianming01/4131187

1、AIDL的作用
        由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
        通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。

        AIDL (Android Interface Definition Language) 是一种IDL 语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
       AIDL IPC机制是面向接口的,像COM或Corba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

2、选择AIDL的使用场合
        官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。
         如果不需要进行不同应用程序间的并发通信(IPC),you should create your interface by implementing a Binder;或者你想进行IPC,但不需要处理多线程的,则implement your interface using a Messenger。无论如何,在使用AIDL前,必须要理解如何绑定service——bindService。
       在设计AIDL接口前,要提醒的是,调用AIDL接口是直接的方法调用的,不是我们所想象的调用是发生在线程里。而调用(call)来自local进程或者remote进程,有什么区别呢?尤其是以下情况(引用原文,不作翻译了,以免翻译有误):
Calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. If it is another thread, that is the one that executes your code in the service. Thus, if only local threads are accessing the service, you can completely control which threads are executing in it (but if that is the case, then you shouldn't be using AIDL at all, but should instead create the interface by implementing a Binder).
Calls from a remote process are dispatched from a thread pool the platform maintains inside of your own process. You must be prepared for incoming calls from unknown threads, with multiple calls happening at the same time. In other words, an implementation of an AIDL interface must be completely thread-safe.
The oneway keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from the Binder thread pool as a normal remote call. If oneway is used with a local call, there is no impact and the call is still synchronous.

3、定义AIDL接口
        AIDL接口文件,和普通的接口内容没有什么特别,只是它的扩展名为.aidl。保存在src目录下。如果其他应用程序需要IPC,则那些应用程序的src也要带有这个文件。Android SDK tools就会在gen目录自动生成一个IBinder接口文件。service必须适当地实现这个IBinder接口。那么客户端程序就能绑定这个service并在IPC时从IBinder调用方法。
        每个aidl文件只能定义一个接口,而且只能是接口的声明和方法的声明。

3.1 创建.aidl文件
         AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。
    其中对于Java编程语言的基本数据类型 (int, long, char, boolean等),String和CharSequence,集合接口类型List和Map,不需要import 语句。
    而如果需要在AIDL中使用其他AIDL接口类型,需要import,即使是在相同包结构下。AIDL允许传递实现Parcelable接口的类,需要import.
    需要特别注意的是,对于非基本数据类型,也不是String和CharSequence类型的,需要有方向指示,包括in、out和inout,in表示由客户端设置,out表示由服务端设置,inout是两者均可设置。
      AIDL只支持接口方法,不能公开static变量。

IMusicService.aidl

package xuxm.demo.service03.server;
/**
 * ClassName:IMusicService
 * Function: TODO ADD FUNCTION
 * Reason:	 TODO ADD REASON
 *
 * @author   Leon
 * @version
 * @since    Ver 1.1
 * @Date	 2011-5-19
 */
 
interface IMusicService{
	void play();
	void pause();
	void stop();
}
       Android会自动生成一个 Stub类,这个类继承了BInder类,同时继承了IMusicService这个接口,还可以看到其中包含了一个Proxy代理类,以实现远程代理。(Stub类如下所示)

生成stub类的位置

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\Users\\xuxianming01.INTERNAL\\workspace\\serviceTest03_server\\src\\xuxm\\demo\\service03\\server\\IMusicService.aidl
 */
package xuxm.demo.service03.server;
/**
 * ClassName:IMusicService
 * Function: TODO ADD FUNCTION
 * Reason:	 TODO ADD REASON
 *
 * @author   Leon
 * @version
 * @since    Ver 1.1
 * @Date	 2011-5-19
 */
public interface IMusicService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements xuxm.demo.service03.server.IMusicService
{
private static final java.lang.String DESCRIPTOR = "xuxm.demo.service03.server.IMusicService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an xuxm.demo.service03.server.IMusicService interface,
 * generating a proxy if needed.
 */
public static xuxm.demo.service03.server.IMusicService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = (android.os.IInterface)obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof xuxm.demo.service03.server.IMusicService))) {
return ((xuxm.demo.service03.server.IMusicService)iin);
}
return new xuxm.demo.service03.server.IMusicService.Stub.Proxy(obj);
}
public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_play:
{
data.enforceInterface(DESCRIPTOR);
this.play();
reply.writeNoException();
return true;
}
case TRANSACTION_pause:
{
data.enforceInterface(DESCRIPTOR);
this.pause();
reply.writeNoException();
return true;
}
case TRANSACTION_stop:
{
data.enforceInterface(DESCRIPTOR);
this.stop();
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements xuxm.demo.service03.server.IMusicService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
public void play() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_play, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public void pause() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_pause, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public void stop() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_play = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_pause = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
public void play() throws android.os.RemoteException;
public void pause() throws android.os.RemoteException;
public void stop() throws android.os.RemoteException;
}
        这个类是android自动生成的,我们不需要动他。

       在MyRemoteBinder需要继承这个Stub类,对播放器的控制写在这个Binder类中。这里为简单起见,我们将这两个类写入同一个文件中。

public class MusicService03 extends Service{
 
	private  String  TAG = MusicService03.class.getSimpleName();
	private  MediaPlayer myMediaPlayer ;
	public   static final  String INTENT_KEY= "action" ;
	
	public MusicService03() {     super(); }
	
	MyRemoteBinder mbinder=new MyRemoteBinder(MusicService03.this.myMediaPlayer);
	
	@Override
	public IBinder onBind(Intent arg0) { 
		// TODO Auto-generated method stub
		Log.v(TAG, TAG+" onBind");
		mbinder=new MyRemoteBinder(MusicService03.this.myMediaPlayer);
		return mbinder;
 
	}
	
	public class MyRemoteBinder extends IMusicService.Stub{
		 
		public  MyRemoteBinder(MediaPlayer mediaPlayer){
			MyMediaController.mediaPlayer=mediaPlayer ;
		};
	 
		@Override
		public void play() throws RemoteException {
	 
			// TODO Auto-generated method stub
			MyMediaController.play.execute();
	 
		}
	 
		@Override
		public void pause() throws RemoteException {
	 
			// TODO Auto-generated method stub
			MyMediaController.pause.execute();
	 
		}
	 
		@Override
		public void stop() throws RemoteException {
	 
			// TODO Auto-generated method stub
			MyMediaController.stop.execute();
	 
		}; 
	 
	}
	
 
	@Override
	public void onCreate() {
 
		// TODO Auto-generated method stub
		Log.v(TAG , TAG+ " onCreate()");
		super.onCreate();
		if(myMediaPlayer==null){
			myMediaPlayer=MediaPlayer.create(this, R.raw.test) ;
			myMediaPlayer.setLooping(false);
		}
	}
 
	@Override
	public void onStart(Intent intent, int startId) {
		// TODO Auto-generated method stub
		Log.v(TAG , TAG + " onStart()");
		super.onStart(intent, startId);
	}
 
	@Override
	public boolean onUnbind(Intent intent) {
 
		// TODO Auto-generated method stub
		Log.v(TAG , TAG +" onUnbind ,成功没有?" + super.onUnbind(intent));
		return true;
		//return false;
 
	}
	
	@Override
	public void onRebind(Intent intent) {
 
		// TODO Auto-generated method stub
		Log.v(TAG , TAG +" onRebind()----------------------------------->");
		super.onRebind(intent);
 
	}
	
	@Override
	public void onDestroy() {
 
		// TODO Auto-generated method stub
		super.onDestroy();
		Log.v(TAG , " onDestroy");
		if(myMediaPlayer!=null){
			myMediaPlayer.stop();
			myMediaPlayer.release();
		}
 
	}

	public MediaPlayer getMyMediaPlayer() {
		return myMediaPlayer;
	}

	public void setMyMediaPlayer(MediaPlayer myMediaPlayer) {
		this.myMediaPlayer = myMediaPlayer;
	}
	
	
 
}
        此时,对于这个service,可以在service所在的程序中调用该service,其他应用程序也可以调用该service(aidl的目的正是如此)。
        当前程序的调用比较简单,大家可以看实例代码。对于其他应用程序的调用,在这里做一说明。

        我们新建了一个client的应用程序。

       在client程序中需要建立一个与service所在包名一致的包(xuxm.demo.service03.server),同时建立一个与service中一模一样的aidl文件。


这里同样自动生成了一个stub文件。

于是client中的activity就可以使用这个服务了。

package xuxm.demo.service03.client;

import xuxm.demo.service03.server.IMusicService;
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.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class ServiceTestServerActivity extends Activity implements OnClickListener{
    /** Called when the activity is first created. */
	private static final String TAG = ServiceTestServerActivity.class.getSimpleName();
	
	private TextView m_TextView_text;
	private Button btnPlay;
	private Button btnPause;
	private Button btnStop;
	private Button btnUnbind;
	private Button btnFinish;
	private Button btnExit;
	
	private IMusicService bindMusicService;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        m_TextView_text= (TextView)findViewById(R.id.text);
        
        btnPlay= (Button)findViewById(R.id.play);
        btnPause= (Button)findViewById(R.id.pause);
        btnStop= (Button)findViewById(R.id.stop);

        btnFinish= (Button)findViewById(R.id.finish);
        btnExit= (Button)findViewById(R.id.exit);
        
        btnPlay.setOnClickListener(this);
        btnPause.setOnClickListener(this);
        btnStop.setOnClickListener(this);

        btnFinish.setOnClickListener(this);
        btnExit.setOnClickListener(this);
        
        connection();
    }
    
    private void connection() {
		Log.v(TAG , TAG+"connection");
		Intent intent = new Intent("xuxm.demo.service03.MusicService");
		this.startService(intent);
		this.bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
 
	}

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch(v.getId())
		{
		case R.id.play:
			try {
				bindMusicService.play();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			m_TextView_text.setText(R.string.play);
			Log.d(TAG, "play.......");
			break;
		case R.id.pause:
			try {
				bindMusicService.pause();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			m_TextView_text.setText(R.string.pause);
			Log.d(TAG, "pause.......");
			break;
		case R.id.stop:
			try {
				bindMusicService.stop();
			} catch (RemoteException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			m_TextView_text.setText(R.string.stop);
			Log.d(TAG, "stop.......");
			break;
		case R.id.finish:
			Log.d(TAG, "close.......");
			this.finish();
			break;
		case R.id.exit:
			Log.d(TAG, "exit.......");
			this.finish();
			this.stopService(new Intent("xuxm.demo.service03.MusicService"));
			break;
		default:
		}
	}
	
	//调用bindService后 Service调用onBind()后 回调此函数
	private ServiceConnection myServiceConnection = new ServiceConnection() {
		 
		@Override
		public void onServiceConnected(ComponentName name, IBinder binder) {
			bindMusicService = IMusicService.Stub.asInterface(binder);
			Log.d(TAG, " onServiceConnected");
		}
 
		@Override
		public void onServiceDisconnected(ComponentName name) {
			bindMusicService = null;
			Log.d(TAG, " onServiceDisconnected");
		}
 
	};
	    //当Activity finish时必须解绑 不然会出现溢出
	 
		@Override
		public void finish() {
			// TODO Auto-generated method stub
			super.finish();
		    this.unbindService(myServiceConnection);
	 
		}
}
4、其他说明

4.1  Permission权限
        如果Service在AndroidManifest.xml中声明了全局的强制的访问权限,其他引用必须声明权限才能来start,stop或bind这个service.
      另外,service可以通过权限来保护她的IPC方法调用,通过调用checkCallingPermission(String)方法来确保可以执行这个操作。

4.2 AndroidManifest.xml的Service元素

<service android:name=".RemoteService" android:process=":remote"> 
        <intent-filter> 
                <action android:name="com.demo.IMyService" /> 
        </intent-filter> 
</service>
        这里的android:process=":remote",一开始我没有添加的,在同一个程序里使用IPC,即同一个程序作为客户端/服务器端,结果运行mRemoteService = IMyService.Stub.asInterface(service);时提示空指针异常。观察了人家的在不同程序里进行IPC的代码,也是没有这个android:process=":remote"的。后来在官方文档http://androidappdocs.appspot.com/guide/topics/manifest/service-element.html里了解到(留意第二段文字):

android:process
The name of the process where the service is to run. Normally, all components of an application run in the default process created for the application. It has the same name as the application package. The <application> element's process attribute can set a different default for all components. But component can override the default with its own process attribute, allowing you to spread your application across multiple processes.
 
If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the service runs in that process. If the process name begins with a lowercase character, the service will run in a global process of that name, provided that it has permission to do so. This allows components in different applications to share a process, reducing resource usage.

         也就是说android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。



原文链接:http://blog.csdn.net/xianming01/article/details/7342026
加载中
返回顶部
顶部