layout: post
date: 2015-12-28
title: Android开发艺术探索-第二章-IPC机制
categories: blog
tags: [Activity,Android]
category: Android
description:
本文首发于KuTear,转载请注明
2.1 Android IPC
Android常用进程间通信
- Binder
- Socket
- ContentProvider
- 文件共享(SharePreference/普通文件/数据库) 对同步要求不是很高才使用,不建议使用SharePreference.
2.2 Android多进程
-
开启方式
在AndroidMenifest中为四大组件指定android:process=""属性
默认进程名字为包名,指定 android:process=":##" 的进程名为 包名:##,该进程为当前App的私有进程,其他应用的组件不可以和他跑在同一进程中.其他类型的进程可以通过ShareUID共享(签名相同) -
多进程的运行
在不同进程中的四大组件,不能通过内存来共享数据.静态字段在不同的进程有不同的副本,且互不影响.不同进程含有不同的Application引用(开启进程会为该进程创建Application).
2.3 IPC基础介绍
-
序列化
-
Serializable
静态成员变量属于类而不属于对象,不会参与序列化过程,其次使用transient(短暂)标志的成员变量也不会参与序列化过程.
序列化到本地
User user = new User(1, "hello world", false); File cachedFile = new File(MyConstants.CACHE_FILE_PATH); ObjectOutputStream objectOutputStream = null; try { objectOutputStream = new ObjectOutputStream( new FileOutputStream(cachedFile)); objectOutputStream.writeObject(user); } catch (IOException e) { e.printStackTrace(); } finally { //数据流关闭 }
从本地恢复序列化
User user = null; File cachedFile = new File(MyConstants.CACHE_FILE_PATH); if (cachedFile.exists()) { ObjectInputStream objectInputStream = null; try { objectInputStream = new ObjectInputStream( new FileInputStream(cachedFile)); user = (User) objectInputStream.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { //数据流关闭 } }
-
Parcelable
在Android Studio中可以利用插件自动生成.注意boolean转化为int/byte存储
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(this.id); dest.writeByte(isFirst ? (byte) 1 : (byte) 0); } protected Demo(Parcel in) { this.id = in.readInt(); this.isFirst = in.readByte() != 0; }
相比Serializable,Parcelable的性能更好,Serializable需要大量的I/O操作
Binder
-
2.4 Android中的IPC
-
Bundle
在Service,Activity,Receiver中使用Intent传递Bundle对象,在日常开发的过程中,对同一线程的通信也使用的比较多.前面说过,支持基本的类型和实现序列化的类
-
文件共享
在A进程向文件xxx写入,在B进程从文件xxx读出内容,需要注意的就是同步问题,前面说过不要使用SharePreference.
-
Messenger
Messenger实质上还是AIDL,不过使用变得相当的简单.
下面是一个栗子:
创建一个Service(MessengerService.java),并指定进程,其主要代码为://接收客户端的数据并做出反馈 private static class MessengerHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.v(TAG, msg.what + ""); Messenger reply = msg.replyTo; Message replymsg = Message.obtain(); replymsg.what = msg.what * 2; try { reply.send(replymsg); } catch (RemoteException e) { e.printStackTrace(); } } } private Messenger messenger = new Messenger(new MessengerHandler()); @Override public IBinder onBind(Intent intent) { return messenger.getBinder(); }
在Activity中链接该服务
//处理服务端返回的数据 private static class MessagerHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.v(TAG, msg.what + ""); } } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Messenger messenger = new Messenger(service); Message msg = Message.obtain(); msg.what = 1024; //指定接收服务端返回的数据的处理者 msg.replyTo = new Messenger(new MessagerHandler()); try { //向服务端发送数据 messenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_messager); Intent intent = new Intent(this, MessengerService.class); bindService(intent, connection, BIND_AUTO_CREATE); }
Messenger串行处理客户端的消息,Message的object不能在跨进程中来传递非系统自定义的Parcelable数据,只能在Bundle中传递.
-
AIDL
注意点:需要在AIDl中使用的已经序列化好了的对象必须编写对应的aidl文件声明(Book.java-->Book.aidl)
// Book.aidl package com.kutear.studydemo.android.art.aidl; // Declare any non-default types here with import statements parcelable Book;
aidl中使用到的类必须按全路径导入.即使在同包下
// IBookManager.aidl package com.kutear.studydemo.android.art.aidl; //同路径任导入 import com.kutear.studydemo.android.art.aidl.Book; import com.kutear.studydemo.android.art.aidl.IListener; // Declare any non-default types here with import statements interface IBookManager { void addBook(in Book book); List
getBookList(); void registerListener(IListener listener); void unregisterListener(IListener listener); } 创建Service(服务端)
public class AidlService extends Service { private static final String TAG = "AidlService"; private CopyOnWriteArrayList
books = new CopyOnWriteArrayList<>(); private ArrayList mListeners = new ArrayList<>(); private Binder mBinder = new IBookManager.Stub() { @Override public void addBook(Book book) throws RemoteException { books.add(book); displayListener(book); } @Override public List getBookList() throws RemoteException { return books; } @Override public void registerListener(IListener listener) throws RemoteException { //listener-->com.kutear.studydemo.android.art.aidl.IListener$Stub$Proxy@41895598 //同加进来的对象类型都变了 if (listener != null) { Log.v(TAG, listener.toString()); mListeners.add(listener); } } @Override public void unregisterListener(IListener listener) throws RemoteException { if (listener != null) { mListeners.remove(listener); } } }; public AidlService() { } //观察者模式分发 private void displayListener(Book book) { for (IListener listener : mListeners) { try { listener.newBookArrived(book); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { super.onCreate(); books.add(new Book(1, "HTTP")); books.add(new Book(2, "TCP")); } } 在Activity中进行绑定(客户端)
//注意这里不能直接new IListener(),因为跨进程过程中对象的还原是基于序列化 //,也就是说还原后的对象已经不是原本的对象 //在AidlService#mBinder#registerListener(IListener listener)中的 //listener实际是不等于直接创建的mListener的 //导致在分发的时候本地无法回调 private IListener mListener = new IListener.Stub() { @Override public void newBookArrived(Book book) throws RemoteException { Log.v(TAG,"NewBook"+book.toString()); //运行在客户端的Binder线程池中,非UI线程,不能操作UI, //使用Hanlder切换至主线程 } }; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IBookManager bookManager = IBookManager.Stub.asInterface(service); try { Log.v(TAG, bookManager.getBookList().toString()); //mListener = com.kutear.studydemo.android.art.demo2.AidlActivity$1@41900c48 bookManager.registerListener(mListener); bookManager.addBook(new Book(100, "Android")); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_aidl); Intent intent = new Intent(this, AidlService.class); bindService(intent, connection, BIND_AUTO_CREATE); }
在客户端调用服务端的函数,运行在服务端的Binder线程池中,若该函数是耗时操作,则需要在客户端开启异步线程来操作.否则ANR
注意使用类RemoteCallbackList来实现注册与解除注册,原因就是代码中的注释部分,对象重新生成过
权限控制
@Override public IBinder onBind(Intent intent) { int check = checkCallingOrSelfPermission("permission_name"); if(check == PackageManager.PERMISSION_DENIED){ //授权失败 //TODO }else{ //授权成功 //TODO } return mBinder; }
-
ContentProvider
主要的类就是ContentProvider(提供内容),ContentResolver(解析内容).自定义类继承至ContentProvider,实现基本的CURL方法.注意SQLiteDatabase本身是处理了同步的.
ContentProvider的onCreate在主线程执行,其余函数在Binder线程池中执行,需要注意的是同步问题.
除了基本的增删改之外,还可以调用自定义的方法.下面是个栗子.//ContentProvider public String customMethod(){ Log.v(TAG,"function customMethod"); return "Hello"; } @Nullable @Override public Bundle call(String method, String arg, Bundle extras) { if(method.equals("customMethod")){ customMethod(); //TODO 封装Bundle返回 //returm bundle; } return super.call(method, arg, extras); }
在需要的地方调用
Bundle bundle = getContentResolver().call(uri,"customMethod",null/*arg*/,null/*extras*/);
-
Socket
利用Socket来进程间的通信同在java中利用Socket通信是一模一样的,只是需要注意的就是一定不要在主线程通信.
2.5 Binder线程池
-
多个AIDL绑定在一个Service上
实现过程为在IBinderPool中绑定Service,获取到AIDL实现的一个IBinder(暂命名iquery,在service中返回的.包含一个查询其他AIDL的IBinder的方法)
在IBinderPool中实现查询的方法,实现部分通过iquery的方法.
2.6 选择合适的IPC
参考
Art of Android Development Reading Notes 2