Android之IPC机制(二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/callmeMrLu/article/details/81542686

Android之IPC机制

IPC全称是Intent Process Communication即进程通信。

为什么需要进程通信呢?

  android是基于linux内核的,linux中两个进程锁分配的虚拟机的地址是不一致的,是两个完全不同

的虚拟机,所以也就意味着两个进程运行不相互影响(进程隔离)。linux中对于进程之间的交流有着

限制,那么在Android中也一样。linux中的进程通信有管道,socket,那么Android中相对应的就有

Binder机制。

由于进程之间交流存在限制,所以需要进程通信,方便进程之间传递数据等信息。

Binder是什么?

binder的中文意思是粘合剂,也就意味者把两个进程粘合在一起,即所谓的进程通信。
1.直接来说,Binder是Android中的一个类,实现了IBinder接口。
2.从ICP角度说,Binder是Android中一种跨进程通信的机制。
3.从Android Framework角度说,它是ServiceManager连接各种Manager(比如ActivityManager
WindowManager等)和相应的ManagerService的桥梁。
4.从应用层来说,Binder是客户端和服务端进行通信的媒介。

在Android中,Binder主要用在Service中,包括AIDL和Messenger。(普通service不涉及进程通信)

通过AIDL来实现进程通信,理解了AIDL的模式对Binder通信的机制会更加容易认识。

在项目中新建一个AIDL文件。

// IProcess.aidl
package com.example.lu.okhttpdemo;

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

interface IProcess {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     int add(int x, int y);
     int min(int x, int y);
}

接着Make Project。然后会在build/generated/source/aidl生成一个相对应的文件。

我们先新建一个服务端Service

package com.example.lu.okhttpdemo;

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


public class MyService extends Service {

    private static final String TAG = "service";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return mBinder;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.d(TAG, "onDRebind");
        super.onRebind(intent);
    }

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

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }

    private final IProcess.Stub mBinder = new IProcess.Stub() {
        @Override
        public int add(int x, int y) throws RemoteException {
            return x + y;
        }

        @Override
        public int min(int x, int y) throws RemoteException {
            return x - y;
        }
    };
}

在AndroidManifest文件中注册service


        <service android:name=".MyService"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.lu.calc" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

这里我加了process属性,就是为service单独开启一个进程。用来模拟多进程通信。

客户端的activity代码

package com.example.lu.okhttpdemo;

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 android.view.View;
import android.widget.Button;
import android.widget.Toast;


public class Main2Activity extends AppCompatActivity implements View.OnClickListener {

    private IProcess iProcess;

    private ServiceConnection serviceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("client", "onServiceConnected");
            iProcess = IProcess.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("client", "onServiceDisConnected");
        }
    };

    private Button button1, button2, button3, button4;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);
        button3 = findViewById(R.id.button3);
        button4 = findViewById(R.id.button4);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        button3.setOnClickListener(this);
        button4.setOnClickListener(this);
    }

    public void bindService() {
        Intent intent = new Intent("com.example.lu.calc");
        intent.setPackage("com.example.lu.okhttpdemo");
        bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);
    }

    public void unbindService() {
        if (serviceConn != null)
            unbindService(serviceConn);
    }

    public void addInvoked() throws RemoteException {
        if (iProcess != null) {
            int addRes = iProcess.add(12, 12);
            Toast.makeText(this, addRes + " ", Toast.LENGTH_SHORT).show();
        }
        else {
            Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
        }
    }

    public void minInvoked() throws RemoteException {
        if (iProcess != null) {
            int addRes = iProcess.min(58, 12);
            Toast.makeText(this, addRes + " ", Toast.LENGTH_SHORT).show();
        }
        else {
            Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                bindService();
                break;
            case R.id.button2:
                unbindService();
                break;
            case R.id.button3:
                try {
                    addInvoked();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.button4:
                try {
                    minInvoked();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}

相应的布局文件

<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:orientation="vertical" >

    <Button
        android:id="@+id/button1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="BindService" />

    <Button
        android:id="@+id/button2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"

        android:text="UnbindService" />

    <Button
        android:id="@+id/button3"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"

        android:text="12+12" />

    <Button
        android:id="@+id/button4"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"

        android:text="50-12" />

</LinearLayout>

运行,查看结果

绑定服务,查看log日志:

成功,客户端连接到了另一个进程的service

08-09 05:02:07.856 3988-3988/? D/client: onServiceConnected
08-09 05:02:07.837 4024-4024/com.example.lu.okhttpdemo:remote D/service: onCreate
08-09 05:02:07.838 4024-4024/com.example.lu.okhttpdemo:remote D/service: onBind

其它的输出可以自己尝试。

分析AIDL自动生成的文件

我们在build/generated/source/aidl里面打开生成的文件。我这里是排了下版,方便浏览

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: C:\\Users\\74650\\AndroidStudioProjects\\OkHttpDemo\\app\\src\\main\\aidl\\com\\example\\lu\\okhttpdemo\\IProcess.aidl
 */
package com.example.lu.okhttpdemo;
// Declare any non-default types here with import statements

public interface IProcess extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.example.lu.okhttpdemo.IProcess
    {
        private static final java.lang.String DESCRIPTOR = "com.example.lu.okhttpdemo.IProcess";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.example.lu.okhttpdemo.IProcess interface,
         * generating a proxy if needed.
         */
        public static com.example.lu.okhttpdemo.IProcess asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.example.lu.okhttpdemo.IProcess))) {
                return ((com.example.lu.okhttpdemo.IProcess)iin);
            }
            return new com.example.lu.okhttpdemo.IProcess.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_add:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_min:
                 {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.min(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                 }
            }
            return super.onTransact(code, data, reply, flags);
        }
        private static class Proxy implements com.example.lu.okhttpdemo.IProcess
        {
            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 add(int x, int y) 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);
                    _data.writeInt(x);
                    _data.writeInt(y);
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            @Override public int min(int x, int y) 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);
                    _data.writeInt(x);
                    _data.writeInt(y);
                    mRemote.transact(Stub.TRANSACTION_min, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    public int add(int x, int y) throws android.os.RemoteException;
    public int min(int x, int y) throws android.os.RemoteException;
}
DESCRIPTOR  Binder的唯一标识,一般为当前的Binder类名标识。

