《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 通讯机制源码分析
- Android IPC 通讯机制源码分析
- Android IPC 通讯机制源码分析 (二)
- [转Android进程间通信--消息机制及IPC机制实现 ]
- Android进程间通信--消息机制及IPC机制实现 推荐
- Android IPC 通讯机制源码分析
- Android的IPC机制Binder的各个部分
- Android IPC 通讯机制源码分析 (二)
- 转帖:Android的IPC机制Binder的各个部分
- Android IPC 通讯机制源码分析2(转)
- Android IPC 通讯机制源码分析(1)
- Android IPC 通讯机制源码分析2(转)
- Android IPC机制详解
- Android IPC 通讯机制源码分析 (二)
- Android IPC 通讯机制源码分析
- Android IPC机制详解
- Android IPC机制 详解
- Android IPC 通讯机制源码分析
- Android的IPC机制Binder的各个部分
- Android IPC 通讯机制源码分析 (二)