日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Launcher3自定义壁纸旋转后拉伸无法恢复

發(fā)布時(shí)間:2025/5/22 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Launcher3自定义壁纸旋转后拉伸无法恢复 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

MTK8382/8121平臺(tái)。

?

描述:將自定義圖片設(shè)置成壁紙后,橫屏顯示時(shí),旋轉(zhuǎn)為豎屏,圖片由于分辨率過(guò)小,會(huì)拉伸;再旋轉(zhuǎn)為橫屏,拉伸不恢復(fù)。

這兩天正在解這個(gè)問(wèn)題,研究了很久,走了不少?gòu)澛?#xff0c;最后發(fā)現(xiàn)是Launcher讀取SharePreferences時(shí)的一個(gè)bug。

?

bug是這樣產(chǎn)生的:

Launcher3設(shè)置完自定義壁紙(系統(tǒng)自帶壁紙不會(huì)記錄)的時(shí)候,會(huì)在com.android.launcher3.WallpaperCropActivity.xml中記錄被設(shè)置壁紙的分辨率,并提交分辨率給WallpaperManager(通過(guò)suggestWallpaperDimension())。具體函數(shù)是:WallpaperCropActivity.java中的updateWallpaperDimensions(),它被WallpaperCropActivity.java的setWallpaper()調(diào)用;

Launcher3每次旋轉(zhuǎn)后會(huì)重新執(zhí)行onCreate(),同時(shí)會(huì)提交當(dāng)前壁紙的分辨率給WallpaperManager,提交分辨率的函數(shù)在Workspace.java中的setWallpaperDimension()中。問(wèn)題在這里:setWallpaperDimension()無(wú)法獲取之前updateWallpaperDimensions()修改的SharedPreferences,導(dǎo)致它提交的是默認(rèn)的壁紙分辨率1920x1080,從而導(dǎo)致低分辨率的壁紙拉伸。

解決此問(wèn)題的方法是:修改Workspace.java中的setWallpaperDimension()中的getSharedPreferences()的flag,把MODE_PRIVATE改為MODE_MULTI_PROCESS。修改后成功訪問(wèn)。

?

我的問(wèn)題是:

根據(jù)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只能被同一個(gè)application或者同一個(gè)userID的application調(diào)用。按這個(gè)說(shuō)法,這兩個(gè)Activity應(yīng)該是可以共同訪問(wèn)的。(Workspace.java使用的是Launcher.java的context,兩個(gè)Activity pid不一樣,uid一樣)

同時(shí)我還看了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.

意思是這個(gè)flag用于給擁有多個(gè)進(jìn)程的application共同訪問(wèn)同一個(gè)SharedPreferences使用的。按照這個(gè)說(shuō)法,似乎又確實(shí)應(yīng)該使用MODEL_MULTI_PROCESS。

?

于是我想找到getSharedPreference實(shí)現(xiàn)代碼,看看它怎么處理這幾個(gè)flag。可惡的是,從Activity父類(lèi)一級(jí)一級(jí)往上找,都找不到實(shí)現(xiàn)的方法,直到找到這篇文章:

http://blog.csdn.net/qinjuning/article/details/7310620

才知道ContextImpl實(shí)現(xiàn)了Context的具體方法,進(jìn)而找到了答案:

ContextImpl類(lèi)中有g(shù)etSharedPreferences的實(shí)現(xiàn)。里面說(shuō)明了在MODE_MULTI_PROCESS標(biāo)志中,getSharedPreferences會(huì)進(jìn)行reload。換言之MODE_PRIVATE不會(huì)重新讀取SharedPreferences。

這里終于搞懂他的意思:在之前的bug,并不是SharedPreferences獲取失敗,而是因?yàn)闆](méi)有reload所以沒(méi)有獲取到新寫(xiě)入的分辨率信息。因?yàn)橹皼](méi)有注意到這個(gè)問(wèn)題,所以走了彎路。

不過(guò)還有問(wèn)題:每次Launcher旋轉(zhuǎn)的時(shí)候都會(huì)重新啟動(dòng)Activity調(diào)用onCreate,為什么我getSharePreferences還是舊的呢?繼續(xù)觀察android.app.ContextImpl的getSharedPreferences:

