Android的IPC機制(一)——AIDL的使用

yangxi_001發表於2017-12-19

綜述

  IPC(interprocess communication)是指程式間通訊,也就是在兩個程式間進行資料互動。不同的作業系統都有他們自己的一套IPC機制。例如在Linux作業系統中可以通過管道、訊號量、訊息佇列、記憶體共享、套接字等進行程式間通訊。那麼在Android系統中我們可以通過Binder來進行程式間的通訊。當然除了Binder我們還可以使用Socket來進行程式間的通訊。 
  既然需要程式通訊,那麼就必須有多個程式。當然,在兩個應用互動中必然出現多程式的情況。若是在一個應用中呢?我們可以通過給四大元件在AndroidMenifest中為他們指定android:process屬性來實現不同的元件在不同程式中執行。下面就來介紹一下Android中程式間通訊的實現方式。

AIDL簡介

  AIDL是 Android Interface Definition Language的縮寫。AIDL 是一種IDL 語言,用於生成可以在Android裝置上兩個程式之間進行 IPC的程式碼。如果在一個程式中(例如Activity)要呼叫另一個程式中(例如Service)物件的操作,就可以使用AIDL生成可序列化的引數。 
  AIDL是面向介面的,像COM或Corba一樣,但是更加輕量級。它是使用代理類在客戶端和實現端傳遞資料。

AIDL用法

  首先我們建立一個AIDL檔案,在AndroidStudio中當我們建立一個AIDL檔案時會自動為我們創件一個AILD資料夾,用於存放AIDL檔案。建立完之後重新rebuild會自動生成aidl實現類。 
  這裡寫圖片描述 
  在下面的例子當中,我們將Service單獨作為一個應用在系統中執行,在另一個用於訪問Service的client也單獨作為一個應用執行在系統中。這樣保證了兩個程式分別執行在兩個程式中。並且使用了butterknife進行控制元件繫結。

AIDL簡單用法

演示

  在Service中我們對客戶端傳來的兩個整數做了一次加法運算並返回到客戶端中。 
這裡寫圖片描述 
  

AIDL程式碼

// ICalculate.aidl
package com.ljd.aidl;

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

interface ICalculate {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    int add(int first, int second);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

服務端程式碼

package com.ljd.aidl.service;

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

import com.ljd.aidl.ICalculate;

public class CalculateService extends Service {
    public CalculateService() {
    }

