深入解读Android的内部进程通信接口AIDL

意义:

由于每个应用进程都有自己的独立进程空间,在android平台上,一个进程通常不能访问另一个进程的内存空间,而我们经常需要夸进程传递对象,就需要把对象分解成操作对象可以理解的基本单元,并且有序的通过进程边界。

定义:

AIDL(Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。

说明以及实现流程:

AIDL接口和普通的java接口没有什么区别,只是扩展名为.aidl,保存在src目录下,如果其他应用程序需要IPC,则也需要在src目录下创建同样的AIDL文件,创建完毕之后,通过ADT工具,会在工程的gen目录下生成相对应的.java文件。

一般实现两个进程之间的通信需要实现下面几个步骤

(1)在Eclipse的android工程目录下面创建一个.aidl扩展名的文件,语法和java定义接口的语法差不多,不过需要自己手动import对应的包名。(比如需要用到list集合,则需要import java.util.List;)

(2)如果aidl文件符合规范,ADT工具会帮助编译器在gen目录下生成相对应的.java文件。

(3)需要继承实现一个服务类,跨进程调用的基础。

(4)在service端实现AIDL接口,如果有回调则在client端实现callback的AIDL接口。

(5)在AndroidManifest.xml注册service。

注意:

实现AIDL,我们需要注意以下五点

(1)AIDL只支持接口方法,不能公开static变量。

(2)AIDL接口方法如果有参数,则需要注意in、out、inout的使用规则,对于基本数据类型,默认是in类型,可以不需要添加声明,非基本可变对象需要在变量名之前添加方法类型

in表示输入参数,调用者把值传递给使用者使用。

out表示输出参数,调用者把容器传递给使用者填充,然后自己使用处理。

inout标书输入输出参数,传送相应的值并接收返回。

列举一个out的使用例子:
服务端传参数给客户端,客户端填充,服务端调用完之后,可以读取到客户端填写的内容,具体的例子后面将给出。

(3)AIDL定义的接口名必须和文件名一致。

(4)oneway表示用户请求相应功能时不需要等待响应可直接调用返回,非阻塞效果,该关键字可以用来声明接口或者声明方法,如果接口声明中用到了oneway关键字,则该接口声明的所有方法都采用oneway方式。

(5)AIDL传递非基本可变长度变量(非final对象),需要实现parcelable接口。
 parcel一般都用在Binder通信,通过read和write方法进行客户端与服务端的数据传递(通信)。
比如:frameworks层服务端与hardware客户端的Binder通信

reply->writeInt32(getCardReaderSize());
int mid = data.readInt32();

用来存放parcel数据的是内存(RAM),而不是永远介质(Nand等)。

parcelable定义了把数据写入parcel和从parcel读出数据的接口,一个类的实例,如果需要封装到消息中去,就必须实现这一接口,如果实现了这个接口,该类的实例就是可以“被打包”。
Parcelabel 的实现,需要在类中添加一个静态成员变量 CREATOR,这个变量需要继承 Parcelable.Creator 接口。

package com.zlc.provider;

import android.os.Parcel;
import android.os.Parcelable;

public class Students implements Parcelable{
  private int stu_id;
  private String stu_name;
  public Students(Parcel source){
    stu_id = source.readInt();
    stu_name = source.readString();
  }
  public int getStu_id() {
    return stu_id;
  }
  public void setStu_id(int stu_id) {
    this.stu_id = stu_id;
  }
  public String getStu_name() {
    return stu_name;
  }
  public void setStu_name(String stu_name) {
    this.stu_name = stu_name;
  }
  @Override
  public int describeContents() {
    // TODO Auto-generated method stub
    return 0;
  }
  @Override
  public void writeToParcel(Parcel dest, int flags) {
    // TODO Auto-generated method stub
    dest.writeInt(stu_id);
    dest.writeString(stu_name);
  }
  //Interface that must be implemented and provided as a public CREATOR field that generates instances of your Parcelable class from a Parcel.
  public final static Parcelable.Creator<Students> CREATOR = new Parcelable.Creator<Students>() {

    @Override
    public Students createFromParcel(Parcel source) {
      // TODO Auto-generated method stub
      return new Students(source);
    }

    @Override
    public Students[] newArray(int size) {
      // TODO Auto-generated method stub
      return new Students[size];
    }
  };
}

实例:

下面列举一个例子,主要实现客户端调用服务端然后回调回来,具体实现功能改变客户端的文字和图片显示,这个例子暂时效果是图片的更改直接使用客户端已经准备好的图片,接下来几篇博客会基于这个功能完善,到达服务端可以发送文字、图片、文件句柄(I/O流),并且直接由服务端通过方法名称直接调用客户端方法,客户端只需要注册对应的view并且提供相应的方法给服务端使用,后面的两部的完善主要用到反射和重写MemoryFile(达到parcelable序列化效果)来实现。

(1)首先按照我们上面的步骤需要创建aidl文件,分别创建调用和回调的aidl文件,为了阐述更详细一些,小编把parcelable对象也添加进去,仅仅作为测试。
IMyAidlService.aidl主要由服务端实现客户端调用

package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import com.zlc.aidl.AIDLCallback;
interface IMyAidlService{
  void registerClient(AIDLCallback cb);//注册回调
  void saveDemoInfo(in DemoParcelable demo);//实际调用方法
}

AIDLCallback.aidl主要由客户端实现,服务端调用

package com.zlc.aidl;
import com.zlc.aidl.DemoParcelable;
import java.util.List;
interface AIDLCallback {
  int returnResult(out List<DemoParcelable> list,int a);//回调给客户端
  void testMethod(out Bundle params);//用来测试参数in/out的使用
}

DemoParcelable.aidl声明传递对象:

package com.zlc.aidl;
parcelable DemoParcelable;

补充一点:out和in参数区别其实很明显我们直接查看adt生成在gen目录下对应的java文件就可以看出区别:当是out参数的时候是执行完之后从parcel对象读取值,而in参数时是写到parcel对象里面传过去。
我们看下当testMethod分别是out和in修饰时生成的文件
当时out的时候是从parcel对象里面读数据

mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
params.readFromParcel(_reply);
}

