IPC简介
IPC 是Inter-Process Communication 的缩写,含义是进程间通讯,是指两个进程之间进行数据交换的的过程,在讲AIDL之前我们先讲一下Android线程与进程的概念:线程是CPU调度的最小单元,每个线程中可以独立完成自己的任务,进程是一个执行单元,在android中一般情况下是指一个应用。一个进程可以包含多个线程,最简单大的情况下,一个进程中可以只有一个线程,即UI线程。
Android为什么要采用多进程
- 更大的内存分配,我们知道,Android设备限制了每一个进程所分配的内存大小, 如果这个大小是32M,那么如果我们的应用有两个进程,那么所能使用的最大内存就是 32*2=64M。所以如果用户经常收到OutOfMemory异常,那么就应该考虑使用多进程。
- 分担主进程的内存压力。现在的应用越来越大,我们可以将一些其他的工作放在额外的进程中运行,来降低主进程的压力。
- 进程间拉起,比如应用来两个进程,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/