MTK8382/8121平台。
描述:将自定义图片设置成壁纸后,横屏显示时,旋转为竖屏,图片由于分辨率过小,会拉伸;再旋转为横屏,拉伸不恢复。
这两天正在解这个问题,研究了很久,走了不少弯路,最后发现是Launcher读取SharePreferences时的一个bug。
bug是这样产生的:
Launcher3设置完自定义壁纸(系统自带壁纸不会记录)的时候,会在com.android.launcher3.WallpaperCropActivity.xml中记录被设置壁纸的分辨率,并提交分辨率给WallpaperManager(通过suggestWallpaperDimension())。具体函数是:WallpaperCropActivity.java中的updateWallpaperDimensions(),它被WallpaperCropActivity.java的setWallpaper()调用;
Launcher3每次旋转后会重新执行onCreate(),同时会提交当前壁纸的分辨率给WallpaperManager,提交分辨率的函数在Workspace.java中的setWallpaperDimension()中。问题在这里:setWallpaperDimension()无法获取之前updateWallpaperDimensions()修改的SharedPreferences,导致它提交的是默认的壁纸分辨率1920x1080,从而导致低分辨率的壁纸拉伸。
解决此问题的方法是:修改Workspace.java中的setWallpaperDimension()中的getSharedPreferences()的flag,把MODE_PRIVATE改为MODE_MULTI_PROCESS。修改后成功访问。
我的问题是:
根据Android Developer的解释:
MODE_PRIVATE:
File creation mode: the default mode, where the created file can only be accessed by the calling application (or all applications sharing the same user ID).
即MODEL_PRIVATE只能被同一个application或者同一个userID的application调用。按这个说法,这两个Activity应该是可以共同访问的。(Workspace.java使用的是Launcher.java的context,两个Activity pid不一样,uid一样)
同时我还看了MODE_MULTI_PROCESS的解释:
MODE_MULTI_PROCESS:
SharedPreference loading flag: when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process. This behavior is sometimes desired in cases where the application has multiple processes, all writing to the same SharedPreferences file. Generally there are better forms of communication between processes, though.
This was the legacy (but undocumented) behavior in and before Gingerbread (Android 2.3) and this flag is implied when targetting such releases. For applications targetting SDK versions greater than Android 2.3, this flag must be explicitly set if desired.
意思是这个flag用于给拥有多个进程的application共同访问同一个SharedPreferences使用的。按照这个说法,似乎又确实应该使用MODEL_MULTI_PROCESS。
于是我想找到getSharedPreference实现代码,看看它怎么处理这几个flag。可恶的是,从Activity父类一级一级往上找,都找不到实现的方法,直到找到这篇文章:
http://blog.csdn.net/qinjuning/article/details/7310620
才知道ContextImpl实现了Context的具体方法,进而找到了答案:
ContextImpl类中有getSharedPreferences的实现。里面说明了在MODE_MULTI_PROCESS标志中,getSharedPreferences会进行reload。换言之MODE_PRIVATE不会重新读取SharedPreferences。
这里终于搞懂他的意思:在之前的bug,并不是SharedPreferences获取失败,而是因为没有reload所以没有获取到新写入的分辨率信息。因为之前没有注意到这个问题,所以走了弯路。