IPC机制的AIDL进阶,结合观察者模式

好久没写博客了,最近确实挺忙的,感情也不顺利,一直萎靡着,就这样吧,进入主题了.

在半年前写过篇AIDL的入门Demo,< IPC机制第二篇,AIDL实现> 这边的案例实现是,客户端只有去请求服务器,服务器才会返回结果,那假设有这样一个需求:用户不想时不时地去访问,而是希望当服务器有最新咨询的时候能够主动推送到客户端上来,这就是一种典型的观察者模式了,当然里面还有一些问题需要注意的我们等下来总结.

1 . IOnNewBookArrivedListener.aidl

// IOnNewBookArrivedListener.aidl
package com.tom.aidldemo;

// Declare any non-default types here with import statements
import com.tom.aidldemo.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrived(in Book book);
}

2 . IBookManager.aidl

// IBookManager.aidl
package com.tom.aidldemo;

// Declare any non-default types here with import statements
import com.tom.aidldemo.Book;
import com.tom.aidldemo.IOnNewBookArrivedListener;

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);
}

3 . Book.java

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(this.bookId);
        dest.writeString(this.bookName);
    }

    public Book() {
    }

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

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

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

4 . Book.aidl

// Book.aidl.aidl
package com.tom.aidldemo;

// Declare any non-default types here with import statements
parcelable Book;

5 . 服务器代码BookManagerService.java

public class BookManagerService extends Service {
    private static final String TAG = "BookManagerService";
    private CopyOnWriteArrayList<Book> mBookList =  new CopyOnWriteArrayList<Book>();

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);

//    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
//            new CopyOnWriteArrayList<IOnNewBookArrivedListener>();

    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =
            new RemoteCallbackList<IOnNewBookArrivedListener>();

    public BookManagerService() {
    }
    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
            //这里也要进行一次通知观察者,省略了...
        }

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

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

    @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 void onDestroy() {
        mIsServiceDestoryed.set(true);
        super.onDestroy();
    }

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

    private class ServiceWorker implements Runnable {
        @Override
        public void run() {
            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 {
                    onNewBookArray(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

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

6 . 客户端代码MainActivity.java

public class MainActivity extends AppCompatActivity {

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

    private IBookManager mRemoteBookManager;

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

    private ServiceConnection mConn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                mRemoteBookManager = bookManager;
                List<Book> bookList = bookManager.getBookList();
                Log.i(TAG, "query book list, list type: " +
                        bookList.getClass().getCanonicalName());
                Log.i(TAG, "query book list: " + bookList.toString());

                Book newBook = new Book(3, "数学之美");
                bookManager.addBook(newBook);
                Log.i(TAG, "onServiceConnected: " + newBook);

                bookList = bookManager.getBookList();
                Log.i(TAG, "query book list, list type: " +
                        bookList.getClass().getCanonicalName());
                Log.i(TAG, "query book list: " + bookList.toString());

                bookManager.registerListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mRemoteBookManager = null;
            Intent intent = new Intent(MainActivity.this, BookManagerService.class);
            bindService(intent, mConn, Context.BIND_AUTO_CREATE);
        }
    };

    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConn, Context.BIND_AUTO_CREATE);
    }

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

AIDL难点理解:
1 . AIDL底层是通过Binder实现的,但是Binder会把客户端传来的对象重新转化并生成一个新的对象.虽然我们在观察者模式注册和解注册的时候使用的是同一个对象,但是通过Binder传递到服务器之后会产生两个对象.因为对象是不能够跨进程传输的,实际的传输是Binder的序列化和反序列化的过程,但是两次的序列化操作Binder是同一个.
解决方案使用RemoteCallbackList.

2 . 不管是客户端还是服务器,我们都可以看到每个方法都会抛一个异常RemoteException.为什么?因为进程间的通信,不管是服务端也好还是客户端也好跨进程的方法都是在Binder线程池中的运行的,也就是说是非UI线程.所以切记耗时不能运行在主线程中,不然就会报ANR了.

3 . Binder是会意外死亡的,所以我们要在onServiceDisconnected中进行重连远程服务

4 . 客户端和服务端的权限验证,这个请读者自行百度,主要是在服务端的onBind方法是否返回null来控制.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值