当时in的时候是从parcel对象里面取数据

if ((params!=null)) {
_data.writeInt(1);
params.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_testMethod, _data, _reply, 0);
_reply.readException();

(2)实现一个服务类用来实现进程之间通信MyAidlService.java,贴出部分代码,详细代码会在后面上传。

@Override
  public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    Log.d(TAG, "MyAidlService onBind");
    return mBinder;
  }

  private final IMyAidlService.Stub mBinder = new IMyAidlService.Stub() {
    private AIDLCallback cb;

    @Override
    public void saveDemoInfo(DemoParcelable demo) throws RemoteException {
      if (demo != null) {
        if ("meinv1".equals(demo.getDemo_name())) {
          demo.setDemo_name("meinv2");
        }
        list.add(demo);
        Log.d(TAG, "saveDemoInfo list.size = " + list.size() + " list = " + list);
        cb.returnResult(list, 5);
        Bundle params = new Bundle();
        cb.testMethod(params);
        int width = params.getInt("width", 0);
        int height = params.getInt("height", 0);
        Log.d(TAG, "width = " + width + " height = "+height);
      }
    }

    @Override
    public void registerClient(AIDLCallback cb) throws RemoteException {
      cb.asBinder().linkToDeath(new DeathRecipient() {
        @Override
        public void binderDied() {
          try {
            Log.i(TAG, "[ServiceAIDLImpl]binderDied.");
          } catch (Throwable e) {
          }
        }
      }, 0);
    }
  };

(3)实现客户端连接并且实现callback方法

private ServiceConnection mRemoteConnection = new ServiceConnection() {

    @Override
    public void onServiceDisconnected(ComponentName name) {
      // TODO Auto-generated method stub
      Log.d(TAG, "onServiceDisconnected");
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      // TODO Auto-generated method stub
      Log.d(TAG, "onServiceConnected");
      mRemoteService = (IMyAidlService) IMyAidlService.Stub
          .asInterface(service);
      if(mRemoteService != null)
        Log.d(TAG, "onServiceConnected success");
    }
  };
……
btn.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        // TODO Auto-generated method stub
        String actionName = "com.zlc.aidl.server.MyAidlService";
        Intent intent = new Intent(actionName);
        boolean ret = bindService(intent, mRemoteConnection,
            Context.BIND_AUTO_CREATE);
        Log.d(TAG, " ret ?=" + ret);
        if (ret) {
          new Thread(new Runnable() {

            @Override
            public void run() {
              // TODO Auto-generated method stub
              try {
                DemoParcelable demo = new DemoParcelable();
                List<String> list = new ArrayList<String>();
                list.add("like dance");
                demo.setDemo_id((Integer) img.getTag());
                demo.setDemo_name("meinv1");
                demo.setDemo_list(list);
                mRemoteService.registerClient(callback);
                mRemoteService.saveDemoInfo(demo);
              } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
              }
            }
          }).start();

        }
      }
    });
  }
……
private final AIDLCallback callback = new AIDLCallback.Stub() {

    @Override
    public int returnResult(List<DemoParcelable> list, int a)
        throws RemoteException {
      if (list != null)
        Log.d(TAG, "list.size = " + list.size()+"  a="+a);
      for (DemoParcelable demoParcelable : list) {
        doFresh(demoParcelable);
      }
      return 0;
    }

    @Override
    public void testMethod(Bundle outParams) throws RemoteException {
      // TODO Auto-generated method stub
      if (outParams != null) {
        outParams.putInt("width", 11);
        outParams.putInt("height", 12);
      }

    }
  };

