Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> AIDL(續)

AIDL(續)

編輯:關於Android編程

這篇文章講的是在不同的工程文件中實現IPC。這次我決定用一個工程完成

首先,我先介紹一下流程

1服務端

先創建Service來監聽客戶端的連接請求,然後創建AIDL文件,將暴露給客戶端的接口在這個aidl文件中聲明,最後在service中實現這個接口

2客戶端

綁定客戶端的service。綁定成功後將服務端返回的binder對象轉成aidl接口所屬的類型,接著就可以調用aidl中的方法

具體步驟

(1)創建AIDL文件,聲明接口

文件名稱IBookManager.aidl。注意無論Book類在哪個包下都要import,package也是必需的。所有參數必須標上in,out,inout

package com.example.aidl.service;
import com.example.aidl.service.Book;
interface IBookManager{
    List getBookList();
    void addBook(in Book book);
}

另外,如果要用到實體類,必須繼承Parcelable,而且要創建和它同名的aidl文件
Book.java

package com.example.aidl.service;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
    public int bookId;
    public String bookName;

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

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

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

    public static Parcelable.Creator CREATOR = new Parcelable.Creator() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source.readInt(), source.readString());
        }

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

    @Override
    public String toString() {
        return "Book [bookId=" + bookId + ", bookName=" + bookName + "]";
    }
}

Book.aidl
必須這樣申明。package + parcelable

package com.example.aidl.service;
parcelable Book;

(2)創建service實現這個接口(BookManagerService.java)

package com.example.aidl;

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

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

import com.example.aidl.service.Book;

public class BookManagerService extends Service {
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList();

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

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List getBookList() throws RemoteException {
            return mBookList;
        }

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

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


然後注冊service並且設置為remote

        

(3)客戶端的實現
綁定service。綁定成功後將服務端返回的binder對象轉成aidl接口所屬的類型,接著就可以調用aidl中的方法

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);

            try {
                List list = bookManager.getBookList();
                for (int i = 0; i < list.size(); i++) {
                    Log.e("booklist", list.get(i).toString());
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(connection);
    }
}

效果截圖
這裡寫圖片描述

同時,我們可以試著調用addBook接口

            try {
                List list = bookManager.getBookList();
                for (int i = 0; i < list.size(); i++) {
                    Log.e("booklist", list.get(i).toString());
                }

                bookManager.addBook(new Book(3, "develop"));
                list = bookManager.getBookList();
                for (int i = 0; i < list.size(); i++) {
                    Log.e("booklist", list.get(i).toString());
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }

效果截圖
這裡寫圖片描述

現在我們在考慮一種情況,假設當有一本新書的時候直接通知用戶(觀察者模式)
首先要提供一個aidl接口,普通接口無法使用(IOnNewBookArrivedListener.aidl)

package com.example.aidl.service;
import com.example.aidl.service.Book;
interface IOnNewBookArrivedListener{
    void onNewBookArrived(in Book book);
}

同時需要在原有接口中添加兩個新方法

package com.example.aidl.service;
import com.example.aidl.service.Book;
import com.example.aidl.service.IOnNewBookArrivedListener;
interface IBookManager{
    List getBookList();
    void addBook(in Book book);
    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);
}

這樣一來BookManagerService.java會自動生成兩個新的方法。同時開啟一個線程,每隔5s就向書庫中添加一個本書並通知所有感興趣單位客戶

public class BookManagerService extends Service {
    private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList();
    private CopyOnWriteArrayList mListenerList = new CopyOnWriteArrayList();
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

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

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List getBookList() throws RemoteException {
            return mBookList;
        }

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

        @Override
        public void registerListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            if (!mListenerList.contains(listener)) {
                mListenerList.add(listener);
            }
            Log.e("BookManagerService", "registerListener size:"
                    + mListenerList.size());
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            if (mListenerList.contains(listener)) {
                mListenerList.remove(listener);
            }
            Log.e("BookManagerService", "unregisterListener size:"
                    + mListenerList.size());
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "iOS"));

        // 每隔5s通知一次
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (!mIsServiceDestroyed.get()) {
                    try {
                        Thread.sleep(5000);
                        onNewBookArrived(new Book(mBookList.size(), "test"));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        for (int i = 0; i < mListenerList.size(); i++) {
            IOnNewBookArrivedListener listener = mListenerList.get(i);
            listener.onNewBookArrived(book);
        }
    }

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

此外還要修改一下客戶端代碼。注冊aidl接口,activity退出時要解注冊

public class MainActivity extends Activity {
    private IBookManager mRemoteBookManager;

    private static Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x001) {
                Log.e("MainActivity", "receive new book :" + msg.obj);
            }
        }
    };

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

        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connection = new ServiceConnection() {

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

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);

            try {
                mRemoteBookManager = bookManager;//這一句不能忘

                List list = bookManager.getBookList();
                for (int i = 0; i < list.size(); i++) {
                    Log.e("booklist", list.get(i).toString());
                }

                bookManager.addBook(new Book(3, "develop"));
                list = bookManager.getBookList();
                for (int i = 0; i < list.size(); i++) {
                    Log.e("booklist", list.get(i).toString());
                }
                bookManager.registerListener(mIOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    };

    private IOnNewBookArrivedListener mIOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            handler.obtainMessage(0x001, book).sendToTarget();
        }
    };

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null
                && mRemoteBookManager.asBinder().isBinderAlive()) {
            try {
                mRemoteBookManager
                        .unregisterListener(mIOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(connection);
        super.onDestroy();
    }
}

這裡寫圖片描述

按back鍵,發現unregister size = 1
這裡寫圖片描述
也就是說並沒有解注冊。
為什麼呢?因為這是多進程,對象是不能跨進程傳輸的,binder會把客戶端傳遞過來的對象重新轉化並生成一個新的對象。
我們可以用RemoteCallbackList(後續會講解)
修改代碼<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> private RemoteCallbackList mListenerList = new RemoteCallbackList();

注冊和解注冊代碼也要改

        @Override
        public void registerListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListenerList.register(listener);
            Log.e("registerListener",
                    mListenerList.getRegisteredCallbackCount() + "");
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)
                throws RemoteException {
            mListenerList.unregister(listener);
            Log.e("unregisterListener",
                    mListenerList.getRegisteredCallbackCount() + "");
        }

