您的位置:首页 > 移动开发 > Android开发

《Android开发艺术探索》--Android中的IPC机制

2017-02-05 16:40 218 查看

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机制。

关于Android中的Binder机制可以阅读这篇文章AndroidBinder机制详解

使用限制

类型限制

首先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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android ipc