谈谈IPC
IPC,是指inter-proccess-communication,即进程间通信。
so,要先理解进程和线程。
1.进程和线程–
进程process是指一个执行单元,一个应用程序,一个虚拟机,可以包含多个线程。
线程thread是指cpu可以调度的最小单元,在Android中分为主线程和子线程。
任何的一个操作系统都有IPC机制。android中IPC机制最主要的就是Binder(messenager,aidl,ContentProvider都是Binder的实现),当然还有socket,通过网络实现任意两个终端,也可以理解为进程,不同应用,之间的通信。
2.多进程的原因
一般情况下,我觉得不推荐使用多进程,因为会存在很多问题,因为进程之间属于不同的内存空间。但是在某些特殊情况下不得不用,比如:
1.每个应用有最大内存限制,需要新开进程,以得到更大的内存空间
2.就是要访问其他应用的数据
3.怎么开
android的四大组件都可以开启多进程模式,通过androidmenifest的androird:process=”:remote”或者android:process=”com.aaa.bb”开启,和JNI fork一个进程(这个还不会)
区别::remote开启的进程名字是 包名:remote,属于私有进程,其他应用组件不能跟他跑在同一个进程
com.aaa.bb开启的进程名字就是它,属于全局进程,其他应用通过shareUID和签名一致后可以和它跑在一个进程(没有测试)
adb shell
$ps |grep com.aaa.bb可以查看进程状态
4.存在的注意问题
1.内存不同,静态变量,单例模式会不同,
2.线程同步失效
3.Application每开一次进程都会调用一次
4.sp由于存在缓存策略,也会存在不稳定情况。
5.主要的原理
主要是通过对象的序列化和反序列化实现进程间的通信。说一下序列化的两种方式:Serializable和Parcelable接口。
Seraialzable中的serialVersionUID主要用来反序列化的时候确保版本相同。当增加了这个参数之后,对象增加或者删除了成员变量,在版本升级之后不会crash,如果没有就会crash。但是发生了毁灭性的改变比如改变类型改变类名还是不行。transient标志的不会参与序列化比如密码信息等。
parcelable建立之后,声明成员变量之后,就可以自动生成那些方法啦。Intent,Bundle,Bitmap都是系统默认实现了这个接口的,可以直接传输。
Parcelable主要用在内存序列化,Serialzable用在本地和网络序列化上。
IInterface接口,所有在Binder中传输的接口都要实现接口
Binder,主角,主要通信方式无非都是返回一个Binder,不管是Messenager还是aidl,都是通过messenager.getBinder和IAIDL.stub然后通过服务的onBinde 返回mBinder.所以说它是进程见通信的桥梁,底层通过binder实现SeriveManager连接各种Manager(ActivityManager,WindowManager等)
当建立好aidl文件荚和xx.aidl之后,系统会自动生成他的Binder实现类。当然这个也可以自己实现,自己建立这个实现类,只不过系统帮助我们通过aidl文件实现了。
这个类内部主要有静态内部类Stub(extends Binder),内部方法有proxy,asinterface,asbinder,ontransact,和接口的实现方法。
Stub就是binder的真正实现类,通过asInterface方法,判断是统一进程不走transact,是多进程走Stub.Proxy进行mRemote.transact.
ontranact结果写入_reply参数后返回true表示调用成功,false表示失败,所以可以通过它来进行权限校验等操作。
远程调用的实现方法都是在Binder线程池进行的操作,所以会存在耗时的情况.
6.主要的方式
首先应该明确,尽量想办法数据在一个进程处理。实在不行再考虑跨进程通信
1.Bundle三大组件。
2.文件共享。要求不要数据同步。
3.Messenager.通过与Handler的配合,传递单条信息,不存在同步问题。不能调方法和自定义对象。
服务端:
private final Messenger mMessenger = new Messenger(new MessengerHandler()); @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); }
客户端:
public void onServiceConnected(ComponentName className, IBinder service) { mService = new Messenger(service);
4.aidl
支持基本类型和String,ArraList,HashMap,Parcelable,AIDL,
其中要显式的import 自定义的parcelable和aidl接口,比如
package com.xxx.xxx.aidl;import com.xxx.xxx.aidl.Book;import com.xxx.xxx.aidl.IOnNewBookArrivedListener;interface IBookManager { List<Book> getBookList(); void addBook(in Book book); void registerListener(IOnNewBookArrivedListener listener); void unregisterListener(IOnNewBookArrivedListener listener);}
其中Book这个类要在java包下一个和aidl文件同名的包下。
每定义一个aidl,然后就定义一个它的.Stub实现类,然后建立一个BinderPool aidl及实现类统一管理,是比较有效的方法
@Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind"); return mBinderPool; }
public static class BinderPoolImpl extends IBinderPool.Stub { public BinderPoolImpl() { super(); } @Override public IBinder queryBinder(int binderCode) throws RemoteException { IBinder binder = null; switch (binderCode) { case BINDER_SECURITY_CENTER: { binder = new SecurityCenterImpl(); break; } case BINDER_COMPUTE: { binder = new ComputeImpl(); break; } default: break; } return binder; } }
7.远程挂掉的监听
1.设置死亡代理
Binder Thread
IBookManager bookManager=IBookManager.Stub.asInterface(service); mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0);
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { Log.d(TAG, "binder died. tname:" + Thread.currentThread().getName()); if (mRemoteBookManager == null) return; mRemoteBookManager.asBinder().unlinkToDeath(mDeathRecipient, 0); mRemoteBookManager = null; // TODO:这里重新绑定远程Service } };
2.判断是否存活
if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()) { try { Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener); //重新连接 }
3.onserviceDisconnected中重连
UIThread
8.解注册方法
RemoteCallbackList 用于删除跨进程listener接口
//配对使用 final int N = mListenerList.beginBroadcast(); mListenerList.finishBroadcast();
9.权限校验
1.onBind
2.OnTranact
//声明权限<uses-permission android:name="com.xxx.PROVIDER" />
//使用权限<permission android:name="com.xxx.PROVIDER" android:protectionLevel="normal" />
socket
需要网络权限、。
这个比较简单,开启一个服务,运行ServerSocket
serverSocket = new ServerSocket(8888); while (!mIsServiceDestoryed) { try { // 接受客户端请求 final Socket client = serverSocket.accept(); System.out.println("accept"); new Thread() { @Override public void run() { try { responseClient(client); } catch (IOException e) { e.printStackTrace(); } }; }.start(); } catch (IOException e) { e.printStackTrace(); } }
客户端开一个线程跑:
private void connectTCPServer() { Socket socket = null; while (socket == null) { try { socket = new Socket("localhost", 8688); mClientSocket = socket; mPrintWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(socket.getOutputStream())), true); mHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED); System.out.println("connect server success"); } catch (IOException e) { SystemClock.sleep(1000); System.out.println("connect tcp server failed, retry..."); } }
contentprovider
android:authoritities 唯一标示
Uri来区分不同的表。
除了OnCreate方法外,其他都是运行在Binder Thread里的
通过返回文件的句柄给外界,支持文件数据,图片和视频等,getType的返回不同。
只通过一个SQLlteDatabase内部对数据有同步处理,但是多个SQLiteDatabase对象的话,就不能实现同步了。
https://github.com/singwhatiwanna/android-art-res
更多相关文章
- Android init进程——属性服务
- android 进程间通信使用aidl和Messenger类
- android实现app通过jni调用C/C++方法
- android为按钮添加事件的三种方法
- Android在初始化时弹出popwindow的方法 .
- android开发环境搭建最新方法
- Android自定义属性 及 TypedArray的使用方法
- android程序退出当前activity的方法
- 获取Android的key的MD5和SHA1的方法