• IPC 之 AIDL 的使用


    一、AIDL 知识储备

     1. AIDL 文件支持的数据类型:

    •   基本数据类型 (int , long , char , boolean ,double 等);
    •   String 和 CharSequence;
    •   List :只支持 ArrayList,里面每个元素都必须能够被 AIDL 支持;
    •   Map :只支持 HashMap,里面每个元素都必须能够被 AIDL 支持,包括 key 和 value;
    •   Parcelable : 所有实现了 Parcelable 接口的对象;
    •   AIDL : 所有的 AIDL 接口本身也可以在 AIDL 文件中使用。

     2. 自定义的 Parcelable 对象 和 AIDL  对象必须要显示 import 进来,不管他们是否和当前的 AIDL 文件位于同一个包内。

     3. 如果 AIDL 文件中用到了自定义的 Parcelable 对象,那么必须新建一个和它同名的 AIDL 文件,并在其中声明它为 Parcelable 类型。

     4. AIDL 中除了基本数据类型,其他类型的参数必须标上方向:in 、out 或者 inout,in 表示输入型参数 , out 表示输出型参数,inout 表示输入输出型参数。

    二、AIDL 的使用:

      AIDL 是用于多进程通信场景的,我们创建两个应用,一个 Client 和一个 Server 来说明 AIDL 的使用。在示例项目中实现了基本的交互操作,还有 权限验证注册 listener 到服务端监听服务端事件 重连机制

      1. 客户端

        客户端工程目录:

                       

      看图我们可以知道,工程中创建了和 AIDL 相关的四个文件,Book类、Book.aidl 、IBookManager.aidl、IOnNewBookArrivedListener.aidl 四个文件。

      (1) Book类是示例中用于传递的数据类,实现了 Parcelable接口,代码如下:

    package com.sl.analyzebinder;
    
    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;
        }
    
        protected Book(Parcel in) {
            bookId = in.readInt();
            bookName = in.readString();
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(bookId);
            dest.writeString(bookName);
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        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];
            }
        };
    }

      (2)Book.aidl 的作用是要符合上面第一章节第 3 点的规定,代码如下:

    package com.sl.analyzebinder;
    parcelable Book;

      (3)IBookManager.aidl 定义交互操作的接口,代码如下:

    // IBookManager.aidl
    package com.sl.analyzebinder;
    import com.sl.analyzebinder.Book;
    import com.sl.analyzebinder.IOnNewBookArrivedListener;
    
    // Declare any non-default types here with import statements
    
    interface IBookManager {
        List<Book> getBookList();
        void addBook(in Book book);
        void registerListener(IOnNewBookArrivedListener listener);
        void unRegisterListener(IOnNewBookArrivedListener listener);
    }

      (4)IOnNewBookArrivedListener.aidl 定义接听服务事件的接口,代码如下:

    // IOnNewBookArrivedListener.aidl
    package com.sl.analyzebinder;
    import com.sl.analyzebinder.Book;
    // Declare any non-default types here with import statements
    
    interface IOnNewBookArrivedListener {
        void onNewBookArrived(in Book book);
    }

      所有关于AIDL的文件写好之后,我们先 Make Project 一下,然后来看一下客户端如何连接服务端,MainActivity 中进行连接:

    package com.sl.analyzebinder;
    
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.TextView;
    
    import java.lang.ref.WeakReference;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        private static final int MESSAGE_NEW_BOOK_ARRIVED = 110;
        private BookHandler mHandler = new BookHandler(this);
        private IBookManager bookManager;
    
        private static class BookHandler extends Handler {
            private WeakReference<MainActivity> activity;
    
            BookHandler(MainActivity activity) {
                this.activity = new WeakReference<>(activity);
            }
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MESSAGE_NEW_BOOK_ARRIVED:
                        if (activity != null) {
                            activity.get().changeContent((Book) msg.obj);
                        }
                        break;
                    default:
                        super.handleMessage(msg);
                        break;
                }
            }
        }
    
        private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
            @Override
            public void binderDied() {
                if (bookManager == null)
                    return;
                bookManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
                bookManager = null;
                bindService();
    
            }
        };
    
        private void changeContent(Book book) {
            String text = content.getText().toString();
            content.setText(text.concat("-----------").concat(book.bookName));
        }
    
        private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub() {
            @Override
            public void onNewBookArrived(Book book) throws RemoteException {
                mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
            }
        };
        private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) { 
                // onServiceConnected 和 onServiceDisconnected 都在ui线程中执行,不要执行服务端耗时操作,否则 ANR
                bookManager = IBookManager.Stub.asInterface(service);
                try {
                    List<Book> bookList = bookManager.getBookList();
                    StringBuilder result = new StringBuilder();
                    if (bookList != null && bookList.size() > 0) {
                        for (int i = 0; i < bookList.size(); i++) {
                            result.append("----").append(bookList.get(i).bookName);
                        }
                    }
                    content.setText(bookList != null ? result : "失败了");
                    bookManager.addBook(new Book(3, "摆渡人"));
                    bookManager.registerListener(mListener);
                    service.linkToDeath(mDeathRecipient,0); //重连机制
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) { //重连机制
                bookManager = null;
            }
        };
        private TextView content;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            bindService();
            content = findViewById(R.id.book_list);
        }
    
        private void bindService() {
            Intent service = new Intent();
            service.setAction("com.sl.aidl");
            service.setPackage("com.sl.binderservice");
            bindService(service, mConnection, Context.BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(mConnection);
            unRegisterListener();
        }
    
        private void unRegisterListener() {
            if (bookManager != null && bookManager.asBinder().isBinderAlive()) { // 通过Binder 的 isBinderAlive 方法也可以判断 Binder 是否死亡
                try {
                    bookManager.unRegisterListener(mListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public void stopInform(View view) {
            unRegisterListener();
        }
    }

      为什么要先 Make Project 一下呢,目的是为了让系统为我们自动生成 AIDL 中的 Binder 实现,你可以到下面这个目录中去查看一下具体生成的代码:

         

        对了,要想 aidl 目录下的 java 文件被 编译,还需要再 app 下的build.xml 文件中添加配置:

    android {
        sourceSets{
    main{
    java.srcDirs = ['src/main/java', 'src/main/aidl']
    aidl.srcDirs = ['src/main/aidl']
    }
    }

      服务端要求权限验证,所以客户端得添加权限,这个权限在服务端中定义:

    <uses-permission android:name="com.sl.permission.aidl" />

      2. 服务端

          将 客户端 aidl 目录全部拷贝到 服务端,拷贝后来看服务端目录:

            

        拷贝过来之后,仍然需要 Make Project ,道理你懂得。BookManagerService 就是我们的服务了,来看具体实现:

    package com.sl.binderservice;
    
    import android.app.Service;
    import android.content.Intent;
    import android.content.pm.PackageManager;
    import android.os.Binder;
    import android.os.IBinder;
    import android.os.Parcel;
    import android.os.RemoteCallbackList;
    import android.os.RemoteException;
    import android.util.Log;
    
    import com.sl.analyzebinder.Book;
    import com.sl.analyzebinder.IBookManager;
    import com.sl.analyzebinder.IOnNewBookArrivedListener;
    
    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    import java.util.concurrent.atomic.AtomicBoolean;
    
    public class BookManagerService extends Service {
        private static final String TAG = "BMS";
    
        private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>(); //CopyOnWriteArrayList 支持并发读写
        private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>(); //CopyOnWriteArrayList 支持并发读写
        private AtomicBoolean mIsServiceDestory = new AtomicBoolean();
    
        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 boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { //onTransact处进行权限验证
                int check = checkCallingOrSelfPermission("com.sl.permission.aidl");
                if (check == PackageManager.PERMISSION_DENIED) //权限验证
                    return false;
                String packageName = null;
                String[] packages = getPackageManager().getPackagesForUid(getCallingUid()); // uid 验证
                if (packages != null && packages.length > 0) {
                    packageName = packages[0];
                }
                if (packageName == null || !packageName.startsWith("com.sl"))
                    return false;
                return super.onTransact(code, data, reply, flags);
            }
        };
    
        @Override
        public void onCreate() {
            super.onCreate();
            mIsServiceDestory.set(false);
            mBookList.add(new Book(1, "Android"));
            mBookList.add(new Book(2, "IOS"));
    
            new Thread(new ServiceWorker()).start();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            mIsServiceDestory.set(true);
        }
    
        @Override
        public IBinder onBind(Intent intent) { //onBind 处进行权限验证,验证权限并没有通过。不知道是为什么?
    /**int check = checkCallingOrSelfPermission("com.sl.permission.aidl");
          * if (check == PackageManager.PERMISSION_DENIED)
          *           return null;
    */
    return mBinder; } private class ServiceWorker implements Runnable { @Override public void run() { while (!mIsServiceDestory.get()) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } int bookId = mBookList.size() + 1; Book newBook = new Book(bookId, "NewBook#" + bookId); try { onNewBookArrived(newBook); } catch (RemoteException e) { e.printStackTrace(); } } } } private void onNewBookArrived(Book book) throws RemoteException { mBookList.add(book); int count = mListenerList.beginBroadcast(); for (int i = 0; i < count; i++) { IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i); if (listener != null) listener.onNewBookArrived(book); } mListenerList.finishBroadcast(); } }

       记得在 AndroidManifest.xml 中注册服务:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.sl.binderservice">
    
        <permission
            android:name="com.sl.permission.aidl"
            android:description="@string/service_permission"
            android:permissionGroup="com.sl.permissions"
            android:protectionLevel="normal" />
    
        <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=".BookManagerService"
                android:permission="com.sl.permission.aidl"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.sl.aidl" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </service>
        </application>
    
    </manifest>

      BookManagerService 中进行了权限验证,涉及到 自定义权限 查看这篇。除了自定义的Permission 验证外,还有通过 UID 验证,这是 Binder 机制提供的,这就是体现 Binder 通信机制相对其他机制安全的点。

      注意服务端中用到了几个类,这里提一下:

      AtomicBoolean :  支持并发读写

      RemoteCallbackList : 支持并发读写 ,是系统专门提供的用于删除跨进程 listener 的接口,它是一个泛型,支持管理任意的 AIDL 接口 。它还能当客户端进程终止后,它能够自动移除客户端所注册的 listener 。

      CopyOnWriteArrayList : 支持并发读写 。

      这三个这里就不详细介绍了,到这里AIDL 的具体使用也基本完事了,如果有漏的话,后续补上或者告诉我。

      示例工程代码,下载地址  密码: smf7

  • 相关阅读:
    领会一些比较巧妙的算法
    操作系统os常识
    C++中的继承与虚函数各种概念
    我学shell程序的记录
    matlab:linux环境中将m文件编译成动态链接库
    struct内存对齐:gcc与VC的差别
    fedora中丢失或损坏fstab,无法启动,如何补救
    判断一个字符串中的字符是否都在另一个中出现
    linux下的不错的小软件:apvlv,zathura和vifm
    C语言中将结构体写入文件
  • 原文地址:https://www.cnblogs.com/aimqqroad-13/p/8953298.html
Copyright © 2020-2023  润新知