天天看點

Android——使用Binder實作程序間通訊簡單案例

1 前言

使用AIDL實作程序間通訊簡單案例 和 使用AIDL實作程序間傳遞對象案例 中介紹了使用 AIDL 進行程序間通訊,文中提到在編寫完 aidl 檔案(如:IMessageManager.aidl)并 Make Buidl 後,會生成一個接口(如:IMessageManager.java),接口中包含一個靜态抽象内部類 Stub,Stub 中又包含一個靜态内部類 Proxy,Stub 和 Proxy 都實作了此接口,同時 Stub 繼承了 Binder 類。本文将從 IMessageManager 接口中剝離 Stub(用于服務端) 和 Proxy(用于用戶端),使讀者更深刻的了解 AIDL 和 Binder,如下。

Android——使用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 的主要屬性和方法如下:

Android——使用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 項目結構

Android——使用Binder實作程式間通訊簡單案例

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>
           

界面如下:

Android——使用Binder實作程式間通訊簡單案例

6 效果展示

(1)發送消息

在 EditView 中輸入【asdfg】,點選【發送】按鈕,在服務端可以收到發送的消息,如下:

Android——使用Binder實作程式間通訊簡單案例

(2)接收消息

點選【接收】按鈕,用戶端 binder_C 界面可以看到服務端 binder_S 傳過來的字元串【abcde】,如下: 

Android——使用Binder實作程式間通訊簡單案例