【问题】
发现app出现crash,具体看到堆栈:
android.app.RemoteServiceException: can't deliver broadcast
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2047)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:236)
at android.app.ActivityThread.main(ActivityThread.java:7879)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)
断点跟代码,发现历史代码有在两个Activity间使用广播传递压缩文件。好吧,这种操作crash一点都不冤,广播就不是用来干这个事儿的。
【问题原因】
使用broadcast传递压缩图片,在压缩图片稍微大一点就会引起系统跑异常,导致应用crash(原理参见)。
【解决方案】
-
全局变量
- 将压缩后的图片文件放在一个全局变量中,使用的Activity通过这个全局变量取出,再进行后续业务操作。
- 这种方案最为直接暴力,但是存在问题就是这种大内存没有非常合适的时机进行清缓存动作,导致应用可能长时间处在高内存的状态运行。
-
SharedPreference
- 将压缩后的图片序列化后存储到本地的SharedPreference中,使用的Activity读取SharedPreference后反序列化获得该对象,进行后续业务操作。
- 该方案解决了一直缓存大对象的问题,但这种io操作对于性能有所损失。
-
Binder
- Binder是谷歌为了跨进程通信而设计的IPC组件,肩负了Android系统大部分通信任务,在跨进程通信中请注意1MB的对象大小要求。由于本问题出现在同一个APP的两个Activity中,属于同进程通信,共享内存块,binder通信没有了对象大小的限制。所以可以完美解决本场景。具体实现如下。
- 创建一个Aidl
interface IPic { String getPic(); }
- 在压缩图片端填入图片对象
public void sendBroadcas(final String pic) { Bundle bundle = new Bundle(); bundle.putBinder("picture", new IPic.Stub() { @Override public String getPic() throws RemoteException { return pic; } }); Intent intent = new Intent("com.android.pic"); intent.putExtras(bundle); sendBroadcast(intent); }
- 在业务处理端获取图片对象
@Override public void onReceive(Context context, Intent intent) { if ("com.android.pic".equals(intent.getAction())) { Bundle bundle = intent.getExtras(); if (bundle == null) { return; } Object object = bundle.get("picture"); if (object instanceof IBinder) { IPic iPic = IPic.Stub.asInterface((IBinder) object); try { String pic = iPic.getPic(); // 后续业务 } catch (RemoteException e) { e.printStackTrace(); } } } }
- 验证是同进程的Binder是否没有1MB限制,简单的进行了一下测试。
// 10MB createSpecifiedSizeData(10*1024*1024) private static String createSpecifiedSizeData(int size) { StringBuilder sb = new StringBuilder(size); for (int i = 0; i < size; i++) { sb.append('0'); } return sb.toString(); }