Android IPC技术之AIDL

IPC简介

IPC 是Inter-Process Communication 的缩写,含义是进程间通讯,是指两个进程之间进行数据交换的的过程,在讲AIDL之前我们先讲一下Android线程与进程的概念:线程是CPU调度的最小单元,每个线程中可以独立完成自己的任务,进程是一个执行单元,在android中一般情况下是指一个应用。一个进程可以包含多个线程,最简单大的情况下,一个进程中可以只有一个线程,即UI线程。

Android为什么要采用多进程

  1. 更大的内存分配,我们知道,Android设备限制了每一个进程所分配的内存大小, 如果这个大小是32M,那么如果我们的应用有两个进程,那么所能使用的最大内存就是 32*2=64M。所以如果用户经常收到OutOfMemory异常,那么就应该考虑使用多进程。
  2. 分担主进程的内存压力。现在的应用越来越大,我们可以将一些其他的工作放在额外的进程中运行,来降低主进程的压力。
  3. 进程间拉起,比如应用来两个进程,main、push,当push进程挂掉的话我们可以通过main进程来唤起push进程,反之亦然。

AIDL实现过程

言归正传,接下来我们介绍一下,android中怎么使用AIDL技术来达到IPC过程,流程如下:
1.创建实现Parcelable接口的类对象(Book)以此为介质在进程间通讯。
2.在AIDL文件中声明该类(Book)。
3.AIDL接口创建,声明要暴露的方法。
4.重新编译,ide自动生成Stub类
5.编写service。
6 编写client与service通讯
下面结合代码来介绍每个步骤:
(1)创建实现Parcelable接口的类对象(Book)以 此为介质在进程间通讯

class Book implements Parcelable {
private String name;
private int price;
public int getPrice() {
    return price;
}
public void setPrice(int price) {
    this.price = price;
}
public Book(){}
public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}
protected Book(Parcel in) {
   name = in.readString();
   price = in.readInt();
}

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;
}
public void readFromParcel(Parcel dest) {
    name = dest.readString();
    price = dest.readInt();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(name);
    dest.writeInt(price);
}

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

}
注意:在这里有个坑android中并不能识别AIDL下的类文件我们必须在gradle中声明加入以下代码:

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

(2) 在AIDL文件中声明该类(Book)

  package com.example.administrator.ipc_aldl;
  parcelable Book;

(3) AIDL接口创建,声明要暴露的方法。

interface IMyAidlInterface {
   /**
    * Demonstrates some basic types that you can use as parameters
    * and return values in AIDL.
    */
   //所有的返回值前都不需要加任何东西,不管是什么数据类型
   List<Book> getBooks();
   Book getBook();
   int getBookCount();

   //传参时除了Java基本类型以及String,CharSequence之外的类型
   //都需要在前面加上定向tag,具体加什么量需而定
   void setBookPrice(in Book book , int price);
   void setBookName(in Book book , String name);
   void addBookIn(in Book book);
   void addBookOut(out Book book);
   void addBookInout(inout Book book);

(4)做好以上工作我们在android studdio 中clean project 自动生成stub类,这里内容很多,笔者就不贴代码了,当然我们可以自己编写stub类,但是没有人这么做。

(5)后我们编写服务端代码, 通常就是创建一个Service, 然后在Service中实现我们在aidl中定义的接口方法。这样客户端调用接口后, 服务端就执行对应方法, 然后返回数据(如果有返回值), 下面是服务端代码:

public class BookService extends Service {
    public final String TAG = "BookService";

