圖凌閃屏頁及Android彩蛋探究
通過本文,你可以 1、瞭解一種特別的閃屏 2、瞭解android版本彩蛋的實現原理 3、獲得一個android各版本彩蛋的 demo
特殊的閃屏
在體驗App時發現了一款叫‘圖凌’的app,閃屏頁非常特別。從下圖可以看到,是一個以桌面桌布為背景的頁面。

這種閃屏效果讓人眼前一亮,所以非常好奇他的實現原理。在不破解apk的情況下(破解失敗,有騰訊樂固加固==),猜想了幾種實現方式:
1、通過Api獲取桌布,然後設定Activity的背景 2、特殊的Activity theme 複製程式碼
在逐個驗證猜想之前,想到了一個頁面的實現與‘圖凌’的閃屏非常相似,就是Android的版本彩蛋(設定-關於手機-Android版本點選3下)。而這個頁面是可以找到原始碼的。
扒原始碼
打開彩蛋頁面,執行以下命令,可以得知Activity的名字是PlatLogoActivity。
adb shell dumpsys activity | grep "Focus" mFocusedActivity: ActivityRecord{2829baa u0 android/com.android.internal.app.PlatLogoActivity t4844} mFocusedStack=ActivityStack{d93bf0d stackId=1, 4 tasks} mLastFocusedStack=ActivityStack{d93bf0d stackId=1, 4 tasks} 複製程式碼
(或者 adb shell dumpsys activity top
)
通過線上原始碼平臺Xref,找到PlatLogoActivity,原始碼就不貼了,因為重點並不在 PlatLogoActivity.java
,而是在AndroidManifest,theme才是關鍵!也就是開始的第二個猜想。
<activity android:name="com.android.internal.app.PlatLogoActivity" android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen" android:configChanges="orientation|keyboardHidden" android:process=":ui"> </activity> 複製程式碼
試一試
通過原始碼基本瞭解了實現原理,但沒有跑一下Demo是不靠譜的。 PlatLogoActivity的實現非常獨立,沒有太多依賴。所以copy了一下各個版本的PlatLogoActivity做了一個Demo。
Github: github.com/PortgasAce/…


探究
現在我們已經知道theme可以方便的實現‘圖凌’的閃屏效果。那麼程式碼可以實現嗎?官方有開放相應的api嗎?
這些問題的答案可以從theme的實現原理入手。通過google或者xref搜尋theme的一些關鍵字,最終可以找到 PhoneWindow#generateLayout 有這樣一段解析theme標籤的程式碼:
... //省略了很多標籤解析 if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) { requestFeature(FEATURE_SWIPE_TO_DISMISS); } if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } ...//省略了很多標籤解析 複製程式碼
theme的主要實現就是通過 window#requestFeature
和 window#setFlags
方法改變樣式。
臨摹著寫了 Theme.Wallpaper.NoTitleBar.Fullscreen
的java實現,見 github:PlatLogoActivityNoStyle 主要程式碼:
requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().getDecorView().setWillNotDraw(true); getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT)); getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER); 複製程式碼
到此為止,猜想2通過theme和java實現都是驗證了。那麼其他猜想是否可行呢? 答案當然是可以的啦。通過sdk提供的 WallpaperManager
可以獲取桌面桌布。
Drawable bg = WallpaperManager.getInstance(context).getDrawable(); rootView.setBackground(bg); 複製程式碼
以上程式碼就可以給Activity設定背景為桌面桌布。
但是有一個問題。動態桌布(live wallpapewr)時通過該方法獲取的drawable不但不會動,而且是錯誤的圖片。
其實liveWallpaper獲取的正確姿勢是通過 wallpaperManager#getWallpaperInfo#loadThumbnail
:
private Drawable getWallpaperDrawable() { Drawable wallpaperDrawable; PackageManager pm = getApplicationContext().getPackageManager(); WallpaperManager wallpaperManager = WallpaperManager.getInstance(this); if (wallpaperManager.getWallpaperInfo() != null) { /* * Wallpaper info is not equal to null, that is if the live wallpaper * is set, then get the drawable image from the package for the * live wallpaper */ wallpaperDrawable = wallpaperManager .getWallpaperInfo().loadThumbnail(pm); } else { /* * Else, if static wallpapers are set, then directly get the * wallpaper image */ wallpaperDrawable = wallpaperManager.getDrawable(); } return wallpaperDrawable; } 複製程式碼
但是問題還是沒有完全解決,背景不會動!每次呼叫loadThumbnail返回的圖片都是一樣的,因此猜想1只使用於靜態桌布。
以上。
巨人的肩膀
Andriod中Style/Theme原理以及Activity介面檔案選取過程淺析: blog.csdn.net/qinjuning/a…