Android之IPC機制(二)

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實現進程通信

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章