Android AIDL机制范例解析

   初次发帖,对Android了解和掌握还不是很深,敬请各位指正交流,一起进步。

 

   Android为了实现进程间通信,提供了AIDL机制。AIDL全称是Android Interface Definition Language,即进程间接口描述语言。通过AIDL机制,应用程序可以通过描述的接口访问远程服务中的方法。

   下面说一下AIDL机制的使用方法,因为自身水平问题,范例都比较简单。

    Server端与Client端功能简介:

    Server端:在Server端开启一个后台服务, 该服务只提供两种方法供调用

            1、int型的getNumber(int)方法,该方法将客户端传递过来的数据加3,再返回给客户端。

            2、void型的setNumber()方法,该方法是设置Service中的变量为客户端传递进来的数据。

    Client端:Activity,主要有两个按钮,一个是调用按钮,读取远程Service中的数据,一个是设置按钮,将Client端EditText中的数值传递给远程Service。

    Service端与Client端的交互逻辑如下:

    Server端负责后台Service的开启与中止,Client端开启后,首先判断服务是否存在,若存在则可以通过AIDL机制调用远程Service提供的方法并设置远程Service中的全局变量,若服务不存在则不可调用(本人在此处一个不经意的错误,排查了很长时间,在后面说)

 

   使用AIDL方法实现上述功能的主要步骤如下:

一、AIDL接口定义:

    在本步骤中,定义的是接口的名称以及方法的参数、返回值,新建一个.aidl文件,文件名需要与接口名相同。本范例中AIDL定义如下:

   

package com.example.servicedemo;

interface IGetNumber{
	int getNumber();
	void setNumber(int i);
}


 二、在Service中实现定义的AIDL接口

    定义完AIDL后,在gen包中,会自动生成一个与AIDL同名的.java文件,即依据所定义的接口自动生成的一个接口文件,该文件包含一个Stub抽象类,该抽象类又包含了我们在AIDL中定义的方法,Stub抽象类继承了Binder,Activity是通过Binder机制来实现与Service通信的,因此我们需要在Service中实现该Stub抽象类,以具体实现在AIDL中定义的方法,这样在Client端绑定远程Service时通过返回一个stub类可以调用Service的方法。

  Service端的主要代码如下:

 

public class ServiceDemo extends Service{

	public  int num=0;
	public Stub getNumber=new MyBinder();
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return getNumber;
	}
		
	public class MyBinder extends IGetNumber.Stub
	{
		@Override
		public int getNumber() throws RemoteException {
			// TODO Auto-generated method stub
			num=num+3;
			return num;
		}

		@Override
		public void setNumber(int i) throws RemoteException {
			// TODO Auto-generated method stub
			num=i;
		}
	}

 

三、Client端绑定远程Service

    
Client端首先需要导入Server端gen文件夹下生成的IGetNumber.java文件,具体的方法为在Client端src文件夹下新建一个与Server端同名的包,并将IGetNumber.java复制到该包下。Client端Activity主要代码如下:

   1、首先判断远程Service是否开启,此处与本文关系不大,一并贴一下。该功能是通过ActivityManager实现的,ActivityManager类中有一个getRunningServices(int MaxNum)方法,该方法可以返回一个正在运行的服务的列表,参数定义了返回的列表中列表项的最大数目。该判断过程放在onCreate()方法中,主要代码如下:

         public List <ActivityManager.RunningServiceInfo> mList;
	public ActivityManager mAcManager;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		if (savedInstanceState == null) {
			getSupportFragmentManager().beginTransaction()
					.add(R.id.container, new PlaceholderFragment()).commit();
		}
		
		findView();         //获取控件变量的函数
		mAcManager=(ActivityManager)getSystemService(ACTIVITY_SERVICE);
		mList=mAcManager.getRunningServices(30);
		
		if(ServiceIsExist(mList,ServiceName))
		{
			Toast.makeText(getApplicationContext(), "远程服务已经开启", 0).show();
			
		}	
		else
		{
			Toast.makeText(getApplicationContext(), "远程服务尚未开启", 0).show();
		}
	}


         ServiceIsExist(mList,ServiceName)即为判断一个服务是否存在的函数,返回一个Boolean型,具体代码为:

           

private boolean ServiceIsExist(List<RunningServiceInfo> servicelist,
			String classname ) {
		// TODO Auto-generated method stub
		for(int i=0;i<servicelist.size();i++)
		{
			if(classname.equals(servicelist.get(i).service.getClassName()))
			{
				return true;
			}
		}
		return false;
	}	

    2、绑定远程Service,该步骤需要通过之前导入的IGetNumber.java对象和一个ServiceConnection对象实现。主要代码如下,其中btn_start即为开始调用的按钮:

     

	private IGetNumber igetNumber=null;
	public ServiceConnection servConn;

         btn_start.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				servConn=new ServiceConnection(){
					
					@Override
					public void onServiceConnected(ComponentName name, IBinder service) {
						// TODO Auto-generated method stub
						igetNumber=IGetNumber.Stub.asInterface(service);
					}

					@Override
					public void onServiceDisconnected(ComponentName name) {
						// TODO Auto-generated method stub
						igetNumber=null;
					}};
					Intent intent=new Intent(IGetNumber.class.getName());
					bindService(intent, servConn, 0);			
				if(igetNumber!=null)
				{
					try {
						Toast.makeText(getApplicationContext(),String.valueOf(igetNumber.getNumber()),0).show();
					} catch (RemoteException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}});

需要特别注意的是,在bindService()函数的第三个参数中,本人吃了大亏。由于自己的大意,第三个参数我写的是Context.BIND_AUTO_CREATE,由于本文的应用场景是远程服务不开启则无法调用,因此在测试的时候发现即使Server端关闭了Client端仍旧可以调用AIDL中定义的方法,即使调用了Unbind()方法和onDestroy()方法。这显然是不对的,经过仔细的研究才发现,BIND_AUTO_CREATE的意义在于,若绑定的Service不存在则自动创建一个Service实例,因此即使通过Server端将开启的Service解除绑定并销毁,Client端仍然可以在系统内实例化一个远程Service并绑定。排查过后改为0,问题就解决了,此处花了好几个小时,新手学艺不精,长教训了。有此应用需求的朋友一定要注意。

通过Toast.makeText(getApplicationContext(),String.valueOf(igetNumber.getNumber()),0).show(),我们成功的通过AIDL方式调用了远程Service中的方法,并将返回值以Toast的方式显示在了客户端界面中。

 

还要再说一点的是,众所周知,onCreate()函数只在Activity创建的过程中调用一次,而本文中需要绑定的远程Service不一定开启,因此我没有将绑定远程Service的代码放在onCreate()函数中,而是放在了“开始调用”按钮的点击事件响应代码中,这样做不好的地方就是如果远程Service已经开启,则Activity创建后需要点击两次才能真正的开始调用AIDL中定义的方法

 

3、设置远程Service中的全局变量,即实现AIDL中定义的setNumber(int )方法。上一步实现之后,这一步就非常简单了,直接贴代码:

		btn_set.setOnClickListener(new OnClickListener(){

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				try {
					igetNumber.setNumber(Integer.parseInt(edt_input.getText().toString()));
				} catch (NumberFormatException | RemoteException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}});


在有些应用场景中,需要将参数传递给远程Service进行处理,实际上该方法就是演示这样一个过程。

 

四、Manifest文件中对远程Service的定义

   这一步就比较简单了,需要注意的是在对Service定义的时候,android:process参数需要设置为":remote"

 

经过以上步骤,基本就可以通过AIDL的方式来调用远程Service提供的方法了。

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值