本文基于WorkManager的版本是2.5.0
我在Oppo的6.0一下手机(<23),使用WM,会发现偶尔有这样的一个错误:
1 | ava.lang.SecurityException |
通过搜索我们知道了是oppo手机的电源管理导致的问题,可以看Service在OPPO系列手机下无法启动。我的日志目前都是发生在WorkManager的SystemAlarmScheduler的cancel方法,我们的目的就是给这个方法增加一个try/catch来避免后台崩溃。
实现
我们看代码,是知道SystemAlarmScheduler是在Schedulers被调用并被实例化,如下:
1 | static Scheduler createBestAvailableBackgroundScheduler( |
我们可以知道,假如在api23以下,他会先走tryCreateGcmBasedScheduler,假如不存在才回去创建一个SystemAlarmScheduler对象,tryCreateGcmBasedScheduler方法如下
1 | @Nullable |
可以看到他通过反射生成GCM_SCHEDULER这个类,该类有一个构造函数的参数是Context。看到是通过GCM_SCHEDULER来生成一个类,那我们能否替换GCM_SCHEDULER来换成自己的路径,然后对新类的cancel方法进行替换呢?然后我们看了下GCM_SCHEDULER的类型,他是public static final String类型的,无法通过发射修改,因此,我们就无法更改tryCreateGcmBasedScheduler的返回了。
然后我们继续重头捋一下,看到:createBestAvailableBackgroundScheduler是被WorkManagerImpl.createSchedulers调用的,createSchedulers得到的schedulers会传递给WorkManagerImpl的构造器,最终到了成员变量mSchedulers中,也到了Processor的变量mSchedulers中,那么我们就可以换一种方式,即在WorkManagerImpl完成初始化之后,通过发射修改成员变量schedulers,从而达到替换SystemAlarmScheduler的目的。具体的代码如下:
1 | package androidx.work.impl |
OppoAlarmScheduler的代码如下:
1 | package androidx.work.impl.background.systemalarm |
这样就可以在WorkManager初始化之后通过反射去掉SystemAlarmScheduler并更换自定义的
可以看到他通过反射生成GCM_SCHEDULER这个类,该类有一个构造函数的参数是Context。
看到了这里,我们就看到了曙光,我们可以通过反射修改GCM_SCHEDULER的值,使得他指向我们自定义的类,我们自定义一个继承自SystemAlarmScheduler的类,然后把它的cancel方法try/catch主即可,这里建议新建的类需要与SystemAlarmScheduler路径保持一致。
1 | @Keep |
然后我们再自定义WorkManaer的初始化或者是Configuration.Provider调用之前发射设置GCM_SCHEDULER值,初始化的问题看orkManager的坑
1 | //在WorkManager.initialize(context!!, config)之前调用 |
这样就fix这个oppo问题了。