asInterface(android.os.IBinder obj)
该方法用于将服务度的Binder对象转换为客户端所需的AIDL接口类型的对象。如果客户端和服务端在同一进程中
那么返回的就是服务端的Stub对象本身,如果不同进程,那么返回的是系统封装后的Stub.proxy(代理)对象
由上面第一章图可以得知,proxy对象是由ServiceManager返回的。

asBinder()
返回当前Binder对象

onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)  *重点*
该方法原型为Binder类中的
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)方法
code参数代表客户端请求的目标方法,data中为目标方法所需的参数,就是客户端传递过来的参数,
reply为服务端返回给客户端的值,
flag为是否有返回值,0为有(双向),1为没有(单向)

Proxy这个类就是代理类

理解完这个AIDL生成的文件之后,我们接下来看看这两个进程是如何通信的。

Binder进程通信的机制

首先,我们的Activity(客户端)通过创建一个ServiceConnection来进行对Service(服务端)的连接

,如果连接成功,那么Service就会在onBind回调方法中返回一个IBinder对象,接着我们使用刚才

AIDL文件生成的类Stub的asInterface(android.os.IBinder obj) 方法,传进IBinder对象,得

到一个代理的对象,里面包含者Service(服务端)相关的调用方法,接着通过这个代理对象调用相关

方法来进行对应的操作。
Binder工作机制

接下来我们不适用AIDL文件来实现Binder。

手动实现Binder,不依赖AIDL

Service(服务端)代码

package com.example.lu.okhttpdemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;


public class MyService extends Service {

    private static final String TAG = "service";

    private static final String DESCRIPTOR = "MyService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");
        return mBinder;
    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        Log.d(TAG, "onDRebind");
        super.onRebind(intent);
    }

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

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate");
        super.onCreate();
    }

    private MyBinder mBinder = new MyBinder();

    private class MyBinder extends Binder{
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case 0x110:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();//读取客户端发过来的第一个参数
                    int _arg1;
                    _arg1 = data.readInt();//读取客户端发过来的第二个参数
                    int _result = _arg0 + _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);//结果写进reply中,通过Binder传递回给客户端
                    return true;
                }
                case 0x111:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0 = data.readInt();
                    int _arg1 = data.readInt();
                    int _result = _arg0 - _arg1;
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    }
}

Activity(客户端)代码

package com.example.lu.okhttpdemo;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;


public class Main2Activity extends AppCompatActivity implements View.OnClickListener {

    private IBinder iBinder;

    private ServiceConnection serviceConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d("client", "onServiceConnected");
            iBinder = service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("client", "onServiceDisConnected");
        }
    };

    private Button button1, button2, button3, button4;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);
        button3 = findViewById(R.id.button3);
        button4 = findViewById(R.id.button4);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        button3.setOnClickListener(this);
        button4.setOnClickListener(this);
    }

    public void bindService() {
        Intent intent = new Intent("com.example.lu.calc");
        intent.setPackage("com.example.lu.okhttpdemo");
        bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);
    }

    public void unbindService() {
        if (serviceConn != null)
            unbindService(serviceConn);
    }

    public void addInvoked() throws RemoteException {
        if (iBinder != null) {
            Parcel _data = Parcel.obtain();
            Parcel _reply = Parcel.obtain();
            int _result;
            try {
                //写入传递的参数数值
                _data.writeInterfaceToken("MyService");//通过该名字查找到相应的服务端
                _data.writeInt(12);
                _data.writeInt(12);
                //通过transact方法传递参数,通过Binder调用相关代理方法
                iBinder.transact(0x110, _data, _reply, 0);
                _reply.readException();
                //获取服务端返回的数值
                _result = _reply.readInt();
                Toast.makeText(this, _result + " ", Toast.LENGTH_SHORT).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }
        else {
            Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
        }
    }

    public void minInvoked() throws RemoteException {
        if (iBinder != null) {
            Parcel _data = Parcel.obtain();
            Parcel _reply = Parcel.obtain();
            int _result;
           try {
               _data.writeInterfaceToken("MyService");
               _data.writeInt(50);
               _data.writeInt(12);
               iBinder.transact(0x111, _data, _reply, 0);
               _reply.readException();
               _result = _reply.readInt();
               Toast.makeText(this, _result + " ", Toast.LENGTH_SHORT).show();
           } catch (RemoteException e) {
               e.printStackTrace();
           } finally {
               _reply.recycle();
               _data.recycle();
           }
        }
        else {
            Toast.makeText(this, "异常,重新绑定", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                bindService();
                break;
            case R.id.button2:
                unbindService();
                break;
            case R.id.button3:
                try {
                    addInvoked();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.button4:
                try {
                    minInvoked();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}

运行结果一致,Binder进程通信可以不依靠AIDL。其实AIDL文件只是为了方便系统帮我们生成代码

节省一定的工作量。

代码参考自鸿洋前辈
图文参考自《Android开发艺术探索》

Messenger实现进程通信

猜你喜欢

转载自blog.csdn.net/callmeMrLu/article/details/81542686