Android进程间通信——AIDL

上一篇我们在使用Messenger来进行进程间通信的方法,可以发现,Messenger是以串行的方式处理客户端发来的消息,如果大量的消息同时发送到服务端,服务断仍然只能一个个处理,如果有大量的并发请求,那么用Messenger就不太合适了。同时,Messenger的作用主要是为了传递消息,很多时候我们可能需要跨进程调用服务端的方法,这种情形用Messenger就无法做到了,但是我们可以使用AIDL来实现跨进程的方法调用。具体步骤如下:

服务端:

          1.服务端创建一个Service用来监听客户端的连接请求

          2.创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明

          3.在Service中实现这个AIDL接口

客户端:

          1.绑定服务端的Service

           2.绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型

           3.调用AIDL中的方法

项目的目录如下:

首先我们先在com.example.bookmanager包下创建aidl目录,并在这个目录下创建Book.class类,Book类实现Parcelable接口

public class Book implements Parcelable {
    private int bookId;
    private String bookName;

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

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

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

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

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

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

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

AIDL接口的创建

接着我们在com.example.bookmanager.aidl目录下,使用Android Studio来创建IBookManager.aidl的AIDL文件

Android Studio会自动帮我们在main目录下创建aidl接口文件

// IOnNewBookArrivedListener.aidl
package com.example.bookmanager.aidl;
import com.example.bookmanager.aidl.Book;

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

interface IOnNewBookArrivedListener {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void onNewBookArrived(in Book book);
}
// IBookManager.aidl
package com.example.bookmanager.aidl;
import com.example.bookmanager.aidl.Book;
import com.example.bookmanager.aidl.IOnNewBookArrivedListener;

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

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    List<Book> getBookList();
    void addBook(in Book book);//in 表示输入型参数,out表示输出型参数,inout表示输入输出型参数
    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);
}

注意:Book是自定义的Parcelable对象,IOnNewBookArrivedListener是AIDL对象,虽然它们和当前IBookManager.aidl在同一个包内,我们仍然需要显示地import进来。上面代码还使用观察者模式,每个感兴趣的用户都观察新书,当新书到的时候,BookManager就通知每一个对这本书感兴趣的用户。

// Book.aidl
package com.example.bookmanager.aidl;

parcelable Book;

注意:Book是自定义的Parcelable对象,还必须创建一个和它同名的AIDL文件,并在其中声明它为Parcelable类型

远程服务端Service的实现

public class BookManagerService extends Service {
    private static final String TAG = "BookManagerService";
    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
    //CopyOnWriteArrayList支持并发读/写
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    //    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<>();
    //当客户端进程终止后,RemoteCallbackList能够自动移除客户端所注册的listener。
    //RemoteCallbackList内部自动实现了线程同步的功能
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
    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 {
//            if (!mListenerList.contains(listener)) {
//                mListenerList.add(listener);
//            } else {
//                Log.d(TAG, "listener already exists.");
//            }
//            Log.d(TAG, "registerListener, size:" + mListenerList.size());
            mListenerList.register(listener);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
//            if (mListenerList.contains(listener)) {
//                mListenerList.remove(listener);
//                Log.d(TAG, "unregister listener succeed.");
//            } else {
//                Log.d(TAG, "not found, can not unregister.");
//            }
//
//            Log.d(TAG, "unregisterListener, current size:" + mListenerList.size());
            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();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //1.这里可以做权限验证,验证不通过返回null,绑定失败
        //2.使用permission验证
        //还有一种验证方式是在服务端的onTransact方法中进行权限验证,验证失败就直接返回false。
//        int check = checkCallingOrSelfPermission("com.example.bookmanager.permission.ACCESS_BOOK_SERVICE");
//        if (check == PackageManager.PERMISSION_DENIED) {
//            return null;
//        }
//        String packageName = null;
//        String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
//        if (packages != null && packages.length > 0) {
//            packageName = packages[0];
//        }
//        if (!packageName.startsWith("com.example")) {
//            return null;
//        }
        return mBinder;
    }

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

    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
//        Log.d(TAG, "onNewBookArrived, notify listeners size:" + mListenerList.size());
//        for (IOnNewBookArrivedListener listener : mListenerList) {
//            Log.d(TAG, "onNewBookArrived, notify listener:" + listener);
//            listener.onNewBookArrived(book);
//        }
        final int N = mListenerList.beginBroadcast();
        for (int i = 0; i < N; i++) {
            IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
            if (listener != null) {
                try {
                    listener.onNewBookArrived(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
        mListenerList.finishBroadcast();
    }

    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 book = new Book(bookId, "new book # " + bookId);
                try {
                    onNewBookArrived(book);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注意:AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以要在AIDL方法中处理线程同步,我们这里直接使用CopyOnWriteArrayList来进行自动的线程同步

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.bookmanager">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".aidl.BookManagerService"
            android:process=":remote" />
    </application>

</manifest>

客户端的实现

首先绑定远程服务,绑定成功后就将服务端返回的Binder对象转换成AIDL接口,然后就可以通过这个接口去调用服务端的远程犯法了。这里我们客户端和服务端在同一个工程中,不同工程除了需要复制AIDL接口所相关的包到客户端,其他完全一样。

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
    private TextView textView;
    private IBookManager mRemoteBookManager;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MESSAGE_NEW_BOOK_ARRIVED:
                    textView.setText("receive new book:" + msg.obj);
                    Log.d(TAG, "receive new book:" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
            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());
                bookManager.addBook(new Book(3, "Python"));
                List<Book> newList = bookManager.getBookList();
                Log.i(TAG, "query new book list:" + newList.toString());
                bookManager.registerListener(onNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mRemoteBookManager = null;
            Log.e(TAG, "binder died.");
        }
    };
    private IOnNewBookArrivedListener onNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book book) throws RemoteException {
            //这个方法是在客户端的Binder线程池中执行的,因此,更新UI,需要一个Handler
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
        }
    };

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

        textView = findViewById(R.id.textview);

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

    @Override
    protected void onDestroy() {
        if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) {
            Log.i(TAG, "unregister listener:" + onNewBookArrivedListener);
            try {
                mRemoteBookManager.unregisterListener(onNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mConnection);
        super.onDestroy();
    }
}

注意:服务端的方法有可能需要很久才能执行完毕,这个时候上面的代码就会导致ANR,这里这样写是为了更好的了解AIDL的实现步骤。

最后效果如下图:

参考:《Android开发艺术探索》

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值