【Android】使用Binder实现进程间传递对象案例

42 阅读3分钟

1 前言

使用AIDL实现进程间通讯简单案例使用AIDL实现进程间传递对象案例 中介绍了使用 AIDL 进行进程间通讯,其本质仍然是Binder,aidl 文件对应生成的接口中,将服务端调用的抽象类(Stub)和客户端调用的代理类(Proxy)糅合在一个 java 文件中,使得新手读起来云里雾里。使用Binder实现进程间通讯简单案例 中试图将生成的接口拆分为2个类(Stub 和 Proxy),分别布置在服务端和客户端,能够帮助读者更好地理解 AIDL 和 Binder。本文将延续 使用Binder实现进程间通讯简单案例 中的思路,介绍使用Binder 实现进程间传输对象。

​ 本文全部代码见→使用Binder实现进程间传递对象案例

2 项目结构

img

3 服务端 aidl_s 代码

​ User.java

package com.zhyan8.binder_c;

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

public class User implements Parcelable{
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

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

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

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

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "User{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
}

​ IMessageManager.java

package com.zhyan8.binder_c;

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

public interface IMessageManager extends IInterface {
    public void sendMsg(User user) throws RemoteException;
    public User 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 {
        User user;
        switch (code) {
            case TRANSACTION_sendMsg:
                data.enforceInterface(DESCRIPTOR);
                user = User.CREATOR.createFromParcel(data);
                this.sendMsg(user);
                reply.writeNoException();
                return true;
            case TRANSACTION_getMsg:
                data.enforceInterface(DESCRIPTOR);
                user = this.getMsg();
                reply.writeNoException();
                user.writeToParcel(reply, 1);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

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

    @Override
    public User getMsg() throws RemoteException {
        return new User("小红", 23); //客户端待接收的消息
    }
}

​ 注意: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 文件中 application 节点下注册 service,如下。

<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);
    }
}

4 客户端 aidl_c 代码

​ User.java 和 IMessageManager.java 同第3节。

​ Proxy.java

package com.zhyan8.binder_c;

import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
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(User user) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            user.writeToParcel(data, 0);
            mRemote.transact(TRANSACTION_sendMsg, data, reply, 0);
            reply.readException();
        } finally {
            reply.recycle();
            data.recycle();
        }
    }

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

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

​ MainActivity.java

package com.zhyan8.binder_c;

import android.app.Activity;
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.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private IMessageManager mMessageManager;
    private EditText et_name;
    private EditText et_age;
    private Button btn_send;
    private TextView tv_name;
    private TextView tv_age;
    private Button btn_recv;

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

    public void init() {
        et_name = (EditText) findViewById(R.id.et_name);
        et_age = (EditText) findViewById(R.id.et_age);
        btn_send = (Button) findViewById(R.id.btn_send);
        tv_name = (TextView) findViewById(R.id.tv_name);
        tv_age = (TextView) findViewById(R.id.tv_age);
        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) {
            hideInputMethod(MainActivity.this, v); //关闭输入法
            if (v.getId()==R.id.btn_send) {
                try {
                    String name_t = et_name.getText().toString();
                    int age_t = Integer.parseInt(et_age.getText().toString());
                    User user = new User(name_t, age_t);
                    sendMsg(user);
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "请输入姓名和年龄", Toast.LENGTH_SHORT).show();
                }
            }else if(v.getId()==R.id.btn_recv) {
                User user = getMsg();
                tv_name.setText(user.getName());
                tv_age.setText("" + user.getAge());
            }
        }
    };

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

    private User getMsg(){
        if (mMessageManager==null) {
            attemptToBindService();
        }
        try {
            User user = mMessageManager.getMsg();
            return user;
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return null;
    }

    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;
        }
    };

    private void hideInputMethod(Activity act, View v) { //关闭输入法
        InputMethodManager imm = (InputMethodManager) act.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(v.getWindowToken(),0);
    }

    @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="com.zhyan8.binder_c.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="姓名:"
            android:textSize="30sp"/>
        <EditText
            android:id="@+id/et_name"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:textSize="30sp"
            android:background="#ffcc66"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="20dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="年龄:"
            android:textSize="30sp"/>
        <EditText
            android:id="@+id/et_age"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:textSize="30sp"
            android:inputType="number"
            android:background="#ffcc66"/>
    </LinearLayout>

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

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="50dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="姓名:"
            android:textSize="30sp"/>
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:textSize="30sp"
            android:background="#ffcc66"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginTop="20dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="年龄:"
            android:textSize="30sp"/>
        <TextView
            android:id="@+id/tv_age"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:textSize="30sp"
            android:background="#ffcc66"/>
    </LinearLayout>

    <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>

​ 界面如下:

img

5 效果展示

(1)发送消息

​ 在2个 EditView 中分别输入::小明、20,点击【发送】按钮,在服务端可以收到发送的消息,如下。

img

(2)接收消息

​ 点击【接收】按钮,客户端 binder_C 界面可以看到服务端 binder_S 传过来的 user 信息,如下。

img

​ 声明:本文转自【Android】使用Binder实现进程间传递对象案例