    List<Book> mBooks = new ArrayList<Book>(){};
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG,"远程连接完成");
        Book Book = new Book();
        Book.setName("android开发IPC");
        Book.setPrice(100);
        mBooks.add(Book);
    }

    IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub()     {
        @Override
        public List<Book> getBooks() throws RemoteException {
            synchronized (this) {
                Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString());
                for (Book book : mBooks) {
                    Log.e(TAG, book.toString());
                }
                if (mBooks != null) {
                    return mBooks;
                }
                return new ArrayList<>();
            }
        }

        @Override
        public Book getBook() throws RemoteException {
            return null;
        }

        @Override
        public int getBookCount() throws RemoteException {
            return mBooks.size();
        }

        @Override
        public void setBookPrice(Book book, int price) throws RemoteException {
            book.setPrice(price);
        }

        @Override
        public void setBookName(Book book, String name) throws RemoteException {
            book.setName(name);
        }

        @Override
        public void addBookIn(Book book) throws RemoteException {
            synchronized (this) {
                if (mBooks == null) {
                    mBooks = new ArrayList<>();
                }
                if (book == null) {
                    Log.e(TAG, "Book is null in In");
                    book = new Book();
                }
                //尝试修改book的参数,主要是为了观察其到客户端的反馈
                book.setPrice(999999);
                if (!mBooks.contains(book)) {
                    mBooks.add(book);
                }
                //打印mBooks列表,观察客户端传过来的值
                Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());
            }

        }

        @Override
        public void addBookOut(Book book) throws RemoteException {

        }

        @Override
        public void addBookInout(Book book) throws RemoteException {

        }


    };

然后需要在AndroidManifest.xml文件里面注册我们的Service,四大组件大家应该都不陌生吧.

   <service android:name=".BookService" android:exported="true" android:process=":remote">
            <intent-filter>
                <action android:name="com.example.administrator.ipc_aldl"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

(6)客户端我们要完成的工作主要是调用服务端的方法,但是在那之前,我们首先要绑定上服务端,完整的客户端代码是这样的:

public class MainActivity extends Activity {
    private boolean mBound = false;
    //暴露出来的是IMyAiDlTnterface;
    private IMyAidlInterface mBinder;
    private final String TAG = "MainActivity";
    ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(TAG,"客户端连接服务器完成");
            mBinder = IMyAidlInterface.Stub.asInterface(service);
            mBound = true;
            if (mBinder !=null){
                try {
                    List<Book> books = mBinder.getBooks();
                    for (Book book: books) {
                        Log.e(TAG,"客户端"+book.toString());
                    }
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }else {
                Log.e(TAG,"binder is null");
            }
          }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG,"service is not connect");
            mBound = false;
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_t);
        init();
    }

    private void init() {
        TextView btn_aa = findViewById(R.id.btn_aa);
        btn_aa.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!mBound){
                    attemptToBindService();
                    Toast.makeText(getApplicationContext(),"未与远程服务器建立连接请从新连接!",Toast.LENGTH_LONG).show();
                }
                if (mBinder !=null){
                    Book book = new Book();
                    book.setName("人生哲学");
                    book.setPrice(1000);
                    try {
                        mBinder.addBookIn(book);
                      Log.e(TAG,"书的数量"+mBinder.getBookCount());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }


            }
        });
    }

    private void  attemptToBindService() {
        Intent intent = new Intent();
        intent.setAction("com.example.administrator.ipc_aldl");
        intent.setPackage("com.example.administrator.ipc_aldl");
        bindService(intent,mConnection,BIND_AUTO_CREATE);
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!mBound){
            attemptToBindService();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound){
            unbindService(mConnection);
            mBound =false;
        }
    }

通讯日志

//服务端的 log 信息,
on bind,intent = Intent { act=com.example.my_chapter_2 pkg=com.example.my_chapter_2.service }
invoking addBooks() method , now the list is : [name : android开发IPC , price : 100,name : 人生哲学 , price : 999999]
//客户端的 log 信息
客户端连接服务器完成,
[name : android开发IPC, price : 100,name : 人生哲学 , price : 999999]
书数量:2
--------------------- 

结语

以上是利用AIDL实现进程通讯的基本方法,希望对大家有所帮助。关于AIDL的核心原理以及Binder,AIDL优化,会在下一篇文章详细讲述。
附上源码地址:https://github.com/LiuShouChun/IPC-AIDL/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值