Binder的学习与理解笔记之三:aidl原理及应用实例

1.Binder机制用于进程间通信,在Android的应用层开发中,所涉及的进程间通信的应用场景较少,一旦有这一类需求呢?是否要应用自身来维护Binder通信的流程?是的,如果应用层的开发者也需要对Binder机制足够了解,那进程通信的开发成本就太大了,毕竟,Binder的原理及流程还是相当复杂的。google向我们提供了应用层开发进程间通信的工具,aidl便是最普遍,也是定制灵活度最高的。其他还有一些,比如Content Provider通过开放数据访问接口支持进程间通信,IMessage等,应用范围都比较局限。

2.先来看一个简单的aidl示例,单向行为,即Client向服务器发送指令,服务器处理指令,返回结果。以下例子只是为了说明aidl的原理,非常简单。

    a.先来看client与server通信的数据类,需要实现Parcel接口,可以在进程间传输的数据格式一定是序列化的

    //com.demo.aidldemo.aidl.Data.java    

package com.demo.aidldemo.aidl;

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

public class Data implements Parcelable{

    private int index;

    private String str;

    protected Data(Parcel in) {
        index = in.readInt();
        str = in.readString();
    }

    public Data(int i, String ss) {
        this.index = i;
        this.str = ss;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(index);
        dest.writeString(str);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<Data> CREATOR = new Creator<Data>() {
        @Override
        public Data createFromParcel(Parcel in) {
            return new Data(in);
        }

        @Override
        public Data[] newArray(int size) {
            return new Data[size];
        }
    };

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}

    b.对应的aidl文件为:

   // com.demo.aidldemo.aidl.Data.aidl

package com.demo.aidldemo.aidl;

// Declare any non-default types here with import statements

parcelable Data;

    之所以要写一个Data的aidl文件,是因为要在aidl接口文件中出现的数据类型,必须要写有java类对应的aidl文件

    c.client与server通用的aidl接口定义

    // com.demo.aidldemo.aidl.IDataProcessManager.java

  package com.demo.aidldemo.aidl;

// 此处必须要显式的引入Data,不管是否是在同一包名下
import com.demo.aidldemo.aidl.Data;

// Declare any non-default types here with import statements

interface IDataProcessManager {
    int getAddIndex(in Data data);
    String getNewString(in Data data);
}

    d.Server端(运行在:process进程中)

   //com.demo.aidldemo.aidl.DataProcessService.java

package com.demo.aidldemo.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

/**
 * Created by wangguoqiang on 2018/5/22.
 */

public class DataProcessService extends Service {

    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private Binder mBinder = new IDataProcessManager.Stub() {
        @Override
        public int getAddIndex(Data data) throws RemoteException {
            return data.getIndex() + 10;
        }

        @Override
        public String getNewString(Data data) throws RemoteException {
            return data.getStr() + " hi, change";
        }
    };
}

    e.client端(运行在主进程中)

    //com.demo.aidldemo.MainActivity.java

package com.demo.aidldemo;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import com.demo.aidldemo.aidl.Data;
import com.demo.aidldemo.aidl.DataProcessService;
import com.demo.aidldemo.aidl.IDataProcessManager;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, DataProcessService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Data data = new Data(10086, "China Mobile");
            IDataProcessManager manager = IDataProcessManager.Stub.asInterface(iBinder);
            try {
                int index = manager.getAddIndex(data);
                String str = manager.getNewString(data);
                Log.d("KCSTEST", "index : " + index + " str : " + str);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
}

    //AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.demo.aidldemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".aidl.DataProcessService"
            android:process=":process"/>
    </application>

</manifest>

运行项目,日志输出结果为:

05-22 16:35:08.209 20529-20529/com.demo.aidldemo D/KCSTEST: index : 10096 str : China Mobile hi, change

达到了项目目的。

3.从上面简单的demo来看一下进程间通信方式Binder的各组成因素及其在aidl的配合方式

    Binder驱动    --       demo中看不到, 从前几章的笔记中可以看出,Binder驱动为底层,面向的为C++层的BpBinder,在aidl向上封装的接口位于Java层,中间间隔着Binder Java层及c++层的一系列调用,具体可参考前两章的笔记

    ServiceManager    --    demo中看不到,隐藏在对外提供的接口调用之下

    Binder Server    --    本例中为DataProcessService,位于:process进程中

    Binder Client    --    本例中为MainActivity,位于应用主进程中

