Android中的多进程
为什么要使用多进程
使用多进程可以避免65535的方法限制,而且由于Android系统对于每个单独的App应用都有内存大小的限制,所以可以通过一个App的多进程方式来增加可用的内存。
开启多进程
在Android中开启多进程很简单,但是当多进程开启之后需要考虑的东西有很多,包括进程间的通讯,进程的创建和销毁的使用场景,以及开启多进程之后各种不可预期的错误
<!--主进程-->
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!--开启多进程,通过process属性来指定进程名-->
<activity
android:name=".Main2Activity"
android:process=":remote">
</activity>
开启多进程的弊端
- 使用多进程之后首先单例模式和静态成员完全失效
- 线程同步机制失效
- sharedPreference可靠性下降,因为对XML文件的并发读写会出问题
- Application会多次创建
Android中的多进程通讯方式
Android中提供了多种的进程间通讯方式:
- 使用Bundle
- 使用共享文件
- 使用Messenger
- 使用ContentProvider
- 使用Socket
- 使用Binder(AIDL)
使用Bundle
由于Bundle实现了Parcelable接口,所以可以在不同的进程之间传递一些可以包裹化的数据,但是不存在交互性,也就是说,只能在启动另一个进程时将需要传入的数据传过去,只能单向一次传输。
使用文件共享
两个文件可以通过同一个对共享文件的读写来进行数据的交换,这样可以实现交互性,但是由于Android中多个进程间无法对文件的读写进行同步,这样就会导致这种跨进程通讯方式会引发读写错误。
使用Messenger
Messenger是Android实现的一种轻量级的IPC机制,他可以传送一些轻量级的数据Message对象,实际的底层实现是用的Binder,只是对AIDL进行了一些封装,使得更便于使用,而且是串行处理Message对象的,所以不存在并发的问题。
服务端代码
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
//声明服务端的Messenger
private final Messenger mMessenger = new Messenger(new MessageHandler());
/**
* 构造Service端用于处理消息的Handler
*/
private static class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Contast.MSG_FROM_CLIENT:
Log.i(TAG, "handleMessage: "+msg.getData().getString(Contast.MSG_CONTENT));
break;
default:
super.handleMessage(msg);
}
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//将其中的Binder返回
return mMessenger.getBinder();
}
}
客户端代码
public class Main2Activity extends AppCompatActivity {
private Messenger mMessenger;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
//构造Message用于发送
Message message = Message.obtain(null, Contast.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString(Contast.MSG_CONTENT,"这是客户端发来的消息");
message.setData(data);
try {
mMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMessenger = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
public void serviceStart(View view){
//启动服务端Service
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
//解除绑定
unbindService(mServiceConnection);
super.onDestroy();
}
}
使用ContentProvider
ContentProvider原本就适合用于进程间通讯,它的底层实现同样是Binder机制。ContentProvider主要定义了一些提供给外部应用访问本应用的一些借口,通过ContentProvider来定义访问接口,通过ContentResolver来进行数据的访问
自定义实现ContentProvider
/**
* 自定义的ContentProvider,实现其中的增删改查方法
*/
public class PipContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
注册自定义的ContentProvider
<provider
<!--声明要访问该ContentProvider所需要的权限-->
android:permission="com.wei.piechart.PROVIDER"
<!--ContentProvider的唯一标识-->
android:authorities="com.wei.piechart.PipContentProvider"
<!--声明要注册的ContentProvider-->
android:name=".PipContentProvider"
android:process=":provider"/>
在程序中通过ContentResolver和Uri来访问指定的ContentProvider
//获取ContentResolver
ContentResolver contentResolver = getContentResolver();
//通过Uri来指定要访问的对象
Uri uri = Uri.parse("content:com.wei.piechart.PipContentProvider");
//类似于SQL数据库一样,就可以进行查询操作,结果放在Cursor结果集中
Cursor cursor = contentResolver.query(uri, null, null, null, null);
使用Socket
Socket套接字本是网络通讯中的一种方式,但是也可以用于本机中的两个进程,通过使用端口号来进行。
Socket分为ServiceSocket和Socket,一个用于服务端,一个用于客户端,和蓝牙通过Socket进行交互的原理是一样的。
使用Binder(AIDL)
准确来说,AIDL并不是一种IPC机制,而是一种语言AIDL(Android Interface Definition Language)Android接口定义语言,通过这种语言我们可以很方便的创建出通过Binder通讯的IPC模型。其实Binder才是Android最为主流的进程间通讯方式,也是前边一些机制的底层实现原理,而AIDL只是使我们有一个便捷的方式来实现Binder机制。
使用限制
类型限制
首先AIDL文件中并不是所有的数据类型都可以使用,可以在AIDL文件中使用的有以下类型:
- 基本数据类型(int long….)
- String和CharSequence
- List:只支持ArrayList,而且List中的元素必须能够被AIDL支持
- Map:只支持HashMap,而且Map中的元素必须能够被AIDL支持
- Parcelable:所有实现了Parcelable接口的对象
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
类型声明限制
除了基本类型之外,所有的类型都需要标明方向,使用in表示输入类型参数,使用out表示输出类型参数,使用inout表示输入输出类型参数
对于用到的自定义的Parcelable类型和AIDL类型,都需要显式的import进来
流程
定义AIDL接口
Book.aidl
// Book.aidl
package com.wei.piechart;
// Declare any non-default types here with import statements
parcelable Book;
IBookManager.aidl
// IBookManager.aidl
package com.wei.piechart;
// Declare any non-default types here with import statements
import com.wei.piechart.Book;
interface 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);
}
Book.java
public class Book implements Parcelable{
int id;
String name;
protected Book(Parcel in) {
id = in.readInt();
name = in.readString();
}
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;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(id);
dest.writeString(name);
}
}
生成IBookManager.java
在定义好接口之后重新Build一下项目,AS会自动生成IBookManager.java这个类。这是AIDL的最主要的功能,通过在aidl文件中定义所需要的字段和方法,可以快速的生成实现了Binder的类。
实现服务端
public class BookManagerService extends Service{
private static final String TAG = "BookManagerService";
//在Service端的书籍列表,支持多进程
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
//用于返回给客户端的Binder
private Binder mBinder = new IBookManager.Stub(){
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
if(!mBookList.contains(book)){
mBookList.add(book);
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
//将服务端创建的Binder返回给客户端
return mBinder;
}
}
实现客户端
//声明ServiceConnection
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//在客户端将服务端返回的Binder转化为接口使用
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
bookManager.addBook(new Book(1, "第一本书"));
List<Book> books = bookManager.getBookList();
Log.i(TAG, "onServiceConnected: "+books.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
public void serviceStart(View view) {
//启动服务端Service
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}