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();

posted on 2015-09-11 21:43  鸿钧老祖  阅读(493)  评论(0编辑  收藏  举报

导航