首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Android 啟動頁閃屏實現:Handler 延遲與 CountDownTimer 兩種方案對比

     編輯:Java 相關知識

前言

啟動頁是 App 給用戶的第一印象,核心需求:展示品牌圖、預載關鍵資源、固定停留時間後跳轉主頁。本文對比 Handler.postDelayed 與 CountDownTimer 兩種主流實現,分析生命週期坑點、記憶體洩漏防範與最佳實踐。

方案一:Handler.postDelayed(最簡單、最常用)

public class SplashActivity extends AppCompatActivity {
    private static final long DELAY_MS = 3000;
    private Handler handler = new Handler(Looper.getMainLooper());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 全屏無標題
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                             WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_splash);

        handler.postDelayed(() -> {
            // 檢查 Activity 是否仍有效(防止已銷毀後回調)
            if (!isFinishing() && !isDestroyed()) {
                startActivity(new Intent(this, MainActivity.class));
                finish(); // 關鍵:防止返回鍵回到啟動頁
            }
        }, DELAY_MS);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacksAndMessages(null); // 防洩漏
    }
}

優點:代碼極簡、單次延遲執行語義清晰。
缺點:無倒計時顯示、無法中途取消邏輯複雜。

方案二:CountDownTimer(支援倒計時 UI 更新)

public class SplashActivity extends AppCompatActivity {
    private TextView tvCountdown;
    private MyCountDownTimer timer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        tvCountdown = findViewById(R.id.tv_countdown);

        // 總時長 3000ms,每 1000ms 回調一次
        timer = new MyCountDownTimer(3000, 1000);
        timer.start();
    }

    private class MyCountDownTimer extends CountDownTimer {
        public MyCountDownTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            // UI 線程安全更新
            tvCountdown.setText("倒計時 " + (millisUntilFinished / 1000) + " 秒");
        }

        @Override
        public void onFinish() {
            if (!isFinishing() && !isDestroyed()) {
                tvCountdown.setText("正在進入...");
                startActivity(new Intent(SplashActivity.this, MainActivity.class));
                finish();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (timer != null) timer.cancel(); // 必須取消,否則洩漏
    }
}

優點:內建倒計時邏輯、自動處理間隔回調、cancel() 一鍵清理。
缺點:內部仍用 Handler,本質相同。

核心坑點與避坑指南

問題後果解決
不檢查 isFinishing()用戶快速返回/切換,Activity 已銷毀卻仍跳轉回調前必加 if (!isFinishing() && !isDestroyed())
不調用 finish()主頁按返回鍵會回到啟動頁,體驗極差跳轉後立即 finish()
Handler/Timer 未清理記憶體洩漏,延遲任務持有 Activity 引用onDestroy 中 removeCallbacksAndMessages / cancel()
主題閃爍啟動瞬間顯示默認主題再切換Manifest 設置 android:theme="@style/SplashTheme"
初始化耗時過長阻塞主線程ANR、啟動頁白屏耗時操作放入 WorkManager/協程/線程池異步執行

進階:啟動頁主題預加載(消除白屏/閃爍)

1. 定義專用主題

<!-- res/values/styles.xml -->
<style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/splash_bg</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowContentOverlay">@null</item>
</style>

2. splash_bg.xml(LayerDrawable 疊加 Logo)

<layer-list xmlns:android="https://schemas.android.com/apk/res/android">
    <item android:drawable="@color/brand_primary"/>
    <item>
        <bitmap android:gravity="center" android:src="@drawable/ic_logo"/>
    </item>
</layer-list>

3. Manifest 直接指定主題

<activity
    android:name=".SplashActivity"
    android:theme="@style/SplashTheme"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

效果:系統啟動 Activity 前即顯示圖片,零代碼、零延遲渲染,完美解決白屏。

現代寫法:Kotlin 協程 + ViewBinding

@AndroidEntryPoint
class SplashActivity : AppCompatActivity() {
    private lateinit var binding: ActivitySplashBinding
    private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivitySplashBinding.inflate(layoutInflater)
        setContentView(binding.root)

        scope.launch {
            // 並行預載:數據庫、配置、Token 刷新
            val initDeferred = async(Dispatchers.IO) { Repository.initialize() }
            val delayDeferred = async { delay(2000) } // 最小停留 2s

            delayDeferred.await() // 等待最小時間
            initDeferred.await()  // 等待初始化完成

            if (!isFinishing && !isDestroyed) {
                startActivity(Intent(this, MainActivity::class.java))
                finish()
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        scope.cancel()
    }
}

優勢:結構化並發、自動生命週期綁定、異常傳播、易測試。

總結

  • 簡單停留:Handler.postDelayed + finish() + 生命週期檢查
  • 需倒計時 UI:CountDownTimer + cancel()
  • 消除白屏:Manifest 靜態主題 windowBackground(最關鍵優化)
  • 現代專案:Kotlin 協程並行初始化,替代舊式 Handler/Timer
  • 鐵律:跳轉必 finish(),銷毀必清理回調,回調必檢查存活
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved