Android AIDL应用间交互

 1、介绍

Android使用AIDL来完成进程间通信(IPC)AIDL全程为AndroidInterface Definition Language。在服务需要接受不同应用多线程的请求时才需要使用AIDL,如果是同一个应用内的请求使用Binder实现即可,见应用内交互的服务;如果只是应用间通信而不是多线程处理的话使用Messenger,当然这两种情况也可以使用AIDL。本地进程和远程进程使用AIDL有所不同,本地进程内调用时会都在调用的线程内执行,远程进程使用是通过Service进程内一个由系统维护的线程池发出调用,所以可能是未知线程同时调用,需要注意线程安全问题。

2、示例

在被调用应用内(后面称服务端)Service内有个属性count,需要在调用者(后面称客户端)内对这个属性进行操作。下面将介绍服务器端和客户端的主要代码

服务器端(代码见ServiceDemo)

(1) 服务器端新建AIDL文件定义接口

在服务器端的src目录下新建以.aidl为后缀的文件,在这个文件中定义一个接口,声明服务器端和客户端交互的api,语法跟普通java接口声明一样,可以添加中英文注释。不同的是

a. 除了Java基本数据类型 (int,long, char, boolean)StringCharSequenceListMap外,其他复杂类型都需要显式import(包括其他AIDL定义的接口),即便复杂类型在同一个包内定义。

b. 支持泛型实例化的List,如List<String>;不支持泛型实例化的Map,如Map<String,String>。对于List为参数接收者接收到的始终是ArrayList;对于Map为参数接收者接收到的始终是HashMap

c. interface和函数都不能带访问权限修饰符。

d. 接口内只允许定义方法,不允许定义静态属性。

我们定义MyAIDLInterface.aidl文件如下

package com.example.aidlserver1;

interface MyAIDLInterface {
	int getCount();
	void setCount(int count);
}

声明两个接口在后面供客户端调用。保存文件,编译后,Sdk工具在gen目录下相同子目录中自动生成同名的以.java为后缀的文件,这里文件名为MyAIDLInterface.java

(2) 自动生成的同名java文件解析

MyAIDLInterface.javaSdk工具自动生成,内容如下

其中有几个主要的部分:

a. 抽象类Stub,继承Binder实现自定义接口,作用同进程内通信服务中自定义的Binder,客户端通过它对服务进行调用。

b. 静态类Proxy,实现自定义接口,代理模式接收对Stub的调用。

 (3) 服务器端定义服务

跟一般的服务定义类似,新建个上面自动生成的MyAIDLInterface.java文件中的Stub的对象,在onBind中返回,代码如下

package com.example.aidlserver1;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Toast;

public class MyAIDLService extends Service {
	private final static String TAG = "MyAIDLService";
	private int mCount;
	
	private MyAIDLInterface.Stub myBinder = new MyAIDLInterface.Stub() {
		@Override
		public int getCount() throws RemoteException {
			// TODO Auto-generated method stub
			Log.v(TAG, "yzf, getCount");
			return mCount;
		}

		@Override
		public void setCount(int count) throws RemoteException {
			// TODO Auto-generated method stub
			Log.v(TAG, "yzf, setCount");
			mCount = count;
		}
	};
	
	public void onCreate() {
		Log.v(TAG, "yzf, onCreate");
		mCount = 0;
		Toast.makeText(this, "Service onCreate", Toast.LENGTH_SHORT).show();
		super.onCreate();
	}
	
	public void onDestroy() {
		Log.v(TAG, "yzf, onDestroy");
		Toast.makeText(this, "Service onDestroy", Toast.LENGTH_SHORT).show();
		super.onDestroy();
	}
	
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.v(TAG, "yzf, onStartCommand");
		Toast.makeText(this, Integer.toString(mCount), Toast.LENGTH_SHORT).show();
		return super.onStartCommand(intent, flags, startId);
	}
	
	public int getCount() {
		Log.v(TAG, "yzf, getCount2");
		return mCount;
	}

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		Log.v(TAG, "yzf, onBind");
		return myBinder;
	}
}

AndroidManifest.xml中注册服务

<service android:name=".MyAIDLService">
	<intent-filter>
		<action android:name="com.trinea.android.demo.remote.MyAIDLServiceAction" />
	</intent-filter>
