本文基于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问题了。