(4)在androidManifest.xml里面注册service服务。   
注意一点:android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。
通过ps直接看pid进程号就可以看出。
让应用的组件在一个单独的进程中运行,如果带冒号: ,则创建一个专属于当前进程的进程,如果不带冒号,需要使用标准的命名规范命名进程名,例如com.xxx.xxx.xxx,而且该进程是全局共享的进程,即不同应用的组件都可以运行于该进程。
这可以突破应用程序的24M(或16M)内存限制。
总之,使用带:remote的属性的进程id pid不同,父进程ID PPID是一样的。而使用不带冒号的remote则会创建两个完全独立的进程。

(0)

相关推荐

  • 基于Android AIDL进程间通信接口使用介绍

    AIDL:Android Interface Definition Language,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口. ICP:Interprocess Communication ,内部进程通信. 使用: 1.先创建一个aidl文件,aidl文件的定义和java代码类似,但是!它可以引用其它aidl文件中定义的接口和类,但是不能引用自定义的java类文件中定义的接口和类,要引用自定义的接口或类,需要为此类也定义一个对应的aidl文件,并且此

  • Android应用程序四大组件之使用AIDL如何实现跨进程调用Service

    一.问题描述 Android应用程序的四大组件中Activity.BroadcastReceiver.ContentProvider.Service都可以进行跨进程.在上一篇我们通过ContentProvider实现了不同应用之间的跨进程调用,但ContentProvider主要是提供数据的共享(如sqlite数据库),那么我们希望跨进程调用服务(Service)呢?Android系统采用了远程过程调用(RPC)方式来实现.与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言

  • Android 使用【AIDL】调用外部服务的解决方法

    在Android 中有一种服务说是服务其实倒不如说是一个接口,这个接口名为:Android Interface Definition Language ,这个接口可提供跨进程访问服务,英文缩写为:AIDL. 此种服务的好处在于,多个应用程序之间建立共同的服务机制,通过AIDL在不同应用程序之间达到数据的共享和数据相互操作,下面将通过一个DEMO 演示AIDL 是如何为应用程序之间提供服务的.本文大纲为:•1.创建AIDL 服务端.•2.创建AIDL 客户端.•3.客户端调用服务端提供的服务接口.

  • Android程序设计之AIDL实例详解

    通常来说,AIDL这项技术在我们的应用开发过程中并不是很常用,虽然新浪微博提供了SSO登录,但是其原理就是使用AIDL.本文就以完整的实例形式讲述了AIDL的原理及实现方法. AIDL(AndRoid接口描述语言)是一种借口描述语言; 编译器可以通过aidl文件生成一段代码,通过预先定义的接口达到两个进程内部通信进程的目的. 如果需要在一个Activity中, 访问另一个Service中的某个对象, 需要先将对象转化成 AIDL可识别的参数(可能是多个参数), 然后使用AIDL来传递这些参数,

  • 实例讲解Android中的AIDL内部进程通信接口使用

    首先描述下我们想要实现的内容,我们希望在一个应用中通过点击按钮,去操作另一个进程中应用的音乐播放功能. 如图,我们点击"播放"时,系统就会去远程调用我们提供的一个service(与当前service不是同一个应用哦),然后操作service中的音乐播放,点击"停止"则会终止播放.想要重新播放的话,必须先点"销毁service",再点播放按钮哦.(至于这里为什么要先点销毁按钮才能播放,完全是为了给大家展示下,远程调用service时,怎么去解绑se

  • Android AIDL——进程通信机制详解

    Android  AIDL, Android进程机制通信机制,这里就整理下AIDL 的知识,帮助大家学习理解此部分知识! 什么是 AIDL AIDL 全称 Android Interface Definition Language,即 安卓接口描述语言.听起来很深奥,其实它的本质就是生成进程间通信接口的辅助工具.它的存在形式是一种 .aidl 文件,开发者需要做的就是在该文件中定义进程间通信的接口,编译的时候 IDE 就会根据我们的 .aidl 接口文件生成可供项目使用的 .java 文件,这和

  • Android编程实现AIDL(跨进程通信)的方法详解

    本文实例讲述了Android编程实现AIDL(跨进程通信)的方法.分享给大家供大家参考,具体如下: 一. 概述: 跨进程通信(AIDL),主要实现进程(应用)间数据共享功能. 二. 实现流程: 1. 服务器端实现: (1)目录结构,如下图: (2)实现*.aidl文件: A. IAIDLService.aidl实现: package com.focus.aidl; import com.focus.aidl.Person; interface IAIDLService { String getN

  • 深入解读Android的内部进程通信接口AIDL

    意义: 由于每个应用进程都有自己的独立进程空间,在android平台上,一个进程通常不能访问另一个进程的内存空间,而我们经常需要夸进程传递对象,就需要把对象分解成操作对象可以理解的基本单元,并且有序的通过进程边界. 定义: AIDL(Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码.如果在一个进程中(例如Activity

  • Android编程防止进程被第三方软件杀死的方法

    本文实例讲述了Android编程防止进程被第三方软件杀死的方法.分享给大家供大家参考,具体如下: 项目测试的时候发现,按home键回到桌面,再用360清理内存,软件被结束,再次进入的时候报错,看了下log,以为是有的地方没有控制好,但是又不知道360结束的是什么(这个现在还没弄明白).使用小米系统的进程管理优化内存就不报错. 后来想到用Service防止软件被kill掉,查了下资料,发现google 管方就有,ForegroundService 前台服务,让服务一直以前台任务的方式运行,可以在s

  • android 应用内部悬浮可拖动按钮简单实现代码

    本文介绍了android 应用内部悬浮可拖动按钮简单实现代码,分享给大家,具体如下: 可以悬浮在activity上面,在加载fragment时悬浮按钮不会消失 实现方式很简单,因为是在应用内部拖动的,只需要通过Activity获取WindowManager,然后将要拖动的view设置上去就行 设置代码: WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); DisplayMetr

  • 详解android webView独立进程通讯方式

    为什么需要将webView放在独立进程 webView 加载网页的时候可能占用大量内存,导致应用程序OOM. webView 在访问结束的时候可以直接杀死该进程,防止内存泄漏. webView 在崩溃的时候不影响主进程. webView独立进程需要注意什么 由于进程之间内存是独立的,所以导致了Appcation, 静态类需要在新的进程重新创建. 内存中的数据不共享,需要跨进程通讯. 如何声明一个独立进程 在默认情况下,同一应用的所有组件都在相同的进程中运行. 在Manifest中可以设置各组件

  • 详细解读Android系统中的application标签

    < application /> :应用的声明. 这个元素包含了子元素,这些子元素声明了应用的组件,元素的属性将会影响应用下的所有组件.很多属性为组件设置了默认值,有些属性设置了全局值并且不能被组件修改. <application>的子节点描述了应用所包含的组件,它的属性会影响到它所有的子节点组件.icon/lable/permission 等 属性是给子节点组件设置一个默认值,可以被复写.而 debuggable/enabled 等 属性是作为整个application的全局属性

  • 深入解读Android的Volley库的功能结构

    Volley 是一个 HTTP 库,它能够帮助 Android app 更方便地执行网络操作,最重要的是,它更快速高效.我们可以通过开源的 AOSP 仓库获取到 Volley . Volley 有如下的优点: 自动调度网络请求. 高并发网络连接. 通过标准的 HTTP cache coherence(高速缓存一致性)缓存磁盘和内存透明的响应. 支持指定请求的优先级. 撤销请求 API.我们可以取消单个请求,或者指定取消请求队列中的一个区域. 框架容易被定制,例如,定制重试或者回退功能. 强大的指

  • 深入剖析Android中init进程实现的C语言源码

    概述 init是一个进程,确切的说,它是Linux系统中用户空间的第一个进程.由于Android是基于Linux内核的,所以init也是Android系统中用户空间的第一个进程.init的进程号是1.作为天字第一号进程,init有很多重要的工作: init提供property service(属性服务)来管理Android系统的属性. init负责创建系统中的关键进程,包括zygote. 以往的文章一上来就介绍init的源码,但是我这里先从这两个主要工作开始.搞清楚这两个主要工作是如何实现的,我

  • Android实现跨进程接口回掉的方法

    前言 同一个进程内实现接口回掉很简单,这里不做叙述,本文主要讲的是跨进程的接口回掉实现方式.有一种跨进程通信的方式就是使用AIDL,但是单纯的AIDL通信只可以实现客户端访问服务端主动获取Binder对象,如果服务端有变化无法及时通知客户端.现在可以通过AIDL跨进程接口回掉来解决服务端发生变化通知客户端的问题. 谷歌提供了RemoteCallbackList来实现对IInterface的管理.public class RemoteCallbackList<E extends IInterfac

  • Android Messenger实现进程间双向通信

    简介 Messenger是安卓进程间通信 (IPC) 最为简单的方式,可以实现进程间双向通信.详见官网介绍 代码实现 服务端应用实现 MessengerService接收客户端发送的消息: package com.test.messengerservice; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import

随机推荐