最近开发了一个视频桌面类的App,期间遇到了一些问题,现在做一个总结,记录一下自己的分析过程。也希望能够给遇到相关问题的人带来一些收获吧。
在网上搜索一下Android视频桌面的实现,很顺利的完成了一个应用,主要用到的类有两个:
这是Android系统提供的一个用于实现壁纸服务的抽象类,它的唯一目的就是根据需要生成一个Engine的实例。主要复写如下方法即可,该方法返回一个Engine类:
@Override
public Engine onCreateEngine() {
return new Engine();
}
这个类是WallpaperService的一个内部类,WallpaperService要返回的Engine就是它。这个Engine是壁纸的实际实现。这个Engine会提供一个对应的SurfaceHolder,预览视频时MediaPlayer可以通过调用mPlayer.setSurface(surface);将Engine提供的SurfaceHolder.getSurface()设置进去。
主要复写以下几个方法:
public void onCreate(SurfaceHolder surfaceHolder) {
}
public void onDestroy() {
}
public void onVisibilityChanged(boolean visible) {
}
public void onSurfaceCreated(SurfaceHolder holder) {
}
public void onSurfaceDestroyed(SurfaceHolder holder) {
}
其中onVisibilityChanged方法非常重要,在该方法中,当visible为false时,你可能就需要暂停视频的播放。
然后通过如下方式启动壁纸服务:
Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
new ComponentName(activity, VideoWallpaperService.class));
activity.startActivityForResult(intent, REQUEST_SET_LIVE_WALLPAPER);
效果如下图所示:
第一反应是不是哪里代码写错了,于是到网上到处找资料。一行一行对比代码。期间下载了网上的例子,发现有些也有这个问题。于是在stackoverflow上搜索,
https://stackoverflow.com/questions/39805800/live-wallpaper-not-change-in-android-device-when-set-another-live-wallpaper/39888063#39888063
结果真的搜索到一个答案。在启动壁纸服务之前之前调用如下代码
try {
WallpaperManager wm = WallpaperManager.getInstance(context);
wm.clear();
} catch (IOException e) {
e.printStackTrace();
}
然后将这个代码加了上去。本以为这样就结束了,可是测试着发现,当你点击设置壁纸到壁纸预览界面的时候。这个时候如果你返回,不把当前视频设置为壁纸,你会发现你之前设置的壁纸也已经被清除了。这显然是不满足产品需求的。
于是老老实实看代码,研究为啥设置壁纸的时候能够预览,但是设置不生效。刚开始以为是缓存,是不是设置过视频之后,系统的壁纸服务是从缓存再读取数据。找了一圈也没有缓存相关的信息与接口。于是在自己实现的WallpaperService内的所有类的所有回调方法里面打印信息,先点击进入预览界面。然后上面提到的Engine的回调方法都走了一遍,然后点击设置壁纸,发现又走了一遍上面的方法。而且又走了次onSurfaceDestroyed,这就很奇怪了。过滤之后的信息如下:
为什么Engine会走两遍了,真的是想不通。点进去Engine里看源代码,结果类上面有这样的注释
上面的意思是一个wallpaper service 可能有多个运行的Engine实例,例如用于预览的Engine以及用于真正壁纸的Engine。于是赶紧去验证一下,在上面自定义Engine里面所有的生命周期方法中打印一下Engine的hashcode,以及surfaceviewholder的hashcode,果然两次的值是不一样的。然后重新设置一下壁纸,发现只有用于Preview的Engine销毁重建了,用于真正壁纸的Engine是没有销毁重建的。这样就解释了为什么第二次设置视频壁纸的时候设置不上,因为real wallpaper的engine还是用的之前的。那么现在我们如果想要更新的话只需要将real wallpaper engine里的视频源更新一下就好。那么我们首先要做的就是将real wallpaper的engine保存下来。那么怎么区分是preview还是real wallpaper的engine呢,查看service的源码还真找到了如下方法
/**
* Returns true if this engine is running in preview mode -- that is,
* it is being shown to the user before they select it as the actual
* wallpaper.
*/
public boolean isPreview() {
return mIWallpaperEngine.mIsPreview;
}
那么我们需要做的就是在自定义的Engine里的onCreate方法中调用该方法判断一下,如果不是Preview的Engine就将当前Engine保存下来。用于之后的视频源更新。
其实这个问题本质是,有些手机清除最近应用时会杀掉进程,而有些手机不会。而一旦进程被杀掉,壁纸服务就会停止运行,然后设置的壁纸自然会失效。然后研究了一下火萤和抖音,发现他们的方案是需要下载一个插件,然后插件其实是一个独立的提供壁纸服务的apk。大家可以去试一下,只要把这个插件的进程杀掉,他们设置的壁纸一样会失效。
由于我们这个壁纸应用只是简单的想进入应用市场试一下,就只做了简单的处理。
1.我们是在android配置文件中设置了如下代码,让应用不显示在最近运行的进程任务列表中;
android:excludeFromRecents="true"
2.同时提高了Service的优先级,让service不那么轻易被杀死;
其实遇到问题的时候,在简单搜索之后,如果找不到答案,就应该好好查看代码,通过代码来分析定位问题。不应该一直在网上搜索,去找现成的答案,我就是在这上面浪费了许多时间。长时间尝试自己定位问题,思考解决方案之后,自己解决代码的能力才会越来越强。加油!