本系列教程指引:
1. 前情回顧
在上一篇文章 Cocos Creator Android 原生啟動優化系列 1 —— 黑屏原因分析 中,我們大概分析了一下 Android 黑屏的原因,共有3個階段,主要在前兩個原生階段
第三階段黑屏時長,實際上會和游戲首場景復雜度掛鉤。越簡單的首場景加載越快。本篇我們著重解決Android原生前兩個階段的黑屏問題。因此為了減少可變因子,我們直接以一個簡單場景作為第三階段要加載場景。
然后構建一個 Android 工程出來,這里特別需要注意一點:因為我們是探索測試,所以強烈建議采用基于 default
模板去進行操作(default 和 link 的區別可以查看官方文檔)
OK,準備好了,Let's go!
2. 基于 Android Theme 做體驗優化
一種簡單的解決方案就是直接設置 AppActivity 的 主題Theme(官方文檔介紹,需翻墻),以下為摘自文檔的兩段說明,方便大家簡單理解。
樣式是指為 View 或窗口指定外觀和格式的屬性集合。樣式可以指定高度、填充、字體顏色、字號、背景色等許多屬性。 樣式是在與指定布局的 XML 不同的 XML 資源中進行定義。
主題是指對整個 Activity 或應用而不是對單個 View(如上例所示)應用的樣式。 以主題形式應用樣式時,Activity 或應用中的每個視圖都將應用其支持的每個樣式屬性。 例如,您可以 Activity 主題形式應用同一
CodeFont
樣式,之后該 Activity 內的所有文本都將具有綠色固定寬度字體。
OK,那么現在我們試試將我們的 AppActivity 主題修改一下。
比如:設置游戲開屏顏色為其他顏色,又或者將一張游戲圖片作為游戲開屏背景
2.1 設置其他顏色作為開屏背景
- 創建一個自定義顏色(
app/res/values/colors.xml
)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 這里我們取主場景的背景色 -->
<color name="splashBg">#DA8020</color>
</resources>
- 創建一個主題 (
app/res/values/colors.xml
)
<?xml version="1.0" encoding="utf-8"?>
<resources >
<!--這里為繼承 CCC 默認說采用的主題 Theme.NoTitleBar.Fullscreen -->
<style name="SplashStyle"
parent="@android:style/Theme.NoTitleBar.Fullscreen" >
<!--自定義主題背景為我們剛剛創建的顏色-->
<item name="android:windowBackground" >@color/splashBg</item >
<!--令主題無標題-->
<item name="android:windowNoTitle" >true</item >
</style >
</resources >
- 修改主 Activity 的主題為我們剛剛創建的主題(
app/AndroidManifest.xml
)
<activity
android:name="org.cocos2dx.javascript.AppActivity"
...
android:theme="@style/SplashStyle"
...
>
...
</activity >
整體修改大概如下:
現在我們跑一下
嗯?一開始看起來比黑屏好多了,但是之后又黑屏了,然后才進入到我們的主場景,那么這是為什么呢?這里面發生了什么呢?這里我們需要再次看回黑屏的三個階段
從上面這個圖,我們不難得到答案。
- 首先我們設置的主題在第一個階段生效了,所以一打開游戲就是我們期望的背景顏色
- 然后一段時間之后,進入到第二個階段,觸發了
setContentView
,因此 AppActivity 擁有了 ContentView 了,從而會擋住主題,而又因為之前我們分析過,這里的 setContentView 是沒有背景的,所以就出現黑屏了 - 然后黑屏一直持續到我們的主場景加載成功,最后出現我們的主場景
OK,所有現象,完美符合代碼運行(多看源碼)。
那么這里的黑屏我們又該怎么解決呢?
在不大改源碼的情況下,我們可以這樣子來:
- 在第二個階段再次添加新的 純色背景ImageView 放到最上層,那么第二個階段也就是我們期望的顏色了
- 然后在第三階段結束,也就是我們主場景加載后,我們在通過 JSB 通知原生 Activity 該隱藏這個 純色背景ImageView 了
Android 原生代碼如下:
public class AppActivity extends Cocos2dxActivity {
private static Cocos2dxActivity sCocos2dxActivity;
private static ImageView sSplashBgImageView = null;
private static void showSplash() {
sSplashBgImageView = new ImageView(sCocos2dxActivity);
sSplashBgImageView.setBackgroundColor(
sCocos2dxActivity.getResources().getColor(R.color.splashBg)
);
sSplashBgImageView.setScaleType(ImageView.ScaleType.FIT_XY);
sCocos2dxActivity.addContentView(sSplashBgImageView,
new WindowManager.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
);
}
/**
* 這是給 CC JS 調用的隱藏原生開屏背景的方法
*/
public static void hideSplash() {
sCocos2dxActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (sSplashBgImageView != null) {
sSplashBgImageView.setVisibility(View.GONE);
}
}
});
}
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// DO OTHER INITIALIZATION BELOW
SDKWrapper.getInstance().init(this);
// 第一步:在第二階段加入我們的背景View
sCocos2dxActivity = this;
showSplash();
}
...
}
主場景掛一個腳本:
@ccclass
export default class MainSceneCtrl extends cc.Component {
start() {
// 第二步:場景加載之后,隱藏原生純色背景View
// 這里延遲1秒是為了更好的體驗,實際可以不用
this.scheduleOnce(() => {
this._hideNativeSplash();
}, 1);
}
private _hideNativeSplash() {
if (CC_JSB) {
if (cc.sys.os == cc.sys.OS_ANDROID) {
// 反射調用原生的隱藏方法
jsb.reflection.callStaticMethod(
"org/cocos2dx/javascript/AppActivity",
"hideSplash",
"()V"
);
}
}
}
}
那么,再跑一下?
現在,我們成功地把黑屏給切換為一個有顏色的啟動頁了,原生和JS完美切換,看起來比黑屏好多了
2.2 設置圖片作為開屏背景
當然,上一章,我們更多是一種驗證和測試,而事實上,這個方法可行,并且 我們實現了使用任意純色背景作為啟動頁!
但實際我們更有可能在啟動頁放的是一個帶背景的游戲LOGO、游戲插圖等圖片,比如這樣子:
那么這個又應該怎么實現呢?其實有了上一章基礎之后,我們只需要極小的改動就可以實現了
- 首先我們簡單修改下游戲場景,恩,其實就是一個圖片背景加點Label
- 然后,第一階段黑屏處理:
2.1 將啟動頁圖片加入到Android工程的 drawable 中
2.2 然后修改我們剛剛的主題(或者新建一個主題)背景為啟動頁圖片
2.3 設置app/AndroidManifest.xml
中的 Activity 主題為第2步的主題
theme - 最后,第二階段黑屏處理:
3.1 基于上一章代碼,修改ImageView的背景顏色為背景圖片即可
-- sSplashBgImageView.setBackgroundColor(
-- sCocos2dxActivity.getResources().getColor(R.color.splash_bg)
-- );
++ sSplashBgImageView.setImageResource(R.drawable.splash);
OK,恭喜你又收獲一個技能。
當然,這里還有其他一些問題需要關注的。比如:
我們一直在設置的主題背景,如果是純色背景,那么很容易理解它的填充方式,就是全屏填充。但是,如果背景換成了圖片,那么圖片是怎么填充到屏幕的呢?拉伸?縮放?九宮格?保持尺寸不變?
要知道,我們上述步驟,本質上,給第一階段黑屏設置一個(主題)背景,給第二階段設置一個背景,而如果這兩個階段的背景都是圖片(第三階段就是場景了,不同游戲不一致,這里我們不討論),那么我們假設一種情況如下:
- 第一個階段是 拉伸圖片 以達到全屏
- 第二個階段是 縮放圖片 以達到全屏
那么顯然在部分分辨率上是達不到上面我們圖示那樣子的無縫原生與JS切換的UI體驗的。那么圖示為什么又那么完美呢?其實只是因為啟動頁圖寬高比和手機分辨率寬高比恰好一致,要是不一致就會很難看
所以,理解不同階段的圖片填充方式,對于這個啟動過程無縫切換很重要。
可以確定的是,第二階段,其實我們可以隨便控制填充方式,比如:例子代碼中我們用的是 FIT_XY
,當然還有其他很多 ImageView.ScaleType。但遺憾的是,我們第一階段的 windowBackground
是采用拉伸圖片的方式去適配的,那么只用一張固定尺寸的圖片,那么肯定會出現在部分手機上有拉伸的問題。
此時,這種設置單一全屏圖片的方式已經存在缺陷了,所以這也有可能是大部分游戲開屏沒有全屏圖片啟動頁的一個成因
2.3 設置 Slogan + 背景色 作為啟動頁
那么大部分游戲的啟動頁長什么樣子呢?一般都是一個 Slogan + 背景色 作為啟動頁,比如這樣子:
同樣在上面章節的基礎上,我們只需要改動幾個地方就可以實現了
第一階段黑屏處理:
- 將你的 Slogan 圖片加入到Android工程的 drawable 中(假設文件名字為
splash_slogan_small.png
),同時建立一個背景色
<?xml version="1.0" encoding="utf-8"?>
<resources >
...
<color name="splash_slogan_bg" >#FFCC00</color >
</resources >
- 創建一個DrawableXML文件(如:
app/res/drawable/splash_slogan_with_bg.xml
),然后使用 Android 的圖層列表 LayerList【需翻墻】 生成一個 Drawable,這個 Drawable 由一個純色的矩形作為背景色,以及在這之上放置一個 Slogan 的 Bitmap 圖片
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!--矩形 黃色作為背景色-->
<item >
<shape android:shape="rectangle" >
<solid android:color="@color/splash_slogan_bg" />
</shape >
</item >
<!--單獨的slogan圖片居中顯示-->
<item >
<bitmap
android:gravity="center"
android:src="@drawable/splash_slogan_small" />
</item >
</layer-list >
- 修改我們剛剛的主題(或者新建一個主題)背景為剛剛的圖層列表文件
- 設置
app/AndroidManifest.xml
中的 Activity 主題為第3步的主題
第一階段操作結果預覽:
第二階段黑屏處理:
- 基于上一章代碼,修改ImageView的背景圖片為我們剛剛創建的Drawable
-- sSplashBgImageView.setImageResource(R.drawable.splash);
++ sSplashBgImageView.setImageResource(R.drawable.splash_slogan_with_bg);
OK,大功告成。
現在我們來盤點一下,比如:
為什么我們要用 Android 的圖層列表 LayerList【需翻墻】 ?
這是因為比起上一章單純的圖片可能存在在部分機器上出現拉伸的問題,使用 LayerList 能更好適配,同時比起單純一張圖片,LayerList也更加做多層級組合(背景+Slogan+...)的啟動頁。
如果你想在此基礎上實現貼底部的 Slogan 其實也是可以的,但是篇幅問題,我這里就不在詳述,可以詳細閱讀 Android 的圖層列表 LayerList【需翻墻】 。
3. 總結
當然,這個系列沒完,這篇只是在不改源碼的基礎上提升UI體驗,實際并沒有給減少啟動時間,下一篇我們會嘗試修改底層源碼以減少啟動時長。
4. 題外話
寫這個系列的文章的時候,我可能更多地傾向于 fishing
。
Not only the fish, but fishing!
我不知道我是否還能堅持寫這類型文章。因為每一篇都需要開項目驗證,進行大量錄屏,截圖,組織思路,寫過程,畫圖,摘錄官方文獻等等,一套操作下來,十分耗時,就像這個系列,我斷斷續續寫了兩個多月。當然,這也有可能是因為我懶 ╮( ̄▽ ̄)╭ 。
5. 參考資料
- https://forum.cocos.com/t/topic/52465
- https://forum.cocos.com/t/cocos-ios/56599
- https://forum.cocos.com/t/loadscene/51364
- Android 中樣式 Style 與主題 Theme 的介紹【官網】【需翻墻】
- Android LayerList【官網】【需翻墻】
- Splash適配解決啟動圖拉伸的問題
- Android啟動頁優化,去黑屏實現秒啟動
- Android性能優化之啟動優化
- Android 性能優化—— 啟動優化提升60%
本系列教程指引: