Android 進程間交互 (IPC) 開發(fā)實踐

讓app多進程運行

應用為了獲取更多的內(nèi)存空間,可以采用多進程的方式.
在AndroidManifest.xml中懈涛, 為四大組件指定"android:process"屬性實現(xiàn)多進程, 只有這一個方法泳猬, 也就是說我們無法給一個實例類或是一個線程指定其運行所在的進程.

<activity
        android:name="com.qihoo360.accounts.core.a.FirstActivity"
        android:configChanges="orientation|keyboardHidden|screenSize|fontScale"
        android:exported="false"
        android:process="com.qihoo360.accounts"
        >
</activity>

<activity
        android:name="com.qihoo360.accounts.SecondActivity"
        android:configChanges="orientation|keyboardHidden|screenSize|fontScale"
        android:exported="false"
        android:process=":remote"
        >
</activity>

以:開始的進程名是當前應用的私有進程批钠,其他應用的組件不可以和它跑在同一個進程中.
以全稱設置的進程名是全局進程, 其他應用通過shareUID的方式可以和它跑在同一個進程中,前提是兩個應用的簽名也必須相同.
系統(tǒng)為每個應用分配唯一的UID得封,兩個應用的組件跑在同一個進程除了要求UID相同埋心,簽名也必須相同, 這樣兩個應用就能共享data目錄忙上,共享內(nèi)存數(shù)據(jù)等.

使用多進程帶來的問題.
  1. 靜態(tài)成員和單例模式失效拷呆, 每個進程都有自己獨立的靜態(tài)成員和單例.
  2. 線程同步機制失效
  3. SharedPreference可靠性下降, 因為兩個進程同時對同一份SharedPreference的底層xml文件執(zhí)行寫操作時, 可能會造成數(shù)據(jù)丟失.
  4. Application多次創(chuàng)建茬斧,每個進程有自己獨立的虛擬機腰懂,有自己獨立的Application對象.
實現(xiàn)對象序列化的接口
Serializable接口

java提供的序列化接口,它是一個空接口项秉,使用時CityItem implements Serializable, 并提供一個private static final long serialVersionUID = 1L;值即可绣溜, 系統(tǒng)就會自動幫我們對這個類的對象實現(xiàn)序列化和反序列化了.

public interface Serializable {
}

靜態(tài)成員變量屬于類不屬于對象, 因此不參與序列化過程娄蔼, 另外怖喻, 用transient標記的變量不參與序列化.
通過ObjectOutputStreamd的writeObject()就可以把一個對象寫入一個文件.

LoggingInfo logInfo = new LoggingInfo("MIKE", "MECHANICS");  
System.out.println(logInfo.toString());  
FileOutputStream fos = new FileOutputStream("c:\\logging.obj");  
ObjectOutputStream oos = new ObjectOutputStream(fos);  
oos.writeObject(logInfo);  
oos.close();  

通過ObjectInputStream的readObject()就可以從一個文件中讀出一個對象.

FileInputStream fis = new FileInputStream("c:\\logging.obj");  
ObjectInputStream ois = new ObjectInputStream(fis);  
LoggingInfo info = (LoggingInfo) ois.readObject();  
ois.close();  
System.out.println(info); 
Parcelable接口

android提供的序列化方案, 一個類只有實現(xiàn)了Parcelable接口岁诉,它的對象才可以通過intent和Binder傳遞.
需要自己實現(xiàn)3個方法锚沸,writeToParcel(Parcel dest, int flags), describeContents(), 內(nèi)部接口Creator里的createFromParcel(Parcel source). 要序列化的數(shù)據(jù)就保存在參數(shù)Parcel對象中涕癣。

 public class MyParcelable implements Parcelable {
     private int mData;

     public int describeContents() {
         return 0;
     }

     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mData); //通過Parcel的一系列write方法實現(xiàn)序列化
     }

     public static final Parcelable.Creator<MyParcelable> CREATOR
             = new Parcelable.Creator<MyParcelable>() {
         public MyParcelable createFromParcel(Parcel in) {
             return new MyParcelable(in);//內(nèi)部實現(xiàn)上哗蜈, 通過Parcel的一系列write方法實現(xiàn)反序列化
         }

         public MyParcelable[] newArray(int size) {
             return new MyParcelable[size];
         }
     };
     
     private MyParcelable(Parcel in) {
         mData = in.readInt();
     }
 }

注意這句話:

Classes implementing the Parcelable interface must also have a non-null static field called "CREATOR" of a type that implements the Parcelable.Creator interface.

難點在于理解CREATOR這個成員變量, 為什么要提供一個名字必須是"CREATOR"的成員變量呢? 改為其他的名字成不成呢?
答案是不行, 因為在反序列化時坠韩, Parcel類要獲取Parcelable接口實現(xiàn)類中名稱為"CREATOR"的這個成員變量恬叹,也就是說這個名字是被寫死的。

//Parcel.java file
try {
    Class c = loader == null ?
        Class.forName(name) : Class.forName(name, true, loader);
    Field f = c.getField("CREATOR");
    creator = (Parcelable.Creator)f.get(null);
}
Serializable接口和Parcelable接口都能實現(xiàn)序列化同眯,選擇哪個?

Parcelable比Serializable效率高, 一般情況下首先Parcelable唯鸭。
Parcelable主要用在把對象序列化到內(nèi)存上须蜗,雖然通過Parcelable也可以把對象寫入存儲設備或是把對象序列化后通過網(wǎng)絡傳輸, 但稍顯復雜目溉,所以在這兩種情況下明肮, 建議通過Serializable實現(xiàn)對象的序列化.

實現(xiàn)跨進程通信的方式
1. 通過Intent使用Bundle對象攜帶數(shù)據(jù)

Intent持有一個成員變量, private Bundle mExtras; 而Intent實現(xiàn)了Parcelable接口, 因此Intent的對象可以被跨進程傳輸.

public class Intent implements Parcelable, Cloneable {
    ...
    private String mAction;
    private Uri mData;
    private ArraySet<String> mCategories;
    private Bundle mExtras;
    ...
}

需要通過intent傳遞數(shù)據(jù)時缭付,調(diào)用Intent的一系列putExtra(String name, T value)方法柿估,把各種類型的數(shù)據(jù)存入Bundle對象中.
以T為String為例.

public Intent putExtra(String name, String value) {
if (mExtras == null) {
    mExtras = new Bundle();
}
mExtras.putString(name, value);
return this;
}

而Bundle的內(nèi)部維護了一個ArrayMap mMap, 以鍵值對的方式存儲著所有的數(shù)據(jù).
Bundle.java的定義如下:

/**
 * A mapping from String values to various Parcelable types.
 *
 */
public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
    public static final Bundle EMPTY;
    static final Parcel EMPTY_PARCEL;

    static {
        EMPTY = new Bundle();
        EMPTY.mMap = ArrayMap.EMPTY;
        EMPTY_PARCEL = BaseBundle.EMPTY_PARCEL;
    }
public class BaseBundle {
    ...
    ArrayMap<String, Object> mMap = null;
    ...
    public void putString(String key, String value) {
        unparcel();
        mMap.put(key, value);
    }
    ...
}

被啟動的組件接收到intent后, 通過intent把數(shù)據(jù)取出來陷猫, 有兩種方式.
一是通過intent.getExtras()把整個Bundle對象取出來秫舌, 然后再通過Bundle的一系列getXXX(String key)方法從map中把數(shù)據(jù)取出來.

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    bundle = getIntent().getExtras();
    weiboType = bundle.getInt(ShareBlogActivity.EXTRA_WEIBO_TYPE);
    weiboPicURI = bundle.getString(ShareBlogActivity.EXTRA_PIC_URI);
    weiboContent = bundle.getString(ShareBlogActivity.EXTRA_WEIBO_CONTENT);
}


二是直接通過一系列intent.getXXXExtras(String key)方法, 直接從Bundle的map中取特定類型的數(shù)據(jù).

final String title = intent.getStringExtra("title");
final String filename = intent.getStringExtra("filename");
final long id = intent.getLongExtra("id");


//Intent.java file
    public String getStringExtra(String name) {
        return mExtras == null ? null : mExtras.getString(name);
    }

//BaseBundle.java file
public String getString(String key) {
    unparcel();
    final Object o = mMap.get(key);
    try {
        return (String) o;
    } catch (ClassCastException e) {
        typeWarning(key, o, "String", e);
        return null;
    }
}


總結: Intent有一個成員變量Bundle mExtras存儲自己的數(shù)據(jù)绣檬, 而Bundle實際上就是一個ArrayMap, 以鍵值對的方式保存著各種類型的數(shù)據(jù).
why ArrayMap? not use HashMap, 看到ArrayMap的注釋有這么一段.
/**

  • ArrayMap is a generic key->value mapping data structure that is
  • designed to be more memory efficient than a traditional java.util.HashMap.
    */
2. 使用文件共享

多個進程對同一份文件進行讀寫, 典型例子是SharedPreferences, 它通過鍵值對的方式在xml文件中存儲數(shù)據(jù), 文件位于/data/data/package_name/shared_prefs目錄下. 如果應用是多進程模式的足陨, 不同進程就會讀寫同一份xml文件,也就實現(xiàn)了跨進程通信. 但由于SharedPreferences有緩存策略娇未, 即在內(nèi)存中有一份SharedPreferences墨缘。因此多進程下容易發(fā)生數(shù)據(jù)丟失, 因此不建議采用SharedPreferences實現(xiàn)跨進程通信.

3. Messager

兩個進程間通過互相發(fā)送message通信,服務端 mMessenger = new Messenger(mHandler)镊讼, 客戶端使用bindlerService請求連接遠程服務端宽涌, 服務端返回Messager的binder對象, 客戶端onServiceConnected()中用接收到的IBinder對象創(chuàng)建一個客戶端的Messager對象蝶棋, 然后通過它發(fā)送消息給服務端即可實現(xiàn)跨進程通信.
public void onServiceConnected(ComponentName name, IBinder service) {
rMessenger = new Messenger(service);     
......
}
http://blog.csdn.net/cs_lht/article/details/6698468
實際項目中卸亮, 還沒有使用過這種方式, 初步了解一下即可嚼松,它只能傳遞消息嫡良, 無法完成跨進程的方法調(diào)用, 它的底層也是通過binder實現(xiàn)的。

4. 使用AIDL實現(xiàn)服務端和客戶端方法的雙向調(diào)用

*.aidl接口中只支持方法献酗, 不支持聲明靜態(tài)常量寝受, 這一點區(qū)別于傳統(tǒng)接口.
*.aidl接口中聲明的方法在服務端的binder線程池中執(zhí)行. 當多個客戶端同時連接一個服務端時, 不同客戶端調(diào)用的aidl方法就在不同的線程中執(zhí)行, 這些aidl方法中訪問的數(shù)據(jù)就要考慮線程同步的問題, 比如當aidl方法需要訪問ArrayList時罕偎, 最好是采用CopyOnWriteArrayList很澄,因為它對內(nèi)部元素的訪問實現(xiàn)了線程同步, 這樣我們的aidl方法就不用考慮線程同步的問題了, 類似的情形還有ConcurrentHashMap. 另外颜及, 這些因為aidl方法本身就是運行在binder線程池甩苛, 所以也沒有必要開啟新的線程去執(zhí)行耗時操作.
方法中的參數(shù)要標上方向, in, out 或是inout.
服務端和客戶端擁有相同的aidl文件, 并且aidl文件所在的包目錄也必須一致, 因為客戶端需要反序列化服務端中和aidl接口相關的所有的類. 一般的做法是服務端代碼中編寫所有的aidl文件俏站,并把這些文件放在同一個目錄讯蒲, 然后原封不動的拷貝一份到客戶端的代碼中.
這一節(jié)中有一些需要注意的點, 在下面的代碼注釋中進行總結.
//code: https://github.com/singwhatiwanna/android-art-res/tree/master/Chapter_2/src/com/ryg/chapter_2/aidl

服務端代碼:
第一步, 編寫aidl文件.
//Book.aidl file
package com.ryg.chapter_2.aidl;

parcelable Book;

//IBookManager.aidl file, 編譯后在gen目錄下生成IBookManager.java
package com.ryg.chapter_2.aidl;

import com.ryg.chapter_2.aidl.Book;
import com.ryg.chapter_2.aidl.IOnNewBookArrivedListener;

interface IBookManager {
     List<Book> getBookList();
     void addBook(in Book book);
     void registerListener(IOnNewBookArrivedListener listener);
     void unregisterListener(IOnNewBookArrivedListener listener);
}
//生成的IBookManager.java
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements IBookManager
{
    ...
    ...
}
//最重要的是生成了這個IBookManager.Stub這個內(nèi)部類, 它繼承Binder. 這個Stub就是一個Binder.

生成類的結構
public interface IBookManager extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements IBookManager
    {
        ...
        public static IBookManager asInterface(android.os.IBinder obj)
        {
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            return ((IBookManager)iin);
        }
        ...
    }
    public java.util.List<com.qihoo360.mobilesafe.service.Book> getBookList() throws android.os.RemoteException;
    public void addBook(com.qihoo360.mobilesafe.service.Book book) throws android.os.RemoteException;
    public void registerListener(com.qihoo360.mobilesafe.service.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
    public void unregisterListener(com.qihoo360.mobilesafe.service.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
}
// IOnNewBookArrivedListener.aidl file
package com.ryg.chapter_2.aidl;

import com.ryg.chapter_2.aidl.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book newBook);
}
// Book.java
package com.ryg.chapter_2.aidl;

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

public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book() {

    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(bookId);
        out.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public String toString() {
        return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
    }

}
第二步, 實現(xiàn)服務端,繼承Service
package com.ryg.chapter_2.aidl;

//服務端繼承Service
public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

    //不用普通ArrayList的原因是肄扎,服務端可能被多個客戶端同時綁定, aild方法就被多個binder線程同時執(zhí)行, 因此要保證線程同步墨林,而CopyOnWriteArrayList已經(jīng)為我們實現(xiàn)了操作list時的線程同步, 這樣調(diào)用aidl方法時就不要考慮線程同步的問題了.
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    // private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
    // new CopyOnWriteArrayList<IOnNewBookArrivedListener>();

    //保存所有客戶端的對象犯祠,當圖書列表發(fā)生變化時旭等,可以遍歷這個list,調(diào)用客戶端的方法.
    //RemoteCallbackList是系統(tǒng)提供的專門用于刪除跨進程listener的接口.
    //用RemoteCallbackList衡载,而不用ArrayList的原因是, 客戶端的對象注冊進來后搔耕, 服務端會通過它反序列化出一個新的對象保存一起,所以說已經(jīng)不是同一個對象了. 在客戶端調(diào)用解除注冊方法時痰娱, 在list中根本就找不到它的對象弃榨, 也就無法從list中刪除客戶端的對象. 而RemoteCallbackList的內(nèi)部保存的是客戶端對象底層的binder對象, 這個binder對象在客戶端對象和反序列化的新對象中是同一個對象,  RemoteCallbackList的實現(xiàn)原理就是利用的這個特性.
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();

    private Binder mBinder = new IBookManager.Stub() {//創(chuàng)建一個生成類IBookManager.Stub內(nèi)部類的對象, 并在其中對aidl中的方法進行代碼實現(xiàn).

        @Override
        public List<Book> getBookList() throws RemoteException {
            SystemClock.sleep(5000); //模擬一個耗時操作
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

    //雙重安全性檢查.
    Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
        //為保護Service不被任意的其他應用綁定, 可以檢查客戶端是否具有特定的permission.
            //對客戶端的permission進行驗證, 驗證不通過就返回null.
        //需要在服務端的AndroidManifest.xml中, 聲明<permission android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"/>
        //檢查在客戶端的AndroidManifest.xml中,是否使用了<uses-permission android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE">標簽
            int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
            Log.d(TAG, "check=" + check);
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

        //通過getCallingUid()得到客戶端的uid梨睁, 再通過PackageManager根據(jù)uid查到package name進行檢查.
            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(
                    getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            Log.d(TAG, "onTransact: " + packageName);
            if (!packageName.startsWith("com.ryg")) {
                return false;
            }

            return super.onTransact(code, data, reply, flags);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListenerList.register(listener);

            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "registerListener, current size:" + N);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            boolean success = mListenerList.unregister(listener);

            if (success) {
                Log.d(TAG, "unregister success.");
            } else {
                Log.d(TAG, "not found, can not unregister.");
            }
            final int N = mListenerList.beginBroadcast();
            mListenerList.finishBroadcast();
            Log.d(TAG, "unregisterListener, current size:" + N);
        };

    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
    //為保護Service不被任意的其他應用綁定, 可以檢查客戶端是否具有特定的permission.
        //對客戶端的permission進行驗證, 驗證不通過就返回null.
    //需要在服務端的AndroidManifest.xml中, 聲明<permission android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"/>
    //檢查在客戶端的AndroidManifest.xml中惭墓,是否使用了<uses-permission android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE">標簽
    //也可在onTransact()中檢查
        int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
        Log.d(TAG, "onbind check=" + check);
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;//在onBind()方法內(nèi)把生成類IBookManager.Stub內(nèi)部類的對象返回給客戶端,ServiceConnection類對象的onServiceConnected(ComponentName                //className, IBinder service)而姐, 把mBinder賦值給IBinder service參數(shù).
    }

    @Override
    public void onDestroy() {
        mIsServiceDestoryed.set(true);
        super.onDestroy();
    }

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
    //RemoteCallbackList類的beginBroadcast()和finishBroadcast()的配對使用, 完成對List的遍歷. 用法比較特殊腊凶, 不同于對普通List的遍歷.
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
            if (l != null) {
                try {
                    l.onNewBookArrived(book);//onNewBookArrived()方法在客戶端的binder線程池中執(zhí)行, 當前線程被掛起, 因此如果是耗時操作的話, 注意不要在UI線程執(zhí)行客戶端的aidl方法().
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        mListenerList.finishBroadcast();
    }

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            // do background processing here.....
            while (!mIsServiceDestoryed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
客戶端代碼
//BookManagerActivity.java
package com.ryg.chapter_2.aidl;

public class BookManagerActivity extends Activity {

    private static final String TAG = "BookManagerActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private IBookManager mRemoteBookManager;

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_NEW_BOOK_ARRIVED:
                Log.d(TAG, "receive new book :" + msg.obj);
                break;
            default:
                super.handleMessage(msg);
            }
        }
    };

    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() { //服務端進程停止時被binder線程池回調(diào).
            Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName());
            if (mRemoteBookManager == null)
                return;
            mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mRemoteBookManager = null;
            // TODO:這里重新綁定遠程Service
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
        //綁定成功后, 把參數(shù)IBinder service對象轉(zhuǎn)化為生成類IBookManager.java的對象供客戶端在后面使用
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            mRemoteBookManager = bookManager;
            try {
        //為保證程序的健壯性, 使用Binder類的linkToDeath()方法對服務端進程意外停止時進行處理.
        //服務端進程停止時, IBinder.DeathRecipient mDeathRecipient對象的binderDied()方法會被回調(diào).
        //也可選擇在onServiceDisconnected()時處理服務端進程意外停止的情況, 區(qū)別是binderDied()方法在線程池中執(zhí)行钧萍,而onServiceDisconnected()在UI線程執(zhí)行, 選擇一種處理方式即可.
                mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);

        //調(diào)用服務端的方法, 但要注意onServiceConnected()和onServiceDisconnected()都是在UI線程執(zhí)行褐缠, 當調(diào)用遠程服務端方法時, 服務端的方法在binder線程池中執(zhí)行, 客戶端會被掛起风瘦,因此這里要確保遠程服務的方法不是耗時方法, 否則客戶端會發(fā)生ANR. 不確定是否耗時的情況下队魏, 要開新線程執(zhí)行,不要直接在onServiceConnected()中調(diào)用遠程服務端的方法.
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:"
                        + list.getClass().getCanonicalName());
                Log.i(TAG, "query book list:" + list.toString());
                Book newBook = new Book(3, "Android進階");
                bookManager.addBook(newBook);
                Log.i(TAG, "add book:" + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.i(TAG, "query book list:" + newList.toString());
        // 把自己的對象傳給服務端, 服務端就可以通過這個對象調(diào)用客戶端的方法万搔, 這樣就實現(xiàn)了雙向調(diào)用.
                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mRemoteBookManager = null;
            Log.d(TAG, "onServiceDisconnected. tname:" + Thread.currentThread().getName());
        }
    };

    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {

        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
        //這個方法在客戶端的binder線程池中執(zhí)行, 因此如果方法內(nèi)要操作UI的話胡桨,就要通過Handler對象發(fā)送一個消息, 把執(zhí)行邏輯切換到UI線程.
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook)
                    .sendToTarget();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);//綁定Service, 成功后ServiceConnection mConnection的onServiceConnected()方法被回調(diào).
    }

    public void onButton1Click(View view) {
        Toast.makeText(this, "click button1", Toast.LENGTH_SHORT).show();
    //開啟一個新線程執(zhí)行遠程服務端的方法是良好的編程方法, 因為調(diào)用方法返回前當前線程會被掛起,如果在UI線程中執(zhí)行就可能會發(fā)生ANR.
        new Thread(new Runnable() {

            @Override
            public void run() {
                if (mRemoteBookManager != null) {
                    try {
                        List<Book> newList = mRemoteBookManager.getBookList();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null
                && mRemoteBookManager.asBinder().isBinderAlive()) {
            try {
                Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener);
                mRemoteBookManager
                        .unregisterListener(mOnNewBookArrivedListener);//把自己的對象從服務端的通知list中移除.
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);//在Activity銷毀時, 應該解除對service的綁定.
        super.onDestroy();
    }

}


5. 通過ContentProvider跨進程交換數(shù)據(jù)

ContentProvider的底層實現(xiàn)也是Binder. 系統(tǒng)本身就內(nèi)置了一些ContentProvider, 比如通信錄, 日歷表.
ContentProvider: 數(shù)據(jù)提供者
ContentResolver: 數(shù)據(jù)使用者
創(chuàng)建一個自定義的ContentProvider很簡單瞬雹, 繼承自ContentProvider, 并實現(xiàn)6個抽象方法.
onCreate(), query(), update(), insert(),delete()以及getType().
除了onCreate()運行在主線程昧谊,其他5個均運行在binder線程池.
ContentProvider以表格的形式來存儲數(shù)據(jù),但對底層的數(shù)據(jù)存儲方式?jīng)]有要求酗捌,可以是數(shù)據(jù)庫, 也可以是普通的文件呢诬,甚至可以是用內(nèi)存中的一個對象來存儲數(shù)據(jù).
下面通過一個完整的例子,并在代碼中插入注釋的方式方便我們的理解.

幾個知識點:
  1. UriMatcher的使用胖缤,將uri和int整形進行關聯(lián)尚镰,對int值 switch-case獲知客戶要訪問的是數(shù)據(jù)庫中的哪個表.
  2. 數(shù)據(jù)源變化時, 如何通知客戶.mContext.getContentResolver().notifyChange(uri, null)
    客戶用mContext.getContentResolver().registerContentObserver("content://com.ryg.chapter_2.book.provider/book", false, mBookObserver);注冊一個ContentObserver對象.
  3. 客戶調(diào)用query(uri)后得到的cursor對象對結果集進行遍歷的操作.
//BookProvider.java
package com.ryg.chapter_2.provider;

public class BookProvider extends ContentProvider {

    private static final String TAG = "BookProvider";

    public static final String AUTHORITY = "com.ryg.chapter_2.book.provider"; //對應在AndroidManifest.xml
        //        <provider
        //                android:name="com.ryg.chapter_2.provider.BookProvider"
        //                android:authorities="com.ryg.chapter_2.book.provider"
        //                >
        //    </provider>
    public static final Uri BOOK_CONTENT_URI = Uri.parse("content://"
            + AUTHORITY + "/book");   //"book"表對應的完整uri
    public static final Uri USER_CONTENT_URI = Uri.parse("content://"
            + AUTHORITY + "/user");   //"user"表對應的完整uri

    public static final int BOOK_URI_CODE = 0;
    public static final int USER_URI_CODE = 1;

    //使用UriMatcher的目的是把一個uri和一個int數(shù)對應起來, 這樣再調(diào)用sUriMatcher.match(uri)就返回一個int數(shù),再根據(jù)這個int數(shù)switch-case出客戶要訪問的是數(shù)據(jù)庫中的哪個表了.
    private static final UriMatcher sUriMatcher = new UriMatcher(
            UriMatcher.NO_MATCH);

    static {
        sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
        sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
    }

    private Context mContext;
    private SQLiteDatabase mDb; //底層使用數(shù)據(jù)庫存儲數(shù)據(jù)哪廓,也是最常用的方式.

    @Override
    public boolean onCreate() {
        Log.d(TAG, "onCreate, current thread:"
                + Thread.currentThread().getName());
        mContext = getContext();
        initProviderData();
        return true;
    }

    private void initProviderData() {
        mDb = new DbOpenHelper(mContext).getWritableDatabase();//創(chuàng)建一個名為"book_provider.db"的數(shù)據(jù)庫
        mDb.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME);//刪除"book"表中的數(shù)據(jù)
        mDb.execSQL("delete from " + DbOpenHelper.USER_TALBE_NAME);//刪除"user"表中的數(shù)據(jù)
    //為兩個表插入數(shù)據(jù)
        mDb.execSQL("insert into book values(3,'Android');");
        mDb.execSQL("insert into book values(4,'Ios');");
        mDb.execSQL("insert into book values(5,'Html5');");
        mDb.execSQL("insert into user values(1,'jake',1);");
        mDb.execSQL("insert into user values(2,'jasmine',0);");
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        Log.d(TAG, "query, current thread:" + Thread.currentThread().getName());//執(zhí)行在binder線程池
        String table = getTableName(uri); //首先根據(jù)uri判斷出客戶到底要訪問的是數(shù)據(jù)庫中的哪個table
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    //返回一個cursor對象狗唉,也就是一個迭代器,客戶使用這個迭代器對查詢到的結果集進行遍歷. (參看設計模式與源碼書中對迭代器的講解.)
        return mDb.query(table, projection, selection, selectionArgs, null, null, sortOrder, null); //到實際的"book_provider.db"數(shù)據(jù)庫中進行查詢
    }

    @Override
    public String getType(Uri uri) {
        Log.d(TAG, "getType");
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.d(TAG, "insert");
        String table = getTableName(uri);//首先根據(jù)uri判斷出客戶到底要訪問的是數(shù)據(jù)庫中的哪個table
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        mDb.insert(table, null, values);
        mContext.getContentResolver().notifyChange(uri, null);// 通知系統(tǒng), 這個uri對應的數(shù)據(jù)源發(fā)生了變化, 系統(tǒng)會根據(jù)這個uri查看所有調(diào)用了        registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)的ContentResolver, 回調(diào)observer對象的onChange()方法.
        return uri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.d(TAG, "delete");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        int count = mDb.delete(table, selection, selectionArgs);
        if (count > 0) {
            getContext().getContentResolver().notifyChange(uri, null); //通知系統(tǒng)uri對應的數(shù)據(jù)源發(fā)生了變化
        }
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        Log.d(TAG, "update");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        int row = mDb.update(table, values, selection, selectionArgs);
        if (row > 0) {
            getContext().getContentResolver().notifyChange(uri, null);//通知系統(tǒng)uri對應的數(shù)據(jù)源發(fā)生了變化
        }
        return row;
    }

    private String getTableName(Uri uri) {
        String tableName = null;
        switch (sUriMatcher.match(uri)) {
        case BOOK_URI_CODE:
            tableName = DbOpenHelper.BOOK_TABLE_NAME;
            break;
        case USER_URI_CODE:
            tableName = DbOpenHelper.USER_TALBE_NAME;
            break;
            default:break;
        }

        return tableName;
    }
}
用SQLite建立一個數(shù)據(jù)庫
//DbOpenHelper.java
package com.ryg.chapter_2.provider;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

//繼承自SQLiteOpenHelper涡真,目的是定制數(shù)據(jù)庫的名字, 以及這里在創(chuàng)建對象時敞曹,默認建立了兩個表"book"和"user"
public class DbOpenHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "book_provider.db";
    public static final String BOOK_TABLE_NAME = "book";
    public static final String USER_TALBE_NAME = "user";

    private static final int DB_VERSION = 3;//升級時, 要記得更新DB_VERSION的值

    private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS "
            + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";

    private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS "
            + USER_TALBE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT,"
            + "sex INT)";

    public DbOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK_TABLE);
        db.execSQL(CREATE_USER_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO ignored
    }

}
客戶端
//ProviderActivity.java
package com.ryg.chapter_2.provider;


public class ProviderActivity extends Activity {
    private static final String TAG = "ProviderActivity";
    private ContentObserver mBookObserver;
    private ContentObserver mUserObserver;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_provider);
        // Uri uri = Uri.parse("content://com.ryg.chapter_2.book.provider");
        // getContentResolver().query(uri, null, null, null, null);
        // getContentResolver().query(uri, null, null, null, null);
        // getContentResolver().query(uri, null, null, null, null);

        Uri bookUri = Uri.parse("content://com.ryg.chapter_2.book.provider/book"); //這個uri值對應AndroidManifest.xml的<ContentProvider>中android:authorities="com.ryg.chapter_2.book.provider" 指定的值, "book"是表的名字,一個ContentProvider可以分多個table存不同的數(shù)據(jù).

    //使用ContentValues的對象综膀,插入數(shù)據(jù)
        ContentValues values = new ContentValues();
        values.put("_id", 6);
        values.put("name", "程序設計的藝術");
        getContentResolver().insert(bookUri, values);

    //查詢到的是一個結果集,用返回的cursor對象局齿,也就是一個迭代器剧劝,對結果集進行遍歷.
        Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
        while (bookCursor.moveToNext()) {
            Book book = new Book();
            book.bookId = bookCursor.getInt(0);
            book.bookName = bookCursor.getString(1);
            Log.d(TAG, "query book:" + book.toString());
        }
        bookCursor.close();

        Uri userUri = Uri.parse("content://com.ryg.chapter_2.book.provider/user");
        Cursor userCursor = getContentResolver().query(userUri, new String[]{"_id", "name", "sex"}, null, null, null);
        while (userCursor.moveToNext()) {
            User user = new User();
            user.userId = userCursor.getInt(0);
            user.userName = userCursor.getString(1);
            user.isMale = userCursor.getInt(2) == 1;
            Log.d(TAG, "query user:" + user.toString());
        }
        userCursor.close();

    //自己擴展, 書中忘寫了.
    //向系統(tǒng)注冊某個uri對應的observer, observer和某個特定的uri對應起來
    //當數(shù)據(jù)源發(fā)生變化并調(diào)用了notifyChange(uri, null)時抓歼,系統(tǒng)回調(diào)uri對應的所有的observer的onChange()方法.
    mBookObserver = new ContentObserver {
        @Override
        public void onChange(boolean selfChange) {
        Log.d(TAG, "book table 中數(shù)據(jù)源發(fā)生了變化");
        }
    }
    this.getContentResolver().registerContentObserver("content://com.ryg.chapter_2.book.provider/book", false, mBookObserver);

    mUserObserver = new ContentObserver {
        @Override
        public void onChange(boolean selfChange) {
        Log.d(TAG, "user table 中的數(shù)據(jù)源發(fā)生了變化");
        }
    }
    this.getContentResolver().registerContentObserver("content://com.ryg.chapter_2.book.provider/user", false, mUserObserver);

    }
}

6. 使用socket

分為TCP和UDP
TCP連接的建立需要經(jīng)過"三次握手"才能完成, 為了提供穩(wěn)定的數(shù)據(jù)傳輸功能, 實現(xiàn)了超時重傳機制讥此,因此具有很高的穩(wěn)定性.
UDP具有更好的效率,但不保證數(shù)據(jù)一定能夠正確傳輸谣妻,尤其是在網(wǎng)絡阻塞的情況下.
不能在UI線程訪問網(wǎng)絡萄喳,否則在android 4.0以上的手機會拋出android.os.NetworkOnMainThreadException異常.
socket使用上的知識點就不總結了。

Binder連接池

注意這里說的不是Binder線程池.
考慮一種情形蹋半,有10個業(yè)務模塊都需要和服務端進行通信他巨,每個業(yè)務模塊都擁有一個自己的aidl文件和服務端共享. 如果在服務端為每個aidl都創(chuàng)建一個Service,創(chuàng)建一個Binder對象,去實現(xiàn)aidl文件生成類中的Stub內(nèi)部類中的方法(真正去實現(xiàn)那些aidl方法), 那么這個服務端就有10個service在同時運行,這個應用就會很重, 是用戶無法接受的, 那么如何去解決這個問題呢?
這個時候就需要引入binder連接池的概念.
原理就是在服務端, 為每個業(yè)務模塊建立一個類染突,繼承自aidl文件對應java類中的Stub內(nèi)部類捻爷,在這個新建類中實現(xiàn)Stub類中所描述的aidl方法. 這樣有幾個業(yè)務模塊就新建幾個這樣的類。
以例子中有兩個業(yè)務模塊為例份企, 在服務端創(chuàng)建3個Binder對象, 分別實現(xiàn)3個aidl文件對應的Stub內(nèi)部類中的aidl方法. 2個業(yè)務模塊的binder對象也榄, 和一個IBinderPool對應的Binder對象. 服務端綁定成功后, 就把這個IBinderPool的對象返還給客戶端司志√鹱希客戶端用這個binder對象,再用IBinderPool.aidl中聲明的queryBinder(int binderCode)獲取到實際要使用的binder對象骂远,就可以用這個業(yè)務模塊的binder對象進行實際的業(yè)務方法的調(diào)用了.

通過完整的代碼來說明如何實現(xiàn)一個binder連接池.
服務端代碼

兩個業(yè)務模塊, 分別對應各自的aidl文件.

// ISecurityCenter.aidl file, 生成ISecurityCenter.java, 最重要的是內(nèi)部類ISecurityCenter.Stub.
package com.ryg.chapter_2.binderpool;

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}
//ICompute.aidl file, 生成ICompute.java, 最重要的是內(nèi)部類ICompute.Stub.
package com.ryg.chapter_2.binderpool;

interface ICompute {
    int add(int a, int b);
}

對ISecurityCenter.aidl中的aidl方法進行實現(xiàn), 內(nèi)部類 Stub extends android.os.Binder囚霸,因此SecurityCenterImpl的對象就是一個Binder對象.

// SecurityCenterImpl.java file
package com.ryg.chapter_2.binderpool;

import android.os.RemoteException;

public class SecurityCenterImpl extends ISecurityCenter.Stub {

    private static final char SECRET_CODE = '^';

    @Override
    public String encrypt(String content) throws RemoteException {
        char[] chars = content.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return encrypt(password);
    }

}

對ICompute.aidl中的aidl方法進行實現(xiàn),ComputeImpl的對象就是一個Binder對象.

ComputeImpl.java file
package com.ryg.chapter_2.binderpool;

import android.os.RemoteException;

public class ComputeImpl extends ICompute.Stub {

    @Override
    public int add(int a, int b) throws RemoteException {
        return a + b;
    }

}

引入一個IBinderPool.aidl吧史,給客戶提供一個queryBinder(int code)方法, 客戶端調(diào)用這個方法, 可以得到實現(xiàn)了某個具體業(yè)務aidl方法的Binder對象.

//IBinderPool.aidl
package com.ryg.chapter_2.binderpool;

interface IBinderPool {

    /**
     * @param binderCode, the unique token of specific Binder<br/>
     * @return specific Binder who's token is binderCode.
     */
    IBinder queryBinder(int binderCode);
}

新建一個BinderPoolServer邮辽,它的內(nèi)部類BinderPoolImpl繼承IBinderPool.Stub,并實現(xiàn)queryBinder(int binderCode)方法.

package com.ryg.chapter_2.binderpool;

import java.util.concurrent.CountDownLatch;

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.util.Log;

public class BinderPoolServer {
    private static final String TAG = "BinderPoolServer";
    public static final int BINDER_NONE = -1;
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;


    private BinderPoolServer(Context context) {
    }

    public static class BinderPoolImpl extends IBinderPool.Stub {

        public BinderPoolImpl() {
            super();
        }

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode) {
            case BINDER_SECURITY_CENTER: {
                binder = new SecurityCenterImpl();
                break;
            }
            case BINDER_COMPUTE: {
                binder = new ComputeImpl();
                break;
            }
            default:
                break;
            }

            return binder;
        }
    }

}


提供一個BinderPoolService贸营, 繼承自Service, 綁定成功后給客戶端返回Binder mBinderPool = new BinderPoolServer.BinderPoolImpl(); 這個Binder對象.

package com.ryg.chapter_2.binderpool;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class BinderPoolService extends Service {

    private static final String TAG = "BinderPoolService";

    private Binder mBinderPool = new BinderPoolServer.BinderPoolImpl();

    @Override
    public void onCreate() {
        super.onCreate();
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

}


看看客戶端怎么用

新建一個BinderPoolClient, 用于綁定遠程服務吨述,在onServiceConnected()時, 得到IBinderPool的實際Binder對象.

package com.ryg.chapter_2.binderpool;

import java.util.concurrent.CountDownLatch;

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.util.Log;

public class BinderPoolClient {
    private static final String TAG = "BinderPoolClient";
    public static final int BINDER_NONE = -1;
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;

    private Context mContext;
    private IBinderPool mBinderPoolClient;
    private static volatile BinderPool sInstance;
    private CountDownLatch mConnectBinderPoolCountDownLatch;

    private BinderPoolClient(Context context) {
        mContext = context.getApplicationContext();
        connectBinderPoolService();//在BinderPoolClient對象構造時钞脂, 與遠程服務端BinderPoolService.class進行綁定.
    }

    //單例提供一個BinderPoolClient的對象, 使用double check null的方式
    public static BinderPoolClient getInsance(Context context) {
        if (sInstance == null) {
            synchronized (BinderPoolClient.class) {
                if (sInstance == null) {
                    sInstance = new BinderPoolClient(context);
                }
            }
        }
        return sInstance;
    }

//用CountDownLatch的目的是把bindService()的操作由異步操作轉(zhuǎn)換為同步操作.
    private synchronized void connectBinderPoolService() {
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);//初始化計數(shù)值為1.
        Intent service = new Intent(mContext, BinderPoolService.class);
        mContext.bindService(service, mBinderPoolConnection,
                Context.BIND_AUTO_CREATE);
        try {
        //讓當前線程進入阻塞狀態(tài)揣云, 直到mConnectBinderPoolCountDownLatch的值為0時, 當前線程才能往下執(zhí)行.
            //這就保證了在執(zhí)行完onServiceConnected()后冰啃,才能繼續(xù)往下執(zhí)行, 當前方法才會退出.
        //這樣就把一個異步操作轉(zhuǎn)換成了同步操作邓夕,
            mConnectBinderPoolCountDownLatch.await(); 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * query binder by binderCode from binder pool
     * 
     * @param binderCode
     *            the unique token of binder
     * @return binder who's token is binderCode<br>
     *         return null when not found or BinderPoolService died.
     */
    public IBinder queryBinder(int binderCode) {
        IBinder binder = null;
        try {
            if (mBinderPoolClient != null) {
                binder = mBinderPoolClient.queryBinder(binderCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // ignored.
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPoolClient = IBinderPool.Stub.asInterface(service); //給mBinderPoolClient賦值
            try {
                mBinderPoolClient.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();//把mConnectBinderPoolCountDownLatch的值減1.
        }
    };

    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            Log.w(TAG, "binder died.");
            mBinderPoolClient.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
            mBinderPoolClient = null;
            connectBinderPoolService();
        }
    };
}


客戶端真正的使用方法
package com.ryg.chapter_2.binderpool;

import com.ryg.chapter_2.R;
import android.app.Activity;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class BinderPoolActivity extends Activity {
    private static final String TAG = "BinderPoolActivity";

    private ISecurityCenter mSecurityCenter;
    private ICompute mCompute;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_binder_pool);
        new Thread(new Runnable() {

            @Override
            public void run() {
                doWork();
            }
        }).start();
    }

    private void doWork() {
        BinderPoolCient binderPoolClient = BinderPool.getInsance(BinderPoolActivity.this);//創(chuàng)建BinderPool對象, 創(chuàng)建時與BinderPoolService.class進行了綁定.
        IBinder securityBinder = binderPoolClient
                .queryBinder(BinderPool.BINDER_SECURITY_CENTER); //得到的是實現(xiàn)了ISecurityCenter.Stub的binder對象

        mSecurityCenter = (ISecurityCenter) SecurityCenterImpl
                .asInterface(securityBinder);
        Log.d(TAG, "visit ISecurityCenter");
        String msg = "helloworld-安卓";
        System.out.println("content:" + msg);
        try {
            String password = mSecurityCenter.encrypt(msg);//調(diào)用服務端的aidl方法
            System.out.println("encrypt:" + password);
            System.out.println("decrypt:" + mSecurityCenter.decrypt(password));
        } catch (RemoteException e) {
            e.printStackTrace();
        }

        Log.d(TAG, "visit ICompute");
        IBinder computeBinder = binderPoolClient
                .queryBinder(BinderPool.BINDER_COMPUTE);//得到的是實現(xiàn)了ICompute.Stub的binder對象
        ;
        mCompute = ComputeImpl.asInterface(computeBinder);
        try {
            System.out.println("3+5=" + mCompute.add(3, 5));//調(diào)用服務端的aidl方法
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

}


總結: 書中的BinderPool代碼需要在客戶端和服務器端都擁有一份相同的代碼, 不是很好理解阎毅, 我這里對BindPool進行的裁剪焚刚, 客戶端的叫BinderPoolClient,只提供綁定服務的功能. 服務器端的叫BinderPoolServer, 只提供創(chuàng)建IBinderPool.Stub的子類的對象扇调,并在onBind()時把這個對象返回給客戶端使用.
還有一個知識點是, 用CountDownLatch對象矿咕, 把一個異步操作轉(zhuǎn)換為一個同步操作.

到這里, IPC機制就完整地總結完了.
最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狼钮,一起剝皮案震驚了整個濱河市碳柱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌熬芜,老刑警劉巖莲镣,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涎拉,居然都是意外死亡瑞侮,警方通過查閱死者的電腦和手機的圆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來区岗,“玉大人略板,你說我怎么就攤上這事〈鹊蓿” “怎么了叮称?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長藐鹤。 經(jīng)常有香客問我瓤檐,道長,這世上最難降的妖魔是什么娱节? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任挠蛉,我火速辦了婚禮,結果婚禮上肄满,老公的妹妹穿的比我還像新娘谴古。我一直安慰自己,他們只是感情好稠歉,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布掰担。 她就那樣靜靜地躺著,像睡著了一般怒炸。 火紅的嫁衣襯著肌膚如雪带饱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天阅羹,我揣著相機與錄音勺疼,去河邊找鬼。 笑死捏鱼,一個胖子當著我的面吹牛执庐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播导梆,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼轨淌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了问潭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤婚被,失蹤者是張志新(化名)和其女友劉穎狡忙,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體址芯,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡灾茁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年窜觉,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片北专。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡禀挫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拓颓,到底是詐尸還是另有隱情语婴,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布驶睦,位于F島的核電站砰左,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏场航。R本人自食惡果不足惜缠导,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望溉痢。 院中可真熱鬧僻造,春花似錦、人聲如沸孩饼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捣辆。三九已至蔬螟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間汽畴,已是汗流浹背旧巾。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留忍些,地道東北人鲁猩。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像罢坝,于是被迫代替她去往敵國和親廓握。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Jianwei's blog 首頁 分類 關于 歸檔 標簽 巧用Android多進程嘁酿,微信隙券,微博等主流App都在用...
    justCode_閱讀 5,900評論 1 23
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,500評論 25 707
  • 1.為什么要使用多進程? 相信很多同學在實際開發(fā)中闹司,基本都不會去給app劃分進程娱仔,而且,在Android中使用多進...
    一分耕耘一分收獲閱讀 2,977評論 1 5
  • Android開發(fā)藝術探索 第二章IPC機制 Linux中IPC通信方式游桩?答:命名管道牲迫,共享內(nèi)存耐朴,信號量(具體再細...
    方木Rudy閱讀 1,083評論 0 2
  • 2017年第一張: 2017年第二張: 去年的這個時候,不知什么原因盹憎,一股強烈的畫畫欲望在體內(nèi)燃起筛峭。沒有任何基礎的...
    白衡閱讀 230評論 4 5