Android IPC(跨进程通信)之AIDL

IPC——跨进程通信,是指两个进程之间的数据交换过程。在说IPC的同时我们要知道什么是进程,什么是线程。线程是CPU调度的最小单元,进程可以理解为一个程序或者一个应用。一个进程中可以运行多个线程,而在Android程序中有一个主线程,也叫UI线程。
在Android上,一个应用代表一个进程,当你运行应用的是时候,Android会为你分配一个独立的虚拟机,这也就相当于给你分配一块独立的内存,程序中使用的对象以及数据可以在这里共享的。但当你开启多进程时,这个进程的内存跟应用的内存就是两块不同的内存,这个时候两个内存之间的数据是不可以共享的。
多进程会产生以下几个问题:
(1)静态成员和单例模式完全失效。
(2)线程同步机制完全失效。
(3)SharedPreferences的可靠性下降。
(4)Application会多次创建。
跨进程通信的方式有多种,如Bundle、AIDL、文件共享、Messenger、ContentProvider和Socket等,今天主要介绍的AIDL的使用。

一、项目代码文件结构

这里以书店为例讲解一下,这里实现的功能是书店(服务端)把自家拥有哪些书籍告知客户(客户端),而且客户(客户端)还进行了消息订阅,当书店(服务端)有新书了就通知客户(客户端)。
Android IPC(跨进程通信)之AIDL_第1张图片

二、代码实现与讲解

1、新建实体类Book.java,使用Pracelable实现序列化。

package com.fenght.aidldemo.aidl;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {    private int bookId;    private String bookName;    public Book(int bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    private Book(Parcel in) {        bookId = in.readInt();        bookName = in.readString();    }    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeInt(bookId);        dest.writeString(bookName);    }    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];        }    };    @Override    public String toString() {        return "Book{" +                "bookId=" + bookId +                ", bookName='" + bookName + '\'' +                '}';    }}

2、右击Book.java,新建Book.aidl和IBookManager.aidl以及NewBookArriveListener.aidl文件,AS会自动帮你把文件路径建好,不需要再新建文件夹。
Android IPC(跨进程通信)之AIDL_第2张图片
Book.aidl文件代码

// Book.aidlpackage com.fenght.aidldemo.aidl;// Declare any non-default types here with import statementsparcelable Book;

IBookManager.aidl文件代码

// IBookManager.aidlpackage com.fenght.aidldemo.aidl;import com.fenght.aidldemo.aidl.Book;import com.fenght.aidldemo.aidl.NewBookArriveListener;// Declare any non-default types here with import statementsinterface IBookManager {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);    List<Book> getBookList();    void addBook(in Book book);    void registerListener(NewBookArriveListener listener);    void unregisterListener(NewBookArriveListener listener);}

NewBookArriveListener.aidl文件代码

// NewBookArriveListener.aidlpackage com.fenght.aidldemo.aidl;import com.fenght.aidldemo.aidl.Book;// Declare any non-default types here with import statementsinterface NewBookArriveListener {    //通知方法    void newBookArrived(in Book newBook);}

添加代码之后,点击Make Project重新编译项目。

注意:包名必须是一样com.fenght.aidldemo.aidl,不然后续编译会报错,如下图。
Android IPC(跨进程通信)之AIDL_第3张图片
在aidl文件中写相关方法时AS没有自动帮你引入相关类,你需要自己引入。如在IBookManager.aidl文件中添加方法List getBookList(); 你可能需要手动引入import com.fenght.aidldemo.aidl.Book; Book.java的类。否则会报错:Failed to resolve ‘Book’
而且每次在aidl文件中添加相关代码之后需要重新编译一下项目。
Android IPC(跨进程通信)之AIDL_第4张图片
3、远程服务端service的实现,新建BookManagerService.java服务。

