Android(java)学习笔记175:Android进程间通讯(IPC)之AIDL
一、IPC
inter process communication 进程间通讯
二、AIDL
android interface defination language 安卓接口定义语言
满足两个进程之间 接口数据的交换(ipc)
首先我们搞清楚两个概念 远程服务和本地服务 ?
本地服务:服务的代码在应用程序工程的内部
远程服务:服务的代码在另一个应用程序的里面
三、下面通过案例说明AIDL(IPC)在远程服务中使用
1.首先创建一个Android项目,命名为"远程服务";
(1)工程一栏表如下:
(2)既然是远程服务,我们就先创建远程服务为RemoteServiceDemo.java,同时也需要在AndroidMainfest.xml的清单文件中注册,如下:
AndroidMainfest.xml:
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.himi.remoteservice" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk 8 android:minSdkVersion="15" 9 android:targetSdkVersion="17" /> 10 11 <application 12 android:allowBackup="true" 13 android:icon="@drawable/ic_launcher" 14 android:label="@string/app_name" 15 android:theme="@style/AppTheme" > 16 <activity 17 android:name=".MainActivity" 18 android:label="@string/app_name" > 19 <intent-filter> 20 <action android:name="android.intent.action.MAIN" /> 21 22 <category android:name="android.intent.category.LAUNCHER" /> 23 </intent-filter> 24 </activity> 25 <service android:name="com.himi.remoteservice.RemoteServiceDemo"> 26 <intent-filter > 27 <action android:name="com.himi.remoteservice"/> 28 </intent-filter> 29 </service> 30 </application> 31 32 </manifest>
这里我们定义了service的action,这是因为倘若应用程序自己调用服务,直接使用显式意图即可,也就是如下这种形式:
Intent intent = new Intent(this,Service.class);
startActivity(intent);
但是,我们这里是自己定义的远程服务程序,也就是这个程序在远程,让本地(或者其他用户)去调用的,所以这里使用了隐式意图。
与此同时,我们编写RemoteServiceDemo.java代码如下:
1 package com.himi.remoteservice; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Binder; 6 import android.os.IBinder; 7 8 public class RemoteServiceDemo extends Service { 9 10 private class MyBinder extends Binder implements IService { 11 12 public void callMethodInService() { 13 methodInservice(); 14 15 } 16 17 } 18 19 @Override 20 public IBinder onBind(Intent intent) { 21 System.out.println("远程的服务被绑定 onBind"); 22 return new MyBinder(); 23 } 24 25 @Override 26 public boolean onUnbind(Intent intent) { 27 System.out.println("远程的服务被解除绑定 onUnbind"); 28 return super.onUnbind(intent); 29 } 30 31 @Override 32 public void onCreate() { 33 System.out.println("远程的服务onCreate"); 34 super.onCreate(); 35 } 36 37 @Override 38 public void onDestroy() { 39 System.out.println("远程的服务onDestroy"); 40 super.onDestroy(); 41 } 42 43 public void methodInservice() { 44 System.out.println("我是远程服务里面的方法,我被调用了"); 45 } 46 }
这里我们定义的methodInservice(),是远程服务中的方法,这也是本地用户(或者其他用户)希望调用和访问的方法。
现在我们的需求,就是可以由别的用户程序调用这个methodInservice()方法。
上面接口IService.java为如下:
1 package com.himi.remoteservice; 2 3 public interface IService { 4 5 public void callMethodInService(); 6 }
(3)其他的MainActivity和activity_main布局文件我们这里没有修改
(4)这样一个远程的服务就搭建好了
工程一览表如下:
2.接下来我们再新建一个Android项目,命名为" 调用远程服务的方法 ",如下:
(1)项目一览表:
(2)先编写布局文件activity_main.xml,如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 tools:context="com.himi.remotecall.MainActivity" > 7 8 <Button 9 android:onClick="bind" 10 android:layout_width="match_parent" 11 android:layout_height="wrap_content" 12 android:text="绑定远程服务" /> 13 <Button 14 android:onClick="call" 15 android:layout_width="match_parent" 16 android:layout_height="wrap_content" 17 android:text="调用远程服务方法" /> 18 19 </LinearLayout>
布局效果如下:
(3)实现一下MainActivity里面的代码,如下:
1 package com.himi.remotecall; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.view.View; 10 11 public class MainActivity extends Activity { 12 private MyConn conn; 13 14 @Override 15 protected void onCreate(Bundle savedInstanceState) { 16 super.onCreate(savedInstanceState); 17 setContentView(R.layout.activity_main); 18 } 19 20 /** 21 * 绑定远程服务 22 * @param view 23 */ 24 public void bind(View view) { 25 Intent service = new Intent(); 26 service.setAction("com.himi.remoteservice"); 27 conn = new MyConn(); 28 bindService(service, conn, BIND_AUTO_CREATE); 29 } 30 31 private class MyConn implements ServiceConnection { 32 33 public void onServiceConnected(ComponentName name, IBinder service) { 34 35 } 36 37 public void onServiceDisconnected(ComponentName name) { 38 39 } 40 41 } 42 43 public void call(View view) { 44 45 } 46 }
这个客户端访问远程服务的框架,我们搭建好了;
但是我们不能访问远端的服务方法,因为这里的
public void onServiceConnected(ComponentName name, IBinder service),这个方法是绑定服务成功的时候调用的,Service.java会反馈一个IBinder service的信息,但是我们这里并不像远程服务那样,具备这个IService的接口,通过接口类型转化,也就是如下:
IService iService = (IService)service;
iService.callMethodInService();
现在不能这样调用methodInservice()方法,因为根本就不能接收到 IBinder service
所以不能调用远程服务的methodInservice()方法。
3.该怎么办?如何调用远程服务方法?这里要利用到AIDL(IPC)
(1)第一步,我们回到"远程服务”的项目,如下:
IService.java文件在工程目录下的路径,如下:
找到这个路径如下:
看到上面的IService.java文件了,这里我们修改它的扩展名,由java 改成 aidl,如下:
回到"远程服务"这个工程,刷新它,结果如下:
这会我们发现这里的IService.java变成了IService.aidl,但是报错了,不用担心我们慢慢修改错误;
(2)来到IService.aidl文件下,如下:
aidl作为两个进程之间的接口,当然是共有的,不是共有的无法互相通信了,这里aidl中没有权限修饰符,所以删除上下两个public,结果如下:
现在IService.aidl文件也就不报错了;
(3)接着,我们来到RemoteServiceDemo.java如下:
修改成如下形式,即可:
为什么这样修改?
相应我们在gen目录下,生成一个IService.java,如下:
因为IService.Stub在gen目录下生成的IService.java的实现了接口com.himi.remoteservice.IService,同时继承了android.os.Binder,如下:
1 /* 2 * This file is auto-generated. DO NOT MODIFY. 3 * Original file: C:\\Users\\Administrator.GDIZOK2X2LA0SQG\\workspace\\远程服务\\src\\com\\himi\\remoteservice\\IService.aidl 4 */ 5 package com.himi.remoteservice; 6 public interface IService extends android.os.IInterface 7 { 8 /** Local-side IPC implementation stub class. */ 9 public static abstract class Stub extends android.os.Binder implements com.himi.remoteservice.IService 10 { 11 private static final java.lang.String DESCRIPTOR = "com.himi.remoteservice.IService"; 12 /** Construct the stub at attach it to the interface. */ 13 public Stub() 14 { 15 this.attachInterface(this, DESCRIPTOR); 16 } 17 /** 18 * Cast an IBinder object into an com.himi.remoteservice.IService interface, 19 * generating a proxy if needed. 20 */ 21 public static com.himi.remoteservice.IService asInterface(android.os.IBinder obj) 22 { 23 if ((obj==null)) { 24 return null; 25 } 26 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); 27 if (((iin!=null)&&(iin instanceof com.himi.remoteservice.IService))) { 28 return ((com.himi.remoteservice.IService)iin); 29 } 30 return new com.himi.remoteservice.IService.Stub.Proxy(obj); 31 } 32 @Override public android.os.IBinder asBinder() 33 { 34 return this; 35 } 36 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException 37 { 38 switch (code) 39 { 40 case INTERFACE_TRANSACTION: 41 { 42 reply.writeString(DESCRIPTOR); 43 return true; 44 } 45 case TRANSACTION_callMethodInService: 46 { 47 data.enforceInterface(DESCRIPTOR); 48 this.callMethodInService(); 49 reply.writeNoException(); 50 return true; 51 } 52 } 53 return super.onTransact(code, data, reply, flags); 54 } 55 private static class Proxy implements com.himi.remoteservice.IService 56 { 57 private android.os.IBinder mRemote; 58 Proxy(android.os.IBinder remote) 59 { 60 mRemote = remote; 61 } 62 @Override public android.os.IBinder asBinder() 63 { 64 return mRemote; 65 } 66 public java.lang.String getInterfaceDescriptor() 67 { 68 return DESCRIPTOR; 69 } 70 @Override public void callMethodInService() throws android.os.RemoteException 71 { 72 android.os.Parcel _data = android.os.Parcel.obtain(); 73 android.os.Parcel _reply = android.os.Parcel.obtain(); 74 try { 75 _data.writeInterfaceToken(DESCRIPTOR); 76 mRemote.transact(Stub.TRANSACTION_callMethodInService, _data, _reply, 0); 77 _reply.readException(); 78 } 79 finally { 80 _reply.recycle(); 81 _data.recycle(); 82 } 83 } 84 } 85 static final int TRANSACTION_callMethodInService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); 86 } 87 public void callMethodInService() throws android.os.RemoteException; 88 }
(4)这样"远端服务",我们就修改好了,如下洁白无瑕:
4. 接着回到"调用远程服务的方法”这个项目下:
(1)在src目录下,创建一个包名,这个包名要 和 远程服务下的RemoteServiceDemo.java的包名一致, 这里都是com.himi.remoteservice,如下:
(2)把工程"远程服务"中的IService.aidl 复制到 工程”调用远程服务的方法"的src/com.himi.remoteservice,如下:
工程”调用远程服务的方法"的gen目录下也生成了IService.java
(3)回到工程”调用远程服务的方法"的MainActivity,编写代码如下:
1 package com.himi.remotecall; 2 3 import com.himi.remoteservice.IService; 4 5 import android.app.Activity; 6 import android.content.ComponentName; 7 import android.content.Intent; 8 import android.content.ServiceConnection; 9 import android.os.Bundle; 10 import android.os.IBinder; 11 import android.os.RemoteException; 12 import android.view.View; 13 14 public class MainActivity extends Activity { 15 private MyConn conn; 16 private IService iservice; 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 } 22 23 /** 24 * 绑定远程服务 25 * @param view 26 */ 27 public void bind(View view) { 28 Intent service = new Intent(); 29 service.setAction("com.himi.remoteservice"); 30 conn = new MyConn(); 31 bindService(service, conn, BIND_AUTO_CREATE); 32 } 33 34 private class MyConn implements ServiceConnection { 35 36 public void onServiceConnected(ComponentName name, IBinder service) { 37 iservice = IService.Stub.asInterface(service); 38 } 39 40 public void onServiceDisconnected(ComponentName name) { 41 42 } 43 44 } 45 46 public void call(View view) { 47 try { 48 iservice.callMethodInService(); 49 } catch (RemoteException e) { 50 // TODO 自动生成的 catch 块 51 e.printStackTrace(); 52 } 53 } 54 }
(4)布署程序到模拟器上演示:
首先布署"远端服务" 到模拟器上,如下:
最小化 "远程服务", 然后布署" 调用远程服务的方法"到模拟器上,如下:
接下我们点击 按钮----绑定远程服务 ,观察logcat打印的日志,如下:
这两条日志是 工程"远程服务"中的RemoteServiceDemo中打印的;
我们再点击多次这个 按钮----调用远程服务的方法 ,在观察logcat打印的日志,如下:
这两条日志是 工程"调用远程服务的方法"中的MainActivity中call点击事件,调用工程“远程服务”中RemoteServiceDemo的methodInservice()方法打印的;如下:
四、使用 AIDL 远程服务绑定调用的步骤:
(1)采用bind的方法绑定开启服务。
Intent intent = new Intent();//隐式的意图
intent.setAction("action");
bindService(intent, conn, BIND_AUTO_CREATE);
(2) .java的接口文件改成.aidl文件,删除public 访问修饰符。
(3) 在工程目录gen目录下会自动编译生成IService.java的接口文件
生成的IService.java,是通过aidl文件生成的。服务的中间人想暴露什么方法,就怎么定义接口
(4) 远程服务代码为private class MyBinder extends IService.Stub
同时在返回代理人对象,如下:
public IBinder onBind(Intent intent){……}
(5)实现ServiceConnection接口里面的方法
private class MyConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IService.Sub.asInterface(service);
System.out.println("Activity,得到代理人对象");
}
注意:iService = IService.Stub.asInterface(service)
(6) iService.callMethodInService();