将Matisse进程化
Matisse是什么?
Matisse是知乎团队开源的一个设计良好的本地图片/视频文件选择库,支持不同的图片加载方式。
先来张效果图,镇镇压。
具体的使用方法,参考官方Sample
github 地址: https://github.com/zhihu/Matisse
什么是多进程?为什么要多进程?
正常情况下,一个apk启动后就是一个进程,这是系统的默认行为,其进程名就是我们在AndroidManifest.xml配置的包名。多进程之间的内存是不可见的,所以这也是我们手机上运行某款app突然崩溃后,并没有影响到其他app的原因。不过我们可以使用android:process属性,来将我们的app配置成多进程。
多进程的好处:
减轻app负担,将内存均匀化。什么意思呢,我们知道Android系统对每个应用进程的内存占用是有限制的,而且占用内存越大的进程,通常被系统杀死的可能性越大。而让app多进程化,可以减少主进程所占用的内存,降低被系统杀死的概率;
如果子进程崩溃后,并不影响主进程,我们可以继续工作;
多进程的坏处:
- 由于进程之间内存是不可见的,所以我们无法像平时一样正常的通讯。不过安卓已经提供了相应的跨进程之间通讯的技术,如AIDL,Messager,ContentProvider,Socket…等。这里不讨论具体的实现方案,大家可以自行百度;
微博App安卓客户端配置了5个进程:
公司项目中安卓客户端进程化:
公司项目中配置了4个:
- com.shengda.extension 主进程
- com.shengda.extension:mult 子进程,极光推送后台服务
- com.shengda.extension:remote 子进程,百度地位后台服务
- com.shengda.extension:matisse 子进程,Matisse图片选择库
为什么需要把Matisse配置成单独进程?
我们都知道图片在手机中是比较占用内存的,如果处理不好会很容易造成OOM而导致App崩溃的,所以这里对Matisse的简单修改,来达到应用的进程化;
应用多进程化的细节
当一个应用配置了多个进程后,那么Application会有多个,它和进程的数量是相对的。所以我们要注意下平时在Application所做的初始化工作。
如下:
public static Context mApplicationContext;
@Override
public void onCreate() {
super.onCreate();
mApplicationContext = this.getApplicationContext();
//x5内核初始化
//initX5();
//极光推送初始化
JPushInterface.init(mApplicationContext);
//初始化百度地图
SDKInitializer.initialize(mApplicationContext);
this.registerActivityLifecycleCallbacks(callbacks);
//全局异常捕获
MobclickAgent.setCatchUncaughtExceptions(false);
CrashHandler.getInstance().init();
}
由于我们的应用以及多进程了,所以这里的初始化并不是每一个进程都需要用到的,所以我们建议修改为如下:
public static Context mApplicationContext;
@Override
public void onCreate() {
super.onCreate();
mApplicationContext = this.getApplicationContext();
//如果是主进程,则开始初始化第三方sdk,因为这些sdk只有在主进程中才被使用到
if(AppUtils.isMainProcess(mApplicationContext)){
//x5内核初始化
//initX5();
//极光推送初始化
JPushInterface.init(mApplicationContext);
//初始化百度地图
SDKInitializer.initialize(mApplicationContext);
}
this.registerActivityLifecycleCallbacks(callbacks);
//全局异常捕获
MobclickAgent.setCatchUncaughtExceptions(false);
CrashHandler.getInstance().init();
}
将Matisse进程化
由于Matisse是开源的,所以我们需要下载他的源码并导入到Android Studio中,并在其上进行简单修改即可。
我们打开Matisse的AndroidManifest.xml修改代码如下:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zhihu.matisse">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
//android:process 注意这个属性,也就是指定该进程的名称
<application>
<activity android:name="com.zhihu.matisse.ui.MatisseActivity" android:process=":matisse"/>
<activity android:name="com.zhihu.matisse.internal.ui.AlbumPreviewActivity" android:process=":matisse"/>
<activity android:name="com.zhihu.matisse.internal.ui.SelectedPreviewActivity" android:process=":matisse"/>
</application>
</manifest>
然后我们看下Matisse的使用:
Matisse.from(MainActivity.this)
.choose(MimeType.allOf())
.countable(true)
.maxSelectable(9)
.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
.gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.thumbnailScale(0.85f)
.imageEngine(new GlideEngine())
.forResult(REQUEST_CODE_CHOOSE);
链式调用,进行了一些配置设置之后进行了startActivityForResult
public void forResult(int requestCode) {
Activity activity = mMatisse.getActivity();
if (activity == null) {
return;
}
Intent intent = new Intent(activity, MatisseActivity.class);
Fragment fragment = mMatisse.getFragment();
if (fragment != null) {
fragment.startActivityForResult(intent, requestCode);
} else {
activity.startActivityForResult(intent, requestCode);
}
}
通过简单的阅读代码后,我们知道了大概的流程,所以我们只需要将那些配置参数转换为Bundle来进行intent传输,并取出最后进行设置即可。
这里说下startActivityForResult 和 onActivityResult 是支持跨进程间的,所以这里我们无需其他的进程间通讯技术。
修改后的代码:
//工具类,直接调用,跳转到图片选择界面,最后通过onActivityResult回调选中结果
public static void openPhotoSlect(Activity activity, int maxSize) {
Bundle bundle = new Bundle();
bundle.putInt("maxSize",maxSize);
bundle.putBoolean("capture",false);
Matisse.from(activity)
.choose(MimeType.ofImage(), false)
.forResult(Constant.INTENT_MATISSE_REQUEST,bundle);
}
SelectionSpec源码配置参数设置,进行了一些初始值的设置:
public Set<MimeType> mimeTypeSet = MimeType.ofImage();
public boolean mediaTypeExclusive;
public boolean showSingleMediaType;
@StyleRes
public int themeId = R.style.Matisse_Zhihu;
public int orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
public boolean countable = true ;
public int maxSelectable = 9;
public List<Filter> filters = new ArrayList<>();
public boolean capture;
public CaptureStrategy captureStrategy = new CaptureStrategy(true, MatisseActivity.APPLICATION_CONTEXT.getPackageName() + ".fileprovider");
public int spanCount;
public int gridExpectedSize = MatisseActivity.APPLICATION_CONTEXT.getResources().getDimensionPixelSize(R.dimen.grid_expected_size);
public float thumbnailScale = 0.85f;
public ImageEngine imageEngine = new GlideEngine();
private SelectionSpec() {
filters.add(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K));
}
这里我们就完成了Matisse的进程化,最后说下Matisse的细节问题。
由于项目中有图片预览功能和图片选择功能,所以统一使用了Matisse。如果大家没对源码进行任何修改,则直接使用图片预览功能会报错,原因就是SelectionSpec的配置没有默认值,大家可以采用以下方式解决:
一、 在使用预览功能之前对起配置进行设置
Matisse.from(MainActivity.this)
.choose(MimeType.allOf())
.countable(true)
.maxSelectable(9)
.addFilter(new GifSizeFilter(320, 320, 5 * Filter.K * Filter.K))
.gridExpectedSize(getResources().getDimensionPixelSize(R.dimen.grid_expected_size))
.restrictOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.thumbnailScale(0.85f)
.imageEngine(new GlideEngine())
//最后无需发起跳转界面,也就是forResult()无需调用
二、 SelectionSpec对这个类的源码进行配置默认值的设置
好了,Matisse进程化大概就这样了,有什么问题,请留言。