@Override public SharedPreferences getSharedPreferences(String name, int mode) { Log.w("Launcher", "contextImpl: " + this, new RuntimeException("getSp").fillInStackTrace()); SharedPreferencesImpl sp; synchronized (ContextImpl.class) { if (sSharedPrefs == null) { Log.e("Launcher", "all init"); sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>(); }final String packageName = getPackageName(); ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName); if (packagePrefs == null) { Log.e("Launcher", "package init"); packagePrefs = new ArrayMap<String, SharedPreferencesImpl>(); sSharedPrefs.put(packageName, packagePrefs); }// At least one application in the world actually passes in a null // name. This happened to work because when we generated the file name // we would stringify it to "null.xml". Nice. if (mPackageInfo.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.KITKAT) { if (name == null) { name = "null"; } }sp = packagePrefs.get(name);if (sp == null) { File prefsFile = getSharedPrefsFile(name); sp = new SharedPreferencesImpl(prefsFile, mode); packagePrefs.put(name, sp); Log.e("Launcher", "new sp"); return sp; } Log.e("Launcher", "old sp"); }if ((mode & Context.MODE_MULTI_PROCESS) != 0 || getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) { // If somebody else (some other process) changed the prefs // file behind our back, we reload it. This has been the // historical (if undocumented) behavior. sp.startReloadIfChangedUnexpectedly(); Log.e("Launcher", "reload"); } return sp; }

里面Log.e("Launcher", ...);是我自己加的調(diào)試信息。首先會(huì)判斷sSharedPrefs是否有內(nèi)容,然后從中獲取對(duì)應(yīng)package的prefsFile。如果sSharedPrefs找不到,才從xml文件中重新讀取。最后加了一個(gè)判斷,如果設(shè)置了MODE_MULTI_PROCESS變量,或者Android 2.2以下的系統(tǒng),會(huì)默認(rèn)從xml文件中重新reload,以保持最新的SharedPreferences數(shù)據(jù)。

注意這里的sSharedPrefs變量,它只在getSharedPreferences中有賦值,也就是說(shuō)SharedPreferences的數(shù)據(jù)一直跟隨著ContextImpl實(shí)例走,只從getSharedPreferences()方法中獲取數(shù)據(jù)。也就是說(shuō),當(dāng)旋轉(zhuǎn)屏幕的時(shí)候,我們調(diào)用getSharedPreferences()獲取的數(shù)據(jù)都是從這個(gè)sSharedPrefs變量中取出來(lái)的。實(shí)際從下面Log信息也可以看到,旋轉(zhuǎn)并不會(huì)有"new sp"的Log打印,只有對(duì)Launcher3在設(shè)置中force stop和clear data的時(shí)候才會(huì)出現(xiàn)"new sp"。

旋轉(zhuǎn)的Log提示:

09-11 08:47:19.599: E/Launcher(4628): launcher:com.android.launcher3.Launcher@42392ac8 09-11 08:47:19.599: E/Launcher(4628): mBase:android.app.ContextImpl@424c1898 09-11 08:47:19.608: E/Launcher(4628): old sp 09-11 08:47:19.727: E/Launcher(4628): old sp 09-11 08:47:19.731: E/Launcher(4628): reload 09-11 08:47:19.932: E/Launcher(4628): old sp

force stop的提示:

09-11 08:48:29.456: E/Launcher(5271): launcher:com.android.launcher3.Launcher@4238f220 09-11 08:48:29.456: E/Launcher(5271): mBase:android.app.ContextImpl@42391ab8 09-11 08:48:29.489: E/Launcher(5271): all init 09-11 08:48:29.489: E/Launcher(5271): package init 09-11 08:48:29.493: E/Launcher(5271): new sp 09-11 08:48:29.679: E/Launcher(5271): new sp 09-11 08:48:29.766: E/Launcher(5271): old sp 09-11 08:48:29.767: E/Launcher(5271): old sp 09-11 08:48:29.790: E/Launcher(5271): old sp

里面三個(gè)連著的old sp,只有第二個(gè)是讀取分辨率的,其他兩個(gè)分辨是LauncherAppState的SharedPreferences。因?yàn)榇藭r(shí)Launcher3代碼已經(jīng)被我修改為MODE_MULTI_PROCESS,所以旋轉(zhuǎn)會(huì)打出"reload"信息。

?

也就是說(shuō),旋轉(zhuǎn)的時(shí)候sSharedPrefs的值是一直保存著的。可是通過(guò)Log打印信息,我們發(fā)現(xiàn),每一次的getSharedPreferences()的contextImpl都是不一樣的!

這里要注意的是,執(zhí)行方法的context是Activity的父類(lèi)ContextThemeWrapper的mBase私有成員執(zhí)行的,獲取mBase可以在getSharedPreferences()中打印this出來(lái),也可以在Activity中進(jìn)行反射,這里用的是反射方法:

try { Log.e("Launcher", "launcher:" + this); java.lang.reflect.Field f = Activity.class.getSuperclass().getDeclaredField("mBase"); f.setAccessible(true); Context s = (Context) f.get(this); Log.e("Launcher", "mBase:" + s); } catch(Exception e) { e.printStackTrace(); }

contextImpl不一樣了,但是sSharedPrefs數(shù)據(jù)還保存著,且sSharedPrefs不能通過(guò)其他方法賦值,只能猜測(cè)在旋轉(zhuǎn)的時(shí)候通過(guò)原型模式把原來(lái)的context傳進(jìn)去了。Launcher3的旋轉(zhuǎn)處理,我只找到ACTION_CONFIGURATION_CHANGE這個(gè)廣播,但是我把里面的代碼注釋了,依然可以正常工作。估計(jì)是更底層的代碼控制的。

?

具體代碼還沒(méi)找到,因?yàn)檫€要搬磚。有空再研究吧!

版權(quán)所有,轉(zhuǎn)載請(qǐng)注明出處:

http://www.cnblogs.com/sickworm/p/3966857.html?

轉(zhuǎn)載于:https://www.cnblogs.com/sickworm/p/3966857.html

總結(jié)

以上是生活随笔為你收集整理的Launcher3自定义壁纸旋转后拉伸无法恢复的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。