zoukankan      html  css  js  c++  java
  • 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();

  • 相关阅读:
    消息队列简介
    docker快速构建oracle数据库
    MySQL读写分离之amoeba
    Python替换文件内容
    Nginx图片及样式文件不记录访问日志
    shell方式切割tomcat日志
    split命令
    orange安装文档
    openresty安装文档
    MySQL中kill所有慢查询进程和锁表进程
  • 原文地址:https://www.cnblogs.com/hebao0514/p/4802166.html
Copyright © 2011-2022 走看看