    在项目中我们定义的IDataProcessManager.aidl文件,Android在编译期会生成IDataProcessManager.java文件,位置为:

    

4.重点分析这个类,分析完之后大家就会明白Binder和aidl接口的关系,来看一下该类的定义(为方便,下面的代码经过了格式化,原文件是没有的)

    package com.demo.aidldemo.aidl;


/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/wangguoqiang/othercode/demo/aidl/app/src/main/aidl/com/demo/aidldemo/aidl/IDataProcessManager.aidl
 */
// Declare any non-default types here with import statements


public interface IDataProcessManager extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.demo.aidldemo.aidl.IDataProcessManager
    {
        private static final java.lang.String DESCRIPTOR = "com.demo.aidldemo.aidl.IDataProcessManager";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.demo.aidldemo.aidl.IDataProcessManager interface,
         * generating a proxy if needed.
         */
        public static com.demo.aidldemo.aidl.IDataProcessManager asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.demo.aidldemo.aidl.IDataProcessManager))) {
                return ((com.demo.aidldemo.aidl.IDataProcessManager)iin);
            }
            return new com.demo.aidldemo.aidl.IDataProcessManager.Stub.Proxy(obj);
        }
        @Override public android.os.IBinder asBinder()
        {
            return this;
        }
        @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
            switch (code)
            {
                case INTERFACE_TRANSACTION:
                {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getAddIndex:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.demo.aidldemo.aidl.Data _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    int _result = this.getAddIndex(_arg0);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_getNewString:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.demo.aidldemo.aidl.Data _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    java.lang.String _result = this.getNewString(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        private static class Proxy implements com.demo.aidldemo.aidl.IDataProcessManager
        {
            private android.os.IBinder mRemote;
            Proxy(android.os.IBinder remote)
            {
                mRemote = remote;
            }
            @Override public android.os.IBinder asBinder()
            {
                return mRemote;
            }
            public java.lang.String getInterfaceDescriptor()
            {
                return DESCRIPTOR;
            }
            @Override public int getAddIndex(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((data!=null)) {
                        _data.writeInt(1);
                        data.writeToParcel(_data, 0);
                    }
                    else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_getAddIndex, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            @Override public java.lang.String getNewString(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((data!=null)) {
                        _data.writeInt(1);
                        data.writeToParcel(_data, 0);
                    }
                    else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_getNewString, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        static final int TRANSACTION_getAddIndex = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getNewString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    public int getAddIndex(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException;
    public java.lang.String getNewString(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException;
}

回顾一下第一章提到的Java层的类结构,来对比着看一下上面的代码:



没错,上面两个类的角色是一样的,向客户端提供标准的调用方式,向服务端提供标准的实现方式,最终达到的目的是,客户端调用该接口,获取的结果为服务端同名方法的执行结果。

aidl所成生在IDataProcessManager里面呈放有内部类,因此看起来略为怪异,其自身的代码如下:


就是定义了我们需要的业务函数。

再来看一下IDataProcessManager.Stub这个类



这两个类在Binder的角色也是一致的,回忆一下我们之前提到过的ServiceManagerNative

看一下这个类的代码(摘掉了内部类Proxy,稍后会讲这个内部类):

public static abstract class Stub extends android.os.Binder implements com.demo.aidldemo.aidl.IDataProcessManager
    {
        private static final java.lang.String DESCRIPTOR = "com.demo.aidldemo.aidl.IDataProcessManager";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.demo.aidldemo.aidl.IDataProcessManager interface,
         * generating a proxy if needed.
         */
        public static com.demo.aidldemo.aidl.IDataProcessManager asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.demo.aidldemo.aidl.IDataProcessManager))) {
                return ((com.demo.aidldemo.aidl.IDataProcessManager)iin);
            }
            return new com.demo.aidldemo.aidl.IDataProcessManager.Stub.Proxy(obj);
        }
        @Override public android.os.IBinder asBinder()
        {
            return this;
        }
        @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
            switch (code)
            {
                case INTERFACE_TRANSACTION:
                {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getAddIndex:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.demo.aidldemo.aidl.Data _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    int _result = this.getAddIndex(_arg0);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_getNewString:
                {
                    data.enforceInterface(DESCRIPTOR);
                    com.demo.aidldemo.aidl.Data _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.demo.aidldemo.aidl.Data.CREATOR.createFromParcel(data);
                    }
                    else {
                        _arg0 = null;
                    }
                    java.lang.String _result = this.getNewString(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        
        static final int TRANSACTION_getAddIndex = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getNewString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

    }

以下两函数是给Binder Client用的:


向客户端提供Binder对象

以下方法为给服务端用的:


自动实现了经过Binder驱动层之后,调至服务端的事务的具体实现(详情可参考前两章的调度方式)

好,看完这个类之后再来看IDataProcessManager.Stub.Proxy这个内部类,看一下下面两幅图:



这两个类的角色也是一致的。

private static class Proxy implements com.demo.aidldemo.aidl.IDataProcessManager
  {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
          mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
          return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
          return DESCRIPTOR;
      }
      @Override public int getAddIndex(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
      {
          android.os.Parcel _data = android.os.Parcel.obtain();
          android.os.Parcel _reply = android.os.Parcel.obtain();
          int _result;
          try {
              _data.writeInterfaceToken(DESCRIPTOR);
              if ((data!=null)) {
                  _data.writeInt(1);
                  data.writeToParcel(_data, 0);
              }
              else {
                  _data.writeInt(0);
              }
              mRemote.transact(Stub.TRANSACTION_getAddIndex, _data, _reply, 0);
              _reply.readException();
              _result = _reply.readInt();
          }
          finally {
              _reply.recycle();
              _data.recycle();
          }
          return _result;
      }
      @Override public java.lang.String getNewString(com.demo.aidldemo.aidl.Data data) throws android.os.RemoteException
      {
          android.os.Parcel _data = android.os.Parcel.obtain();
          android.os.Parcel _reply = android.os.Parcel.obtain();
          java.lang.String _result;
          try {
              _data.writeInterfaceToken(DESCRIPTOR);
              if ((data!=null)) {
                  _data.writeInt(1);
                  data.writeToParcel(_data, 0);
              }
              else {
                  _data.writeInt(0);
              }
              mRemote.transact(Stub.TRANSACTION_getNewString, _data, _reply, 0);
              _reply.readException();
              _result = _reply.readString();
          }
          finally {
              _reply.recycle();
              _data.recycle();
          }
          return _result;
      }

  }

实现Java层到c++层的调用,最终调至Binder驱动层。

5.为实现服务端具体的执行逻辑,我们只需要继承Stub这个类,并实现相关方法即可,如下所示:

private Binder mBinder = new IDataProcessManager.Stub() {
    @Override
    public int getAddIndex(Data data) throws RemoteException {
        Thread th = Thread.currentThread();
        Log.d("KCSTEST", "getAddIndex thread : " + th.getName());
        return data.getIndex() + 10;
    }

    @Override
    public String getNewString(Data data) throws RemoteException {
        Thread th = Thread.currentThread();
        Log.d("KCSTEST", "getNewString thread : " + th.getName());
        return data.getStr() + " hi, change";
    }
};

然后在Service的onBind方法中返回该mBinder

public IBinder onBind(Intent intent) {
    return mBinder;
}

MainActivity中通过Intent bindService绑定至该Service,则可拿到该Binder对象,通过asInterface转为IDataProcessManager类型的对象,即可使用该接口提供的与服务端同一标准的业务功能。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, DataProcessService.class);
        // 绑定至所定义的bind服务端,远程Service
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            // 此处可以拿到与Service通信的Binder对象 iBinder
            Data data = new Data(10086, "China Mobile");
            IDataProcessManager manager = IDataProcessManager.Stub.asInterface(iBinder);
            try {
                int index = manager.getAddIndex(data);
                String str = manager.getNewString(data);
                Log.d("KCSTEST", "index : " + index + " str : " + str);
                Thread th = Thread.currentThread();
                Log.d("KCSTEST", "onServiceConnected thread : " + th.getName());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };
}

好,原理方面就讲完了,说两个注意点:

1.Binder服务端是开启线程来执行相关逻辑,因此,无特殊需要,不需要再新开线程,如上面代码中,我们加了两个注释,可以看一下日志:

05-25 15:58:23.425 16640-16649/com.demo.aidldemo:process D/KCSTEST: getAddIndex thread : Binder:16640_1

05-25 15:58:23.425 16640-16654/com.demo.aidldemo:process D/KCSTEST: getNewString thread : Binder:16640_2

两函数均运行在线程中,且为不同的线程

2.如果在aidl实现了服务器变化向客户端发回的回调,也是运行在线程中的,如果有ui操作,请回到主线程中执行,回调的代码也不复杂,例子也较多,此处就不再写了。

猜你喜欢

转载自blog.csdn.net/kcstrong/article/details/80435860