初次发帖,对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();@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn getNumber;}public class MyBinder extends IGetNumber.Stub{@Overridepublic int getNumber() throws RemoteException {// TODO Auto-generated method stubnum=num+3;return num;}@Overridepublic void setNumber(int i) throws RemoteException {// TODO Auto-generated method stubnum=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;
@Overrideprotected 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 stubfor(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(){@Overridepublic void onClick(View v) {// TODO Auto-generated method stubservConn=new ServiceConnection(){@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// TODO Auto-generated method stubigetNumber=IGetNumber.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {// TODO Auto-generated method stubigetNumber=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 blocke.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(){@Overridepublic void onClick(View v) {// TODO Auto-generated method stubtry {igetNumber.setNumber(Integer.parseInt(edt_input.getText().toString()));} catch (NumberFormatException | RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});


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

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

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

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

更多相关文章

  1. 基于Android平台的Web服务技术研究
  2. 自定义实现简单的Android颜色选择器(附带源码)
  3. Android中资源管理机制详细分析
  4. Google Android(安卓)SDK 2.1正式发布
  5. Android调用WebService之服务端实现(一)
  6. android 自定义进度条颜色
  7. Android群英传第五章Scroll分析读书笔记
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. android 教程资源推荐
  2. mk中的 android:sharedUserId和LOCAL_CER
  3. Android架构组件Room的使用
  4. 源码分析Android(安卓)AsyncTask
  5. Android基础03-事件处理
  6. Android知识梳理:获得源码和编译系统
  7. android CAMERA 设置照片大小
  8. 详解Android(安卓)目录结构
  9. Mono for Android(安卓)优势与劣势
  10. Android系统介绍及架构概览