同時修改onNewBookArrived函數

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener listener = mListenerList
                    .getBroadcastItem(i);
            if (listener != null) {
                listener.onNewBookArrived(book);
            }
        }
        mListenerList.finishBroadcast();
    }

這裡寫圖片描述

最後介紹一下RemoteCallbackList。我把源碼貼出來,去掉注解其實很容易看懂

public class RemoteCallbackList {
    ArrayMap mCallbacks = new ArrayMap();//用來保存aidl接口的容器
    private Object[] mActiveBroadcast;
    private int mBroadcastCount = -1;
    private boolean mKilled = false;

    //Service進程被異常的退出時,比如被kill掉,這時系統會調用這個IBinder之前通過linkToDeath注冊的DeathRecipient類對象的binderDied函數來釋放資源
    private final class Callback implements IBinder.DeathRecipient {
        final E mCallback;
        final Object mCookie;

        Callback(E callback, Object cookie) {
            mCallback = callback;
            mCookie = cookie;
        }

        public void binderDied() {
            synchronized (mCallbacks) {
                mCallbacks.remove(mCallback.asBinder());
            }
            onCallbackDied(mCallback, mCookie);
        }
    }

    public boolean register(E callback) {
        return register(callback, null);
    }

    //將callback添加到ArrayMap中
    public boolean register(E callback, Object cookie) {
        synchronized (mCallbacks) {
            if (mKilled) {
                return false;
            }
            IBinder binder = callback.asBinder();
            try {
                Callback cb = new Callback(callback, cookie);
                binder.linkToDeath(cb, 0);
                mCallbacks.put(binder, cb);
                return true;
            } catch (RemoteException e) {
                return false;
            }
        }
    }

    // remove函數
    public boolean unregister(E callback) {
        synchronized (mCallbacks) {
            Callback cb = mCallbacks.remove(callback.asBinder());
            if (cb != null) {
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
                return true;
            }
            return false;
        }
    }

    //清空容器
    public void kill() {
        synchronized (mCallbacks) {
            for (int cbi=mCallbacks.size()-1; cbi>=0; cbi--) {
                Callback cb = mCallbacks.valueAt(cbi);
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
            }
            mCallbacks.clear();
            mKilled = true;
        }
    }

    public void onCallbackDied(E callback) {
    }

    public void onCallbackDied(E callback, Object cookie) {
        onCallbackDied(callback);
    }

    /**
     * Prepare to start making calls to the currently registered callbacks.
     * This creates a copy of the callback list, which you can retrieve items
     * from using {@link #getBroadcastItem}.  Note that only one broadcast can
     * be active at a time, so you must be sure to always call this from the
     * same thread (usually by scheduling with {@link Handler}) or
     * do your own synchronization.  You must call {@link #finishBroadcast}
     * when done.
     *
     *

A typical loop delivering a broadcast looks like this: * *

     * int i = callbacks.beginBroadcast();
     * while (i >= 0) {
     *     i--;
     *     try {
     *         callbacks.getBroadcastItem(i).somethingHappened();
     *     } catch (RemoteException e) {
     *         // The RemoteCallbackList will take care of removing
     *         // the dead object for us.
     *     }
     * }
     * callbacks.finishBroadcast();
* * @return Returns the number of callbacks in the broadcast, to be used * with {@link #getBroadcastItem} to determine the range of indices you * can supply. * * @see #getBroadcastItem * @see #finishBroadcast */ //beginBroadcast和finishBroadcast必須配對使用 public int beginBroadcast() { synchronized (mCallbacks) { if (mBroadcastCount > 0) { throw new IllegalStateException( "beginBroadcast() called while already in a broadcast"); } final int N = mBroadcastCount = mCallbacks.size(); if (N <= 0) { return 0; } Object[] active = mActiveBroadcast; if (active == null || active.length < N) { mActiveBroadcast = active = new Object[N]; } for (int i=0; i
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved