前言
通過本文,你可以 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…