IPC进程间通信的使用(三)——AIDL

这一篇记录 AIDL(Android Interface Definition Language,安卓接口定义语言)。
AIDL 默认支持的数据类型包括:

  • Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。
  • String 类型。
  • CharSequence类型。
  • List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。List可以使用泛型。
  • Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。

AIDL接口的创建

两个aidl文件
// Book.aidl
package com.kevin.testaidl;

parcelable Book;
// IBookManager.aidl
package com.kevin.testaidl;
import com.kevin.testaidl.Book;//这里一定要手动导入

interface IBookManager {

void addBook(in Book b);
List<Book> getBookList();
}

代码中使用了自定义的对象Book,一定要在aidl文件中手动导入进来。同时一定要新建一个同名的aild文件,Book.aidl.

AIDL已经定义好了,找到Android Studio的make project进行编译,会在generatedJava里面
生成IBookManager。

服务端Service

/**
 * Created by Kevin on 2019/3/21<br/>
 * Blog:https://blog.csdn.net/student9128<br/>
 * Describe:<br/>
 * 服务端
 */
public class AIDLService extends Service {
    private final String TAG = this.getClass().getSimpleName();
    //    private List<Book> mBooks = new ArrayList<>();//aidl只支持arrayList
    private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();//保证线程同步

    private IBookManager.Stub mBookManager = new IBookManager.Stub() {

        @Override
        public void addBook(Book b) throws RemoteException {
            synchronized (this) {
                if (mBooks == null) {
//                    mBooks = new ArrayList<Book>();
                    mBooks = new CopyOnWriteArrayList<>();
                }
                if (b == null) {
                    Log.e(TAG, "Book is null in In");
                    b = new Book();
                }
                b.setPrice(123);//在服务端修改了book的价格
                if (!mBooks.contains(b)) {
                    mBooks.add(b);
                }
                //打印mBooks列表,观察客户端传过来的值
                StringBuilder sb = new StringBuilder();
                for (Book boo : mBooks) {
                    sb.append("book: name is " + boo.getName() + ",price is" + boo.getPrice());
                }
                Log.e(TAG, "invoking addBooks() method , now the list is : " + sb.toString());
            }
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
          //  SystemClock.sleep(10000);
            synchronized (this) {
                if (mBooks != null) {
                    return mBooks;
                }
            }
            return new CopyOnWriteArrayList<>();
        }
    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setName("Android ADIL 测试");
        book.setPrice(33);
        mBooks.add(book);
        Log.w(TAG, "onCreate");
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, String.format("on bind,intent=%s", intent.toString()));
        return mBookManager;
    }
}

在onCrate中初始化添加了一本图书信息,然后创建了一个Binder对象mBookManager并在onBind中返回它,这个对象实现了IBookManager.Stub内部的ADIL方法——addBook和getBookList方法。 可以看到addBook方法就是将添加的Book修改了price,添加到mBooks集合中,mBooks是CopyOnWriteArrayList对象,CopyOnWriteArrayList支持并发读写,保证多个客户端连接是,线程同步。
清单文件配置:

        <service
            android:name=".AIDLService"
            android:exported="true"
            android:permission="com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.kevin.aidl" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

        </service>

客户端

客户端绑定远程服务,绑定成功后将服务端返回的Binder对象转换成AIDL接口,然后通过这个接口去调用服务端的远程方法。

public class MainActivity extends AppCompatActivity {
    private IBookManager mBookManager = null;
    private boolean mBound = false;
    private List<Book> mBooks;
    private final String TAG = getClass().getSimpleName();
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;


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

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

    private ServiceConnection mSC = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(getLocalClassName(), "service connected");
            mBookManager = IBookManager.Stub.asInterface(service);
            if (mBookManager != null) {
                try {
                    Book book = new Book();
                    book.setPrice(99);
                    book.setName("Test");
                    mBookManager.addBook(book);
                   mBooks = mBookManager.getBookList();
                    for (Book b : mBooks) {

                        Log.d(TAG, "已存在的书本:名字:" + b.getName() + "价格:" + b.getPrice());
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.w(getLocalClassName(), "service disconnected");
        
        }
    };
}

在这里插入图片描述
可以看到,总共添加了两本书,第一本是在服务端添加的,第二本是在客户端添加的。
现在增加一种场景,用户不希望每次都手动去获取服务端增加的新书,而是希望,每次一有新书到来,用户可以收到相关提醒。
添加一个aidl接口,aidl无法使用普通接口

// IOnNewBookArrivedListener.aidl
package com.kevin.testaidl;
import com.kevin.testaidl.Book;

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

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

// IBookManager.aidl
package com.kevin.testaidl;
import com.kevin.testaidl.Book;
import com.kevin.testaidl.IOnNewBookArrivedListener;
// Declare any non-default types here with import statements

interface IBookManager {

void addBook(in Book b);
List<Book> getBookList();
void registerListener(IOnNewBookArrivedListener listener);
void unRegisterListener(IOnNewBookArrivedListener listener);

}

服务端代码修改:

public class AIDLService extends Service {
    private final String TAG = this.getClass().getSimpleName();
    //    private List<Book> mBooks = new ArrayList<>();//aidl只支持arrayList
    private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();//保证线程同步
    //使用RemoteCallbackList存储 listener,这样可以解注册
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

    private IBookManager.Stub mBookManager = new IBookManager.Stub() {

        @Override
        public void addBook(Book b) throws RemoteException {
            synchronized (this) {
                if (mBooks == null) {
//                    mBooks = new ArrayList<Book>();
                    mBooks = new CopyOnWriteArrayList<>();
                }
                if (b == null) {
                    Log.e(TAG, "Book is null in In");
                    b = new Book();
                }
                b.setPrice(123);
                if (!mBooks.contains(b)) {
                    mBooks.add(b);
                }
                //打印mBooks列表,观察客户端传过来的值
                StringBuilder sb = new StringBuilder();
                for (Book boo : mBooks) {
                    sb.append("book: name is " + boo.getName() + ",price is" + boo.getPrice());
                }
                Log.e(TAG, "invoking addBooks() method , now the list is : " + sb.toString());
            }
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
//            SystemClock.sleep(10000);
            synchronized (this) {
                if (mBooks != null) {
                    return mBooks;
                }
            }
            return new CopyOnWriteArrayList<>();
        }

        @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();
        Book book = new Book();
        book.setName("Android ADIL 测试");
        book.setPrice(33);
        mBooks.add(book);
        Log.w(TAG, "onCreate");
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, String.format("on bind,intent=%s", intent.toString()));
        return mBookManager;
    }

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

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

    private class ServiceWorker implements Runnable {

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBooks.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }
        }
    }
}

从代码中可以看到,创建了一个 RemoteCallbackList<>对象用来存储IOnNewBookArrivedListener。(RemoteCallbackList是系统专门提供的专门用于删跨进程listener的接口。RemoteCallbackList是一个泛型,支持管理任意的AIDL接口。

public class RemoteCallbackList<E extends IInterface>

从声明中可以看出,所以的ADIL接口都继承自IInterface。)

实现了IBookManager.Stub内部的registerListener,unRegisterListener在这两个方法中去实现listener的注册和解注册。
同时添加了一个onNewBookArrived方法,当有新书的时候,添加到mBooks集合,并调用IOnNewBookArrivedListener的onNewBookArrived进行通知用户,新书到了。
注意这里需要使用beginBroadcastfinishBroadcast
客户端代码修改:

public class MainActivity extends AppCompatActivity {
    private IBookManager mBookManager = null;
    private boolean mBound = false;
    private List<Book> mBooks;
    private final String TAG = getClass().getSimpleName();
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button = findViewById(R.id.btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                //开子线程防止服务器端进行耗时操作,app出现ANR
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            if (mBookManager != null) {
                                List<Book> bookList = mBookManager.getBookList();
                                for (Book b : bookList) {
                                    Log.d(TAG, "已存在的书本:名字:" + b.getName() + "价格:" + b.getPrice());
                                }
                            }
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
        Intent intent = new Intent(this, AIDLService.class);
        deathRecipient = new DeathRecipient() {

            @Override
            public void binderDied() {
                if (mBookManager == null) {
                    return;
                }
                mBookManager.asBinder().unlinkToDeath(deathRecipient, 0);
                mBookManager = null;
                attemptToBindService();//Binder die后,重新绑定服务
            }
        };

        bindService(intent, mSC, Context.BIND_AUTO_CREATE);
    }

    public void addBook() {
        if (!mBound) {
            attemptToBindService();
            Toast.makeText(this, "当前与服务的处于未连接状态,正在尝试连", Toast.LENGTH_SHORT).show();
            return;
        }
        if (mBookManager == null)
            return;
        Book book = new Book();
        book.setName("App研发录");
        book.setPrice(30);
        try {
            mBookManager.addBook(book);
            Log.d(getLocalClassName(), "添加的课本:name:" + book.getName() + "\tprice:" + book.getPrice());
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

    private void attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("com.kevin.aidl");
        intent.setPackage("com.kevin.testaidl");
        bindService(intent, mSC, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        if (mBookManager != null && mBookManager.asBinder().isBinderAlive()) {
            try {
                Log.i(TAG, "unregister listner:" + mOnNewBookArrivedListener);
                mBookManager.unRegisterListener(mOnNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mSC);
        super.onDestroy();
    }

    private ServiceConnection mSC = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.d(getLocalClassName(), "service connected");
            try {
                service.linkToDeath(deathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mBookManager = IBookManager.Stub.asInterface(service);
            mBound = true;
            if (mBookManager != null) {
                try {
                    Book book = new Book();
                    book.setPrice(99);
                    book.setName("Test");
                    mBookManager.addBook(book);
                    mBooks = mBookManager.getBookList();
                    for (Book b : mBooks) {

                        Log.d(TAG, "已存在的书本:名字:" + b.getName() + "价格:" + b.getPrice());
                    }
                    mBookManager.registerListener(mOnNewBookArrivedListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.w(getLocalClassName(), "service disconnected");
            mBound = false;
        }
    };
    //运行中Binder线程池中,不能在它里面访问UI相关的内容,如果要访问,使用Handler切换到UI线程
    // Binder可能会意外死亡,使用DeathRecipient监听,当Binder死亡是binderDied方法中重新连接服务
    //或者在onServiceDisconnected重新连接服务
    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrived(Book newBook) throws RemoteException {
            //
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
        }
    };
}

可以看到代码中为了防止Binder以外死亡,使用了DeathRecipient,当Binder死亡了,可以在binderDied方法中监听到,重新连接服务。
在使用进程间通信的过程中,还有一点是要去验证应用的权限,这里可以为服务自己定义一个权限,在使用的过程中在清单文件去声明下这个权限,服务端可以进行权限验证。

两种方法,

  1. 在onBind方法中验证权限,如果验证失败,返回null

        @Override
        public IBinder onBind(Intent intent) {
            //在这里校验权限,如果客户端没有声明权限会一直连接不上,但是不知道为什么,不会有提示
           int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE");
            if (i == PackageManager.PERMISSION_DENIED) {
                return null;
            }
            Log.i(TAG, String.format("on bind,intent=%s", intent.toString()));
            return mBookManager;
        }
    
    
  2. 在服务端的onTransact方法中进行权限验证,如果失败就返回false,这样服务端就不会终止执行AIDL中的方法从而达到保护服务端的效果。

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            //验证权限
            int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE");
            if (i == PackageManager.PERMISSION_DENIED) {
                Log.i(TAG, "权限不被允许");
                return false;
            }
            //验证包名
            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            if (!packageName.startsWith("com.kevin.testaidl")) {
                Log.i(TAG, "包名不对");
                return false;
            }
            return super.onTransact(code, data, reply, flags);
        }
    };

清单文件的配置
服务端:

   <permission
        android:name="com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE"
        android:protectionLevel="signature" />

客户端:

    <uses-permission android:name="com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE" />

在这里插入图片描述

最后贴一下服务端的完整代码:

/**
 * Created by Kevin on 2019/3/21<br/>
 * Blog:https://blog.csdn.net/student9128<br/>
 * Describe:<br/>
 * 服务端
 */
public class AIDLService extends Service {
    private final String TAG = this.getClass().getSimpleName();
    //    private List<Book> mBooks = new ArrayList<>();//aidl只支持arrayList
    private CopyOnWriteArrayList<Book> mBooks = new CopyOnWriteArrayList<>();//保证线程同步
    //使用RemoteCallbackList存储 listener,这样可以解注册
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);

    private IBookManager.Stub mBookManager = new IBookManager.Stub() {

        @Override
        public void addBook(Book b) throws RemoteException {
            synchronized (this) {
                if (mBooks == null) {
//                    mBooks = new ArrayList<Book>();
                    mBooks = new CopyOnWriteArrayList<>();
                }
                if (b == null) {
                    Log.e(TAG, "Book is null in In");
                    b = new Book();
                }
                b.setPrice(123);
                if (!mBooks.contains(b)) {
                    mBooks.add(b);
                }
                //打印mBooks列表,观察客户端传过来的值
                StringBuilder sb = new StringBuilder();
                for (Book boo : mBooks) {
                    sb.append("book: name is " + boo.getName() + ",price is" + boo.getPrice());
                }
                Log.e(TAG, "invoking addBooks() method , now the list is : " + sb.toString());
            }
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
//            SystemClock.sleep(10000);
            synchronized (this) {
                if (mBooks != null) {
                    return mBooks;
                }
            }
            return new CopyOnWriteArrayList<>();
        }

        @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);
//            } else {
//                Log.d(TAG, "not found the listener,cannot unregister");
//            }
//            Log.d(TAG, "unRegisterListener,current size :" + mListenerList.size());
            mListenerList.unregister(listener);
        }

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            //验证权限
            int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE");
            if (i == PackageManager.PERMISSION_DENIED) {
                Log.i(TAG, "权限不被允许");
                return false;
            }
            //验证包名
            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            if (!packageName.startsWith("com.kevin")) {
                Log.i(TAG, "包名不对");
                return false;
            }
            return super.onTransact(code, data, reply, flags);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Book book = new Book();
        book.setName("Android ADIL 测试");
        book.setPrice(33);
        mBooks.add(book);
        Log.w(TAG, "onCreate");
        new Thread(new ServiceWorker()).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        //在这里校验权限,如果客户端没有声明权限会一直连接不上,但是不知道为什么,不会有提示
//        int i = checkCallingOrSelfPermission("com.kevin.testaidl.permission.ACCESS_BOOK_SERVICE");
//        if (i == PackageManager.PERMISSION_DENIED) {
//            return null;
//        }
        Log.i(TAG, String.format("on bind,intent=%s", intent.toString()));
        return mBookManager;
    }

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

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

    private class ServiceWorker implements Runnable {

        @Override
        public void run() {
            while (!mIsServiceDestroyed.get()) {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBooks.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }
        }
    }
}

注:本文章知识点来自学习《Android开发艺术探索》一书

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章