⾸先,这可能是⼀个⾮常罕⻅的优化,但我们仍然花费了⼤量的时间和精⼒弄清楚这个优化的细节,所以我认为分享这个⼩优化是有意义的。期待有⼀天,某个地⽅的某个⼈可能会碰到相同的问题,并从这篇⽂章中受益。
什么是反射?
反射⽤于观察并修改程序在运⾏时的⾏为。⼀个反射导向的程序组件可以监测⼀个范围内的代码 执⾏情况,可以根据获取的⽬标对象信息及与此相关的范围修改⾃身。这可通过在运⾏时动态分 配程序代码实现。
在类型检测严格的⾯向对象的编程语⾔如Java中,⼀般需要在编译期间对程序中需要调⽤的对象的具体类型、接⼝(interface)、资料成员(fields)和⽅法的合法性进⾏检查。反射技术则允许将对需要调⽤的对象的消息检查⼯作从编译期间推迟到运⾏期间再现场执⾏。
这样⼀来,可以在编译期间先不明确⽬标对象的接⼝(interface)名称、字段(fields),即对象的资料成员(成员变量)、可⽤⽅法,然后在运⾏根据⽬标对象⾃身的消息决定如何处理。它还允许根据判断结果进⾏实例化新对象和相关⽅法的调⽤。
为什么会出现这个问题?
我们⼀直收到投诉,⽤户声称启⽤动态壁纸后,我们的Buzz⼩部件在红米Note5上滞后。为了复现此问题,我们使⽤友盟+U-APM在红⽶Note5上进⾏线上测试,同时使⽤⾃定义异常抛出问题。
测试表明,这个异常是通过WallpaperManager.getInstance(this).getWallpaperInfo()的if代码触发的,也就是说,这是由于应⽤程序模糊了桌⾯,并试图在桌⾯上呈现内容导致的问题。如果桌⾯上运⾏着动态壁纸,那么模糊它会造成⼀些周期性的处理器消耗。
为什么要使用反射,而不是API?
我更希望我们的每个应⽤程序在AndroidMarket中只有⼀个版本,⽽⽤于此的API仅在⼀部分SDK中可⽤。反射使我们能够使⽤API,同时仍然让应⽤程序在具有较旧固件的设备上运⾏。
如何解决?
好了,我们要开始展示解决⽅案了
if(WallpaperManager.getInstance(this).getWallpaperInfo()!=null){//不模糊} |
要使⽤反射来做到这⼀点,我们必须使⽤Class.forName("")、Class.getDeclaredMethod()和Object.invoke()⽅法,有点像这样:
boolean blurBackground = true; //get the WallpaperManager Class Class classWallpaperManager = Class.forName("android.app.WallpaperManager"); if(classWallpaperManager != null) { //find its .getInstance(this) method Method methodGetInstance = classWallpaperManager.getDeclaredMethod("getInsta //invoke the WallpaperManager's .getInstance(this) method to get one Object objWallpaperManager = methodGetInstance.invoke(classWallpaperManager,
//discover the WallpaperManager Object's .getWallpaperInfo() Method Method methodGetWallpaperInfo = objWallpaperManager.getClass().getMethod("ge //invoke it Object objWallPaperInfo = methodGetWallpaperInfo.invoke(objWallpaperManager, if(objWallPaperInfo!=null) { Log.d("WidgetDroid","WallpaperInfo not null"); blurBackground=false; } } |
同样为了确保安全调⽤,我们将它包装在⼀个快速的 Android版本检查和⼀个 try/catch块中以确保安全:
⼤功告成,现在如果设备使⽤的是动态壁纸,应⽤程序背景就不会模糊了,我们的WidgetWorld可以恢复正常。