1 前言

        使用AIDL实现进程间通讯简单案例 和 使用AIDL实现进程间传递对象案例 中介绍了使用 AIDL 进行进程间通讯,文中提到在编写完 aidl 文件(如:IMessageManager.aidl)并 Make Buidl 后,会生成一个接口(如:IMessageManager.java),接口中包含一个静态抽象内部类 Stub,Stub 中又包含一个静态内部类 Proxy,Stub 和 Proxy 都实现了此接口,同时 Stub 继承了 Binder 类。本文将从 IMessageManager 接口中剥离 Stub(用于服务端) 和 Proxy(用于客户端),使读者更深刻的理解 AIDL 和 Binder,如下。

        说明:左边 Stub 为抽象类,无需重写 sendMsg、getMsg,右边 Stub 为具体类,需要重写 sendMsg、getMsg。

        (1)AIDL中生成服务端与客户端

        服务端:IMessageManager.Stub mBind = new MessageManager.Stub()

        客户端:mMessageManager = IMessageManager.Stub.asInterface(service)(service 是服务端传过来的Binder,即 mBind)

        (2)本文中生成服务端与客户端

        服务端:Stub mBind = new Stub()

        客户端:mMessageManager = new Proxy(service)(service 是服务端传过来的Binder,即 mBind)

        本文全部代码见→使用Binder实现进程间通讯简单案例

2 Binder 简介

        Binder 翻译即粘合剂,是客户端和服务端沟通的桥梁,用于进程建通讯。Binder 的主要属性和方法如下:

        在 transact 方法中调用了 onTransact,如下:

public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    if (data != null) {
        data.setDataPosition(0);
    }
    boolean r = onTransact(code, data, reply, flags);
    if (reply != null) {
        reply.setDataPosition(0);
    }
    return r;
}

        使用 Binder 进行进程间通讯时,需要做以下工作:

        (1)Stub 类(服务端创建 Binder的子类)

  • Stub extends Binder implements IMessageManager
  • 构造方法中调用 this.attachInterface(this, DESCRIPTOR)
  • asBinder 方法返回 this
  • 重写 IMessageManager 接口中的方法
  • 重写 Binder 的 onTransact 方法

        (2)Proxy 类(客户端接受 Binder,并对其进行封装)

  • Proxy implements IMessageManager
  • 构造方法接受 Binder:mRemote = remote
  • asBinder 方法返回 mRemote
  • 重写 IMessageManager 接口中的方法,调用 mRemote.transact(code, data, reply, 0)

  3 项目结构

4 服务端 binder_S 代码

        IMessagManager.java

package com.zhyan8.binder_s;

import android.os.IInterface;
import android.os.RemoteException;

public interface IMessageManager extends IInterface {
    public void sendMsg(String msg) throws RemoteException;
    public String getMsg() throws RemoteException;
}

        Stub.java

package com.zhyan8.binder_s;

import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;

public class Stub extends Binder implements IMessageManager {
    private final String DESCRIPTOR = "com.yyy.binder";
    private final int TRANSACTION_sendMsg = IBinder.FIRST_CALL_TRANSACTION;
    private final int TRANSACTION_getMsg = IBinder.FIRST_CALL_TRANSACTION + 1;

    public Stub(){
        this.attachInterface(this, DESCRIPTOR);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case TRANSACTION_sendMsg:
                data.enforceInterface(DESCRIPTOR);
                String msg = data.readString();
                this.sendMsg(msg);
                reply.writeNoException();
                return true;
            case TRANSACTION_getMsg:
                data.enforceInterface(DESCRIPTOR);
                String _result = this.getMsg();
                reply.writeNoException();
                reply.writeString(_result);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public void sendMsg(String msg) throws RemoteException {
        Log.d("MyService", "客户端发来消息: " + msg);
        System.out.println(msg);
    }

    @Override
    public String getMsg() throws RemoteException {
        return "abcde"; //客户端待接收的消息
    }
}

        注意:DESCRIPTOR 取值必须和 Proxy 中一致。

        MyService.java

package com.zhyan8.binder_s;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MyService extends Service {

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

    Stub mBind = new Stub();
}