package com.fenght.aidldemo.aidl;import android.app.Service;import android.content.Intent;import android.content.pm.PackageManager;import android.os.Binder;import android.os.IBinder;import android.os.RemoteCallbackList;import android.os.RemoteException;import android.util.Log;import java.util.List;import java.util.concurrent.CopyOnWriteArrayList;import java.util.concurrent.atomic.AtomicBoolean;import androidx.annotation.Nullable;/** * 数据管理服务 * @author fht * @time 2020年8月1日14:02:38 */public class BookManagerService extends Service {    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();    private AtomicBoolean isDestory = new AtomicBoolean(false);    //使用RemoteCallbackList可以对监听进行反注册,否则反注册会失败    private RemoteCallbackList<NewBookArriveListener> listeners = new RemoteCallbackList<>();    private Binder mBinder = new IBookManager.Stub() {        @Override        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {        }        @Override        public List<Book> getBookList() throws RemoteException {            return mBookList;        }        @Override        public void addBook(Book book) throws RemoteException {            mBookList.add(book);        }        @Override        public void registerListener(NewBookArriveListener listener) throws RemoteException {            //注册监听            listeners.register(listener);        }        @Override        public void unregisterListener(NewBookArriveListener listener) throws RemoteException {            //反注册            listeners.unregister(listener);        }    };    @Override    public void onCreate() {        super.onCreate();        mBookList.add(new Book(1,"android"));        mBookList.add(new Book(2,"java"));        //启动线程        new Thread(new ServiceWorker()).start();    }    @Nullable    @Override    public IBinder onBind(Intent intent) {        int check = checkCallingOrSelfPermission("com.fenght.aidldemo.aidl.BOOK_SERVICE");        if (check == PackageManager.PERMISSION_DENIED) {            return null;        }        return mBinder;    }    @Override    public void onDestroy() {        isDestory.set(true);        super.onDestroy();    }    private class ServiceWorker implements Runnable{        @Override        public void run() {            while (!isDestory.get()){                try {                    Thread.sleep(100);                    int bookId = mBookList.size() + 1;                    Book newBook = new Book(bookId,"新书" + bookId);                    mBookList.add(newBook);                    Log.e("fht","服务中添加新书:" + newBook.toString());                    final int size = listeners.beginBroadcast();                    for (int i=0;i<size;i++) {                        //获取监听                        NewBookArriveListener newBookArriveListener = listeners.getBroadcastItem(i);                        if (newBookArriveListener != null) {                            //发送通知                            newBookArriveListener.newBookArrived(newBook);                        }                    }                    //beginBroadcast和finishBroadcast必须配对使用                    listeners.finishBroadcast();                    //中断重连测试//                    if (bookId == 9) {//                        //结束当前进程,测试Binder死亡回调//                        android.os.Process.killProcess(android.os.Process.myPid());//                        return;//                    }                } catch (InterruptedException | RemoteException e) {                    e.printStackTrace();                }            }        }    }}

注意:在AndroidMainfest.xml中添加如下代码,开启多进程:

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

4、接着在Mainactivity.java中绑定服务,接收数据。

package com.fenght.aidldemo;import androidx.annotation.NonNull;import androidx.appcompat.app.AppCompatActivity;import android.content.ComponentName;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.util.Log;import android.widget.TextView;import com.fenght.aidldemo.aidl.Book;import com.fenght.aidldemo.aidl.BookManagerService;import com.fenght.aidldemo.aidl.IBookManager;import com.fenght.aidldemo.aidl.NewBookArriveListener;import java.util.List;public class MainActivity extends AppCompatActivity {    private TextView tv_book;    private IBookManager iBookManager;    private Handler handler = new Handler(new Handler.Callback() {        @Override        public boolean handleMessage(@NonNull Message msg) {            switch (msg.what){                case 1:                    Log.e("fht","新书:" + msg.obj.toString());                    tv_book.setText(msg.obj.toString());                    break;            }            return false;        }    });    //服务连接    private ServiceConnection serviceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            iBookManager = IBookManager.Stub.asInterface(service);            try {                //设置binder死亡代理,binder死亡时有回调                service.linkToDeath(deathRecipient,0);                //获取数据                List<Book> list = iBookManager.getBookList();                Log.e("fht","书本:" + list.toString());                iBookManager.addBook(new Book(3,"这是客户端发送的书"));                List<Book> list1 = iBookManager.getBookList();                Log.e("fht","书本:" + list1.toString());                iBookManager.registerListener(newBookArriveListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    //回调方法:当binder死亡时,系统会回调binderDied方法    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {        @Override        public void binderDied() {            if (iBookManager == null) {                return;            }            //先解除binder旧的死亡监听,在ServiceConnection中会重新新的设置监听            iBookManager.asBinder().unlinkToDeath(deathRecipient,0);            iBookManager = null;            //死亡时,重新启动连接            Intent intent = new Intent(MainActivity.this, BookManagerService.class);            bindService(intent,serviceConnection,BIND_AUTO_CREATE);        }    };    //新书监听    private NewBookArriveListener newBookArriveListener = new NewBookArriveListener.Stub() {        @Override        public void newBookArrived(Book newBook) throws RemoteException {            //发送消息,由UI线程处理数据            handler.obtainMessage(1,newBook).sendToTarget();        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv_book = findViewById(R.id.tv_book);        Intent intent = new Intent(this, BookManagerService.class);        bindService(intent,serviceConnection,BIND_AUTO_CREATE);    }    @Override    protected void onDestroy() {        if (iBookManager != null && iBookManager.asBinder().isBinderAlive()) {            try {                //反注册监听                iBookManager.unregisterListener(newBookArriveListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        unbindService(serviceConnection);        super.onDestroy();    }}

AndroidManifest.xml代码,注意跟自己的比对一下

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.fenght.aidldemo">    <permission android:name="com.fenght.aidldemo.aidl.BOOK_SERVICE"        android:protectionLevel="normal"/>    <uses-permission android:name="com.fenght.aidldemo.aidl.BOOK_SERVICE"/>    <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>

项目跑起来就可以了

三、注意点以及相关代码讲解

1、如何监听Binder是否死亡?
有时候服务端进程由于某些意外停止了,这回导致Binder的意外死亡,这时候需要我们重新连接服务。我们如何Binder是否死亡呢?给Binder设置DeathRecipient监听,当Binder死亡时,我们会收到binderDied方法的回调。相关代码在MainActivity.java。
(1)设置代理的代码为:

//设置binder死亡代理,binder死亡时有回调service.linkToDeath(deathRecipient,0);

(2)回调方法代码为:

//回调方法:当binder死亡时,系统会回调binderDied方法private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {    @Override    public void binderDied() {        if (iBookManager == null) {            return;        }        //先解除binder旧的死亡监听,在ServiceConnection中会重新新的设置监听        iBookManager.asBinder().unlinkToDeath(deathRecipient,0);        iBookManager = null;        //死亡时,重新启动连接        Intent intent = new Intent(MainActivity.this, BookManagerService.class);        bindService(intent,serviceConnection,BIND_AUTO_CREATE);    }};

(3)验证方法就是放开如下代码:
Android IPC(跨进程通信)之AIDL_第5张图片
2、如何进行权限验证?
默认情况下,我们的远程服务任何人都可以连接的,但这是不安全的,我们需要加入权限验证以保护数据的安全性。验证通过才可以连接,验证失败则不能连接。相关代码如下:
(1)在AndroidManifest.xml添加权限,这是连接服务需要的权限,自己定义的:

<permission android:name="com.fenght.aidldemo.aidl.BOOK_SERVICE"    android:protectionLevel="normal"/>

(2)在BookManagerService.java中进行权限控制:

@Nullable@Overridepublic IBinder onBind(Intent intent) {    int check = checkCallingOrSelfPermission("com.fenght.aidldemo.aidl.BOOK_SERVICE");    if (check == PackageManager.PERMISSION_DENIED) {        return null;    }    return mBinder;}

(3)在AndroidManifest.xml声明权限,跟(1)中的权限要一致:

<uses-permission android:name="com.fenght.aidldemo.aidl.BOOK_SERVICE"/>

3、因为反注册的需要,这里使用RemoteCallbackList,不需要注册与反注册监听的可以使用CopyOnWriteArrayList,具体原因不多说了。
Android IPC(跨进程通信)之AIDL_第6张图片

Demo代码下载

更多相关文章

  1. Android的进程回收
  2. Android 学习日记(二)android studio运行github代码
  3. Android Apk反编译得到Java源代码
  4. Android 通过java代码实现EditText输入限制
  5. 傻瓜式建立数据库,高效数据库操作代码的编写--android
  6. 15个开发者最亲睐的Android代码编辑器
  7. Android源代码调试环境搭建

随机推荐

  1. Android HAL 介绍
  2. Android Looper类摘记
  3. android中context及全局变量小析 .
  4. Android 学习记录-网络请求
  5. A​n​d​r​o​i​d​体​系​结​构
  6. Android开发几个必去的网站
  7. android 5.1 添加自定义的SystemServer服
  8. Android的几个特点简述
  9. Android中直播视频技术探究之---视频直播
  10. Android studio模拟器 Intel HAXM Intel