Fork me on GitHub

浅析Android进程间通信(一)

简单介绍使用AIDL来实现进程间通信。

序列化接口Parcelable

为什么会有序列化这么个东西?

简单说就是为了保存在内存中的各种对象的状态,并且可以把保存的对象状态再读出来。
虽然你可以用你自己的各种各样的方法来保存,但是Java已经提供一个完善的接口来进行序列化的工作,那就是Serializable。

序列化的使用场景

  1. 永久性保存对象,保存对象的字节序列到本地文件中;
  2. 通过序列化对象在网络中传递对象;
  3. 通过序列化在进程间传递对象。

Parcelable又是什么?

Parcelable是Android特有功能,效率比实现Serializable接口高效,可用于Intent数据传递,也可以用于进程间通信(IPC)

Parcelable和Serializable如何选择?

  1. 在使用内存的时候,Parcelable比Serializable性能高,所以推荐使用Parcelable。
  2. Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC。
  3. Parcelable不能使用在要将数据存储在磁盘上的情况,因为Parcelable不能很好的保证数据的持续性在外界有变化的情况下。尽管Serializable效率低点,但此时还是建议使用Serializable 。

注意:writeToParcel和参数为Parcel的构造方法,里面的读写顺序一定要一致

Android 进程间通信(IPC)

假设要做一个音乐播放的App,除去前台UI展示,还要能够后台播放,实现方案分析可以查看这篇博文,讲的很深入了。
我们就采用多进程的方式来实现这个需求吧。

项目结构


Project视图下可能看的更清楚,在AS中new直接选择AIDL-AIDL File即可自动生成。

  • MediaService就是跑在另一个进程中的后台服务,负责播放音乐文件。
  • MusicTrack是bean类。
  • 要想在AIDL使用到bean类,需要生命与java文件对应的同名aidl文件,即 MusicTrack.aidl
  • MusicAIDLService.aidl 就是远程服务接口声明。

注意同名的java文件和aild文件的包名需要一样。

代码

在写完AIDL和bean类之后,编译一下工程,AS会自动生成后续我们需要使用的代理类 MusicAIDLService.Stub,这个类是AS根据AIDL文件,自动创建的,在
app/build/generated/source/aidl/debug/目录下, MusicAIDLService.Stub就是一个Binder的子类,后续就跟普通的Service的使用没有区别了。

1
2
3
4
5
6
7
8
9
10
11
12
//MusicAIDLService.aidl 声明远程进程方法 在Service重载
package com.dongua.ipc.service;
import com.dongua.ipc.service.MusicTrack;

interface MusicAIDLService{
void play();
void pause();
void stop();
void prev();
void next();
MusicTrack getCurrentTrack();
}

1
2
3
//MusicTrack.aidl 
package com.dongua.ipc.service;
parcelable MusicTrack;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//MusicTrack.java 用来多进程之间传输的数据需要实现Parcelable接口
public class MusicTrack implements Parcelable {

public String mTitle;
public String mAlbum;
public String mArtist;

public MusicTrack(Parcel in) {
mTitle = in.readString();
mArtist = in.readString();
mAlbum = in.readString();
}

public static final Creator<MusicTrack> CREATOR = new Creator<MusicTrack>() {
@Override
public MusicTrack createFromParcel(Parcel in) {
return new MusicTrack(in);
}

@Override
public MusicTrack[] newArray(int size) {
return new MusicTrack[size];
}
};

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mTitle);
dest.writeString(mAlbum);
dest.writeString(mArtist);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class MediaService extends Service {

private static final String TAG = "MediaService";

private Binder mBinder = new MusicAIDLService.Stub() {
@Override
public void play() throws RemoteException {
Log.i(TAG, "play: ");
}

@Override
public void pause() throws RemoteException {
Log.i(TAG, "pause: ");

}

@Override
public void stop() throws RemoteException {
Log.i(TAG, "stop: ");
}

@Override
public void prev() throws RemoteException {
Log.i(TAG, "prev: ");
}

@Override
public void next() throws RemoteException {
Log.i(TAG, "next: ");
}

@Override
public MusicTrack getCurrentTrack() throws RemoteException {
Log.i(TAG, "getCurrentTrack: ");
return null;
}
};




@Override
public void onCreate() {
Log.i(TAG, "onCreate: ");
super.onCreate();
}

@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: ");
super.onDestroy();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: ");
return mBinder;
}


}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//MainActivity.java
private MusicAIDLService mMusicService ;
private ServiceConnection mConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMusicService = (MusicAIDLService) MusicAIDLService.Stub.asInterface(service);
try {
mMusicService.play();
mMusicService.pause();
mMusicService.stop();
} catch (RemoteException e) {
e.printStackTrace();
}

}

@Override
public void onServiceDisconnected(ComponentName name) {

}
};

输出

1
2
3
4
5
6
10-16 20:58:48.257 22211-22211/com.dongua.ipc:music I/MediaService: onCreate: 
10-16 20:58:48.257 22211-22211/com.dongua.ipc:music I/MediaService: onBind:
10-16 20:58:48.261 22211-22268/com.dongua.ipc:music I/MediaService: play:
10-16 20:58:48.261 22211-22223/com.dongua.ipc:music I/MediaService: pause:
10-16 20:58:48.262 22211-22225/com.dongua.ipc:music I/MediaService: stop:
10-16 20:58:51.633 22211-22211/com.dongua.ipc:music I/MediaService: onDestroy:

如果你只是想要了解如何使用AIDL来完成进程间的通信,实现功能即可,对底层的细节不感兴趣,那么后续的直接套上面的模板来,或者使用基于AIDL封装的更加完善的Messenger(注意与Message区别)来完成相应的需求即可。

参考

深入分析Java的序列化与反序列化
序列化原理机制浅谈