</service>

其中的action name用于后面客户端调用时匹配

客户端(代码见AndroidAidlClient@GoogleCode)

 (1) 拷贝aidl文件

将服务器端的aidl文件拷贝到客户端,客户端也会在gen目录下自动生成同名的java文件。服务器端和客户端文件目录如下:

(2) 服务绑定

跟一般的服务绑定类似,只是获取服务的接口不同而已,这里我们通过Stub.asInterface函数获得,代码如下

package com.example.aidlclient1;

import com.example.aidlserver1.MyAIDLInterface;

import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {
	private final static String TAG = "MainActivity";
	private MyAIDLInterface binder = null;
	private Intent myAIDLServiceIntent;
	private ServiceConnection con = new ServiceConnection() {
		@Override
		public void onServiceConnected(ComponentName arg0, IBinder service) {
			// TODO Auto-generated method stub
			Log.v(TAG, "yzf, onServiceConnected");
			binder = MyAIDLInterface.Stub.asInterface(service);
		}

		@Override
		public void onServiceDisconnected(ComponentName arg0) {
			// TODO Auto-generated method stub
			Log.v(TAG, "yzf, onServiceDisconnected");
		}
	};
	private Button boundAIDLServiceBtn, getBoundAIDLServiceProBtn, unboundAIDLServiceBtn;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Log.v(TAG, "yzf, onCreate");
		myAIDLServiceIntent = new Intent("com.trinea.android.demo.remote.MyAIDLServiceAction");
		
		boundAIDLServiceBtn = (Button)findViewById(R.id.boundAIDLService);
		boundAIDLServiceBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				boolean result = bindService(myAIDLServiceIntent, con, Context.BIND_AUTO_CREATE);
				if (!result) {
					binder = null;
					Toast.makeText(getApplicationContext(), "服务绑定失败。", Toast.LENGTH_SHORT).show();
				}
			}
		});
		
		getBoundAIDLServiceProBtn = (Button)findViewById(R.id.getBoundAIDLServicePro);
		getBoundAIDLServiceProBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				try {
					if (binder != null) {
						Toast.makeText(getApplicationContext(), "Service count:" + binder.getCount(),
                                Toast.LENGTH_SHORT).show();
					}else{
						Toast.makeText(getApplicationContext(), "请先绑定服务。", Toast.LENGTH_SHORT).show();
					}
				}catch (RemoteException e) {
					e.printStackTrace();
				}
			}
		});
		
		unboundAIDLServiceBtn = (Button)findViewById(R.id.unboundAIDLService);
		unboundAIDLServiceBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				if (binder != null) {
					unbindService(con);
					binder = null;
				}
			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
}

上面的三个按钮boundAIDLServiceBtngetBoundAIDLServiceProBtnunboundAIDLServiceBtn分别用于绑定服务、得到服务中属性值、解除绑定服务。

从上面可以看出我们先定义ServiceConnection对象用于和服务器通信,在onServiceConnected函数中通过MyAIDLInterface.Stub.asInterface(service)返回用户定义的接口对象,通过此对象调用远程接口api。在boundAIDLServiceBtnonClick函数中绑定服务,在getBoundAIDLServiceProBtnonClick函数中调用服务的api,在boundAIDLServiceBtnonClick函数中取消绑定服务,

现在我们运行两个项目即可,在客户端绑定服务再获取服务属性显示。

3、生命周期

通过bindService绑定服务,若服务未启动,会先执行ServiceonCreate函数,再执行onBind函数,最后执行ServiceConnection对象的onServiceConnected函数,客户端可以自动启动服务。若服务已启动但尚未绑定,先执行onBind函数,再执行ServiceConnection对象的onServiceConnected函数。若服务已绑定成功,则直接返回。这里不会自动调用onStartCommand函数。

通过unbindService解除绑定服务,若已绑定成功,会先执行ServiceonUnbind函数,再执行onDestroy函数,注意这里不会执行ServiceConnection对象的onServiceDisconnected函数,因为该函数是在服务所在进程被kill或是crash时被调用。

转载自:http://www.cnblogs.com/trinea/archive/2012/11/08/2701390.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值