    private Binder mBinder = new ICalculate.Stub(){

        @Override
        public int add(int first, int second) throws RemoteException {
            return first + second;
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

客戶端程式碼

package com.ljd.aidl.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.util.Log;
import android.view.View;
import android.widget.Toast;

import com.ljd.aidl.ICalculate;
import com.ljd.aidl.client.R;

import butterknife.ButterKnife;
import butterknife.OnClick;

public class Demo1Activity extends AppCompatActivity {

    private final String TAG = "DEMO1";
    //是否已經繫結service
    private boolean mIsBindService;
    private ICalculate mCalculate;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(TAG,"bind success");
            Toast.makeText(Demo1Activity.this,"bind service success",Toast.LENGTH_SHORT).show();
            mCalculate = ICalculate.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        //重新繫結Service防止系統將服務程式殺死而產生的呼叫錯誤。
            bindService();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo1);
        ButterKnife.bind(this);
        mIsBindService = false;
    }

    @Override
    protected void onDestroy() {
        unbindService();
        ButterKnife.unbind(this);
        super.onDestroy();
    }

    @OnClick({ R.id.bind_demo1_btn,R.id.unbind_demo1_btn,R.id.calculate_btn})
    public void onClickButton(View v) {
        switch (v.getId()){
            case R.id.bind_demo1_btn:
                bindService();
                break;
            case R.id.unbind_demo1_btn:
                Toast.makeText(this,"unbind service success",Toast.LENGTH_SHORT).show();
                unbindService();
                break;
            case R.id.calculate_btn:
                if (mIsBindService && mCalculate != null ){
                    try {
                        int result = mCalculate.add(2,4);
                        Log.d(TAG,String.valueOf(result));
                        Toast.makeText(this,String.valueOf(result),Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

    private void bindService(){
        Intent intent = new Intent();
        intent.setAction("com.ljd.aidl.action.CALCULATE_SERVICE");
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
        mIsBindService = true;
    }

    private void unbindService(){
        if(mIsBindService){
            mIsBindService = false;
            unbindService(mConnection);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

AIDL高階用法

  對於上面的例子,在AIDL介面中只是使用了一些Java的基本型別,對於AIDL檔案並不是所有的型別都是可用的,那麼在AIDL中究竟有哪些型別可以使用呢?

AIDL語法規則

預設情況下AIDL支援以下資料型別:

  • 所有Java的基本資料型別(例如: int, long,double, char, boolean等)
  • String和CharSequence
  • List:AIDL實際接收到的是ArrayList,並且List裡面所有元素都必須被AIDL支援
  • Map: AIDL實際接收到的是HashMap,並且Map裡面所有元素都必須被AIDL支援

如果不是上面所述型別,我們必須要顯示import進來,即使他們在同一個包中。當我們使用自定義的物件時必須實現Parcelable介面,Parcelable為物件序列化介面,效率比實現Serializable介面高。並且新建一個與該類同名的AIDL檔案,宣告他為Parcelable型別。

我們定義AIDL介面還需要注意以下幾點:

  • 方法可以有多個或沒有引數,可以有返回值也可以為void
  • 在引數中,除了基本型別以外,我們必須為引數標上方向in, out, 或者 inout
  • 在AIDL檔案中只支援方法,不支援靜態常量

演示

  在計算機商店中需要採購筆記本進行銷售,在服務端中我們新增兩臺筆記本,在客戶端中我們為商店加購一臺dell筆記本。 
這裡寫圖片描述 
  

實體類程式碼

  我們首先構建一個計算機實體類,包含筆記本的id,品牌,型號,並且實現Parcelable介面,在AndroidStudio中會為我們自動構造程式碼。 
  

package com.ljd.aidl.entity;

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

public class ComputerEntity implements Parcelable{

    public int computerId;     //id
    public String brand;       //品牌
    public String model;       //型號

    public ComputerEntity(int computerId, String brand, String model) {
        this.brand = brand;
        this.computerId = computerId;
        this.model = model;
    }

    protected ComputerEntity(Parcel in) {
        computerId = in.readInt();
        brand = in.readString();
        model = in.readString();
    }

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

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(computerId);
        dest.writeString(brand);
        dest.writeString(model);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

AIDL程式碼

  在AIDL中對實體類進行宣告,包名和檔名必須與實體類一致。在AndroidStudio中新建一個與實體類同名的AIDL檔案會報錯,需要先用一個其它名字,然後修改與實體類名一致即可。

package com.ljd.aidl.entity;

//包名必須和對用實體類的包名一致
// Declare any non-default types here with import statements
parcelable ComputerEntity;
  • 1
  • 2
  • 3
  • 4
  • 5

  新增兩個介面分別為新增一臺筆記本和獲取全部筆記本,在該檔案中使用到了ComputerEntity類,顯示的import進來。

package com.ljd.aidl;

import com.ljd.aidl.entity.ComputerEntity;
// Declare any non-default types here with import statements

interface IComputerManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     void addComputer(in ComputerEntity computer);
     List<ComputerEntity> getComputerList();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

服務端程式碼

package com.ljd.aidl.service;

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

import com.ljd.aidl.IComputerManager;
import com.ljd.aidl.entity.ComputerEntity;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class ComputerService extends Service {
    private CopyOnWriteArrayList<ComputerEntity> mComputerList = new CopyOnWriteArrayList<>();

    public ComputerService() {
    }

    private final IComputerManager.Stub mBinder = new IComputerManager.Stub() {
        @Override
        public void addComputer(ComputerEntity computer) throws RemoteException {
            mComputerList.add(computer);
        }

        @Override
        public List<ComputerEntity> getComputerList() throws RemoteException {
            return mComputerList;
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mComputerList.add(new ComputerEntity(0,"apple","macbookpro"));
        mComputerList.add(new ComputerEntity(1,"microsoft","surfacebook"));
        mComputerList.add(new ComputerEntity(2,"dell","XPS13"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

  注意:在該類中使用了CopyOnWriteArrayList,CopyOnWriteArrayList能夠自動進行執行緒同步。可是在AIDL中接收和返回的只能是ArrayList,其實AIDL支援的是抽象的List,在Binder中會按照List訪問資料並最終形成一個ArrayList,所以在AIDL中返回的還是一個ArrayList。

客戶端程式碼

package com.ljd.aidl.activity;

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.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.ljd.aidl.IComputerManager;
import com.ljd.aidl.client.R;
import com.ljd.aidl.entity.ComputerEntity;

import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class Demo2Activity extends AppCompatActivity{

    @Bind(R.id.show_linear)
    LinearLayout mShowLinear;

    private boolean mIsBindService;
    private IComputerManager mRemoteComputerManager;
    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if(mRemoteComputerManager != null){
                mRemoteComputerManager.asBinder().unlinkToDeath(mDeathRecipient,0);
                mRemoteComputerManager = null;
                bindService();
            }
        }
    };
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIsBindService = true;
            Toast.makeText(Demo2Activity.this,"bind service success",Toast.LENGTH_SHORT).show();
            mRemoteComputerManager = IComputerManager.Stub.asInterface(service);
            try {
                mRemoteComputerManager.asBinder().linkToDeath(mDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteComputerManager = null;
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo2);
        ButterKnife.bind(this);
        mIsBindService = false;
    }

    @Override
    protected void onDestroy() {
        unbindService();
        ButterKnife.unbind(this);
        super.onDestroy();
    }

    @OnClick({R.id.bind_demo2_btn,R.id.unbind_demo2_btn,R.id.test_demo2_btn,R.id.clear_demo2_btn})
    public void onClickButton(View v) {
        switch (v.getId()){
            case R.id.bind_demo2_btn:
                bindService();
                break;
            case R.id.unbind_demo2_btn:
                Toast.makeText(this,"unbind service success",Toast.LENGTH_SHORT).show();
                unbindService();
                break;
            case R.id.test_demo2_btn:
                if (!mIsBindService || mRemoteComputerManager == null){
                    Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show();
                    return;
                }
                try {
                    List<ComputerEntity> computerList = mRemoteComputerManager.getComputerList();
                    for (int i =0;i<computerList.size();i++){
                        String str = "computerId:" + String.valueOf(computerList.get(i).computerId) +
                                " brand:" + computerList.get(i).brand +
                                " model:" + computerList.get(i).model ;
                        TextView textView = new TextView(this);
                        textView.setText(str);
                        mShowLinear.addView(textView);
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.clear_demo2_btn:
                mShowLinear.removeAllViews();
                break;
        }
    }

    private void bindService(){
        Intent intent = new Intent();
        intent.setAction("com.ljd.aidl.action.COMPUTER_SERVICE");
        mIsBindService = bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    private void unbindService(){
        if(!mIsBindService){
            return;
        }
        mIsBindService = false;
        unbindService(mConnection);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125

  由於Binder是有可能會意外死亡的,也就是Service所在程式被系統殺死,這時候我們呼叫Service的方法就會失敗。在第一個例子中我們通過onServiceDisconnected方法中重新繫結服務。在這個例子中我們採用了另外一種方法,由於在Binder中提供了兩個配對的方法linkToDeath和unlinkToDeath,通過linkToDeath可以給Binder設定一個死亡代理,Binder死亡時回撥binderDied方法,在binderDied方法中我們重新繫結服務即可。

AIDL用法擴充

  當我們需要一種筆記本的時候,由於商店缺貨,這時候我們會給賣家說一聲,我所需要的這款筆記本到貨後通知我。也就成了所謂的觀察者模式。 
  在Android系統中為我們提供了一個RemoteCallbackList,RemoteCallbackList是系統專門用來刪除跨程式的listener介面,並且在RemoteCallbackList中自動實現了執行緒同步功能,下面看一下它的用法。

演示

  客戶端註冊服務以後,服務端每隔三秒會新增一臺筆記本,並通知給客戶端顯示。 
這裡寫圖片描述

AIDL程式碼

  到貨後的AIDL監聽介面 
  

package com.ljd.aidl;

import com.ljd.aidl.entity.ComputerEntity;
// Declare any non-default types here with import statements

interface IOnComputerArrivedListener {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void onComputerArrived(in ComputerEntity computer);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

  在IComputerManager介面中新增兩個方法。顯示importIOnComputerArrivedListener ,即使在同一個包下面。 
  

// IComputerManagerObserver.aidl
package com.ljd.aidl;

import com.ljd.aidl.entity.ComputerEntity;
import com.ljd.aidl.IOnComputerArrivedListener;
// Declare any non-default types here with import statements

interface IComputerManagerObserver {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
     void addComputer(in ComputerEntity computer);
     List<ComputerEntity> getComputerList();
     void registerUser(IOnComputerArrivedListener listener);
     void unRegisterUser(IOnComputerArrivedListener listener);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

服務端程式碼

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;

import com.ljd.aidl.IComputerManagerObserver;
import com.ljd.aidl.IOnComputerArrivedListener;
import com.ljd.aidl.entity.ComputerEntity;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;

public class ComputerObserverService extends Service{
    public ComputerObserverService() {
    }

    private CopyOnWriteArrayList<ComputerEntity> mComputerList = new CopyOnWriteArrayList<>();
    private RemoteCallbackList<IOnComputerArrivedListener> mComputerArrivedListenerList = new RemoteCallbackList<>();
    private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false);

    private Binder mBinder = new IComputerManagerObserver.Stub(){

        @Override
        public void addComputer(ComputerEntity computer) throws RemoteException {
            mComputerList.add(computer);
        }

        @Override
        public List<ComputerEntity> getComputerList() throws RemoteException {
            return mComputerList;
        }

        @Override
        public void registerUser(IOnComputerArrivedListener listener) throws RemoteException {
            mComputerArrivedListenerList.register(listener);
        }

        @Override
        public void unRegisterUser(IOnComputerArrivedListener listener) throws RemoteException {
            mComputerArrivedListenerList.unregister(listener);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mComputerList.add(new ComputerEntity(0,"apple","macbookpro"));
        mComputerList.add(new ComputerEntity(1,"microsoft","surfacebook"));
        mComputerList.add(new ComputerEntity(2,"dell","XPS13"));
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!mIsServiceDestroy.get()){
                    try {
                        Thread.currentThread().sleep(3000);
                        ComputerEntity computer = new ComputerEntity(mComputerList.size(),"******","******");
                        mComputerList.add(computer);
                        final int COUNT = mComputerArrivedListenerList.beginBroadcast();
                        //通知所有註冊過的使用者
                        for (int i=0;i<COUNT;i++){
                            IOnComputerArrivedListener listener = mComputerArrivedListenerList.getBroadcastItem(i);
                            if (listener != null){
                                listener.onComputerArrived(computer);
                            }
                        }
                        mComputerArrivedListenerList.finishBroadcast();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        mIsServiceDestroy.set(true);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90

  注意:RemoteCallbackList並不是一個List,所以我們不能像操作List一樣操作RemoteCallbackList。並且遍歷RemoteCallbackList時,beginBroadcast和finishBroadcast是配對使用的。

客戶端程式碼


import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.ljd.aidl.IComputerManagerObserver;
import com.ljd.aidl.IOnComputerArrivedListener;
import com.ljd.aidl.client.R;
import com.ljd.aidl.entity.ComputerEntity;

import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class Demo3Activity extends AppCompatActivity {

    @Bind(R.id.show_demo3_linear)
    LinearLayout mShowLinear;

    private boolean mIsBindService;
    private static final int MESSAGE_COMPUTER_ARRIVED = 1;

    private IComputerManagerObserver mRemoteComputerManager;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MESSAGE_COMPUTER_ARRIVED:
                    ComputerEntity computer = (ComputerEntity)msg.obj;
                    String str = "computerId:" + String.valueOf(computer.computerId) +
                            " brand:" + computer.brand +
                            " model:" + computer.model ;
                    TextView textView = new TextView(Demo3Activity.this);
                    textView.setText(str);
                    mShowLinear.addView(textView);
                    break;
                default:
                    super.handleMessage(msg);
                    break;
            }

        }
    };

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if(mRemoteComputerManager != null){
                mRemoteComputerManager.asBinder().unlinkToDeath(mDeathRecipient,0);
                mRemoteComputerManager = null;
                bindService();
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIsBindService = true;
            Toast.makeText(Demo3Activity.this,"bind service success",Toast.LENGTH_SHORT).show();
            mRemoteComputerManager = IComputerManagerObserver.Stub.asInterface(service);
            try {
                mRemoteComputerManager.asBinder().linkToDeath(mDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

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

    private IOnComputerArrivedListener mOnComputerArrivedListener = new IOnComputerArrivedListener.Stub(){

        @Override
        public void onComputerArrived(ComputerEntity computer) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_COMPUTER_ARRIVED,computer).sendToTarget();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_demo3);
        ButterKnife.bind(this);
        mIsBindService = false;
    }

    @Override
    protected void onDestroy() {
        unbindService();
        ButterKnife.unbind(this);
        super.onDestroy();
    }

    @OnClick({R.id.bind_demo3_btn,R.id.unbind_demo3_btn,R.id.test_demo3_btn,R.id.clear_demo3_btn})
    public void onClickButton(View v){
        switch (v.getId()){
            case R.id.bind_demo3_btn:
                bindService();
                break;
            case R.id.unbind_demo3_btn:
                Toast.makeText(this,"unbind service success",Toast.LENGTH_SHORT).show();
                unbindService();
                break;
            case R.id.test_demo3_btn:
                if (!mIsBindService || mRemoteComputerManager == null){
                    Toast.makeText(this,"not bind service",Toast.LENGTH_SHORT).show();
                    return;
                }

                try {
                    ComputerEntity computer = new ComputerEntity(3,"hp","envy13");
                    mRemoteComputerManager.addComputer(computer);
                    List<ComputerEntity> computerList = mRemoteComputerManager.getComputerList();
                    for (int i =0;i<computerList.size();i++){
                        String str = "computerId:" + String.valueOf(computerList.get(i).computerId) +
                                " brand:" + computerList.get(i).brand +
                                " model:" + computerList.get(i).model ;
                        TextView textView = new TextView(this);
                        textView.setText(str);
                        mShowLinear.addView(textView);
                    }
                    mRemoteComputerManager.registerUser(mOnComputerArrivedListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            case R.id.clear_demo3_btn:
                mShowLinear.removeAllViews();
                break;
        }
    }

    private void bindService(){
        Intent intent = new Intent();
        intent.setAction("com.ljd.aidl.action.COMPUTER_OBSERVER_SERVICE");
        mIsBindService = bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    private void unbindService(){
        if(!mIsBindService){
            return;
        }
        if (mRemoteComputerManager != null && mRemoteComputerManager.asBinder().isBinderAlive()){
            try {
                mRemoteComputerManager.unRegisterUser(mOnComputerArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        mIsBindService = false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170

原始碼下載

轉自:http://blog.csdn.net/ljd2038/article/details/50704523

相關文章