        在 AndroidManifest.xml 中配置服务如下:

<service
       android:name=".MyService"
       android:enabled="true"
       android:exported="true">
       <intent-filter>
            <action android:name="com.xxx.binder"/>
       </intent-filter>
</service>

        MainActivity.java

package com.zhyan8.binder_s;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

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

5 客户端 binder_C 代码

        IMessagManager.java 同第 4 节。

        Proxy.java

package com.zhyan8.binder_c;

import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;

public class Proxy implements IMessageManager {
    private IBinder mRemote;
    private final String DESCRIPTOR = "com.yyy.binder";
    private final int TRANSACTION_sendMsg = IBinder.FIRST_CALL_TRANSACTION;
    private final int TRANSACTION_getMsg = IBinder.FIRST_CALL_TRANSACTION + 1;

    Proxy(IBinder remote) {
        mRemote = remote;
    }

    @Override
    public IBinder asBinder() {
        return mRemote;
    }

    @Override
    public void sendMsg(String msg) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            data.writeString(msg);
            mRemote.transact(TRANSACTION_sendMsg, data, reply, 0);
            reply.readException();
        } finally {
            reply.recycle();
            data.recycle();
        }
    }

    @Override
    public String getMsg() throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        String result;
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(TRANSACTION_getMsg, data, reply, 0);
            reply.readException();
            result = reply.readString();
        } finally {
            reply.recycle();
            data.recycle();
        }
        return result;
    }
}

        注意:DESCRIPTOR 取值必须和 Stub 中一致。 

        MainActivity.java

package com.zhyan8.binder_c;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private Proxy mMessageManager;
    private EditText et_msg;
    private Button btn_send;
    private TextView tv_msg;
    private Button btn_recv;

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

    public void init() {
        et_msg = (EditText) findViewById(R.id.et_msg);
        btn_send = (Button) findViewById(R.id.btn_send);
        tv_msg = (TextView) findViewById(R.id.tv_msg);
        btn_recv = (Button) findViewById(R.id.btn_recv);
        btn_send.setOnClickListener(cl);
        btn_recv.setOnClickListener(cl);
    }

    View.OnClickListener cl = new View.OnClickListener(){
        @Override
        public void onClick(View v) {
            if (v.getId()==R.id.btn_send) {
                String str = et_msg.getText().toString();
                sendMsg(str);
            }else if(v.getId()==R.id.btn_recv) {
                String str = getMsg();
                tv_msg.setText(str);
            }
        }
    };

    private void sendMsg(String str){
        if (mMessageManager==null) {
            attemptToBindService();
        }
        try {
            mMessageManager.sendMsg(str);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private String getMsg(){
        if (mMessageManager==null) {
            attemptToBindService();
        }
        try {
            String str = mMessageManager.getMsg();
            return str;
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return "";
    }

    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("com.xxx.binder");
        intent.setPackage("com.zhyan8.binder_s");
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessageManager = new Proxy(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mMessageManager = null;
        }
    };

    @Override
    protected void onStart() {
        super.onStart();
        if (mMessageManager==null) {
            attemptToBindService();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mMessageManager!=null) {
            unbindService(conn);
        }
    }
}

        activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/et_msg"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:textSize="30sp"
        android:background="#ffcc66"/>

    <Button
        android:id="@+id/btn_send"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:text="发送"
        android:textSize="30sp"
        android:layout_marginTop="30dp"/>

    <TextView
        android:id="@+id/tv_msg"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:textSize="30sp"
        android:background="#ffcc66"
        android:layout_marginTop="50dp"/>

    <Button
        android:id="@+id/btn_recv"
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:text="接收"
        android:textSize="30sp"
        android:layout_marginTop="30dp"/>
</LinearLayout>

        界面如下:

6 效果展示

        (1)发送消息

        在 EditView 中输入【asdfg】,点击【发送】按钮,在服务端可以收到发送的消息,如下:

        (2)接收消息

        点击【接收】按钮,客户端 binder_C 界面可以看到服务端 binder_S 传过来的字符串【abcde】,如下: 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