Android實戰進階 - 啟動頁

場景:當啟動頁處于倒計時階段,用戶將其切換為后臺的多任務卡片狀態,倒計時會繼續執行,直到最后執行相關邏輯(一般會跳轉引導頁、進入主頁等)
期望:而綜合市場來看,一般我們期望的是當其處于多卡片狀態,需要暫停倒計時,只有恢復前臺狀態后繼續計時。而不是重新計時或已計時完畢

關于啟動頁的一些基礎內容,之前已經做過總結了,此篇主要用于解決上方提到的業務場景

  • Android進階之路 - Splash、Welcome歡迎頁面簡單實現(當年入門時候寫的,Demo級入門示例)
  • Android進階之路 - 快速實現啟動頁(基礎版,可用于實戰項目)

業務實戰

    • 項目實戰
    • AI提供方案
      • CountDownTimer + 生命周期感知
      • ViewModel + LiveData

項目實戰

以下是我從項目中剝離的偽代碼,主要用于解決不同生命周期,計時器帶來的影響,核心思想有以下幾點

  • 倒計時長根據當前計時器的變化而實時變更
  • 當處于onPause(后臺)時,取消計時器
  • 當處于onResume(前臺)時,將計時器剩余時長傳入計時器中

因為我們不考慮橫豎屏切換場景,所以在 AndroidMainfest 中直接為啟動頁 Activityandroid:screenOrientation="portrait"

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

實現方式

package cn.xxxximport android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
import me.jessyan.autosize.internal.CancelAdapt
import timber.log.Timber
import javax.inject.Inject@SuppressLint("CustomSplashScreen")
@AndroidEntryPoint
class SplashActivity : AppCompatActivity(), CancelAdapt {lateinit var tvJump: TextView// 倒計時長var remainingTimeInMillis: Long? = null//計時器private var countDownTimer: CountDownTimer? = null@SuppressLint("SourceLockedOrientationActivity", "MissingInflatedId")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_splash_l)//右上角跳過的視圖tvJump = findViewById<TextView>(R.id.tv_jump)//初始化為計時器時間remainingTimeInMillis = 5 * 1000//跳過邏輯tvJump.onClick {countDownTimer?.cancel()next()}}override fun onResume() {super.onResume()//之所以寫是因為在項目里廣告時間是后臺返回,如果只是單純固定時長可去除該判斷if (!remainingTimeInMillis.isNull()) {// Activity回到前臺時,檢查剩余時間if ((remainingTimeInMillis ?: 0) <= 0) {// 如果時間已經耗盡,直接跳轉next()} else {// 如果時間還有剩余,重新啟動一個計時器,從剩余時間開始startTimer(remainingTimeInMillis ?: 0)}}}override fun onPause() {super.onPause()// Activity進入后臺時,立即取消計時器(防止onFinish在后臺被調用),remainingTimeInMillis還保存著最新的剩余時間countDownTimer?.cancel()}//計時器private fun startTimer(time: Long) {countDownTimer?.cancel()countDownTimer = object : CountDownTimer(time, 1000) {override fun onTick(millisUntilFinished: Long) {//實時更新剩余的倒計時長remainingTimeInMillis = millisUntilFinishedmainHandler.post { tvJump.text = "${millisUntilFinished / 1000 + 1}s跳過" }}override fun onFinish() {//倒計時結束,進入對應邏輯next()}}.start()}override fun onDestroy() {super.onDestroy()countDownTimer?.cancel()countDownTimer = null}private fun next() {//可自行根據業務場景,決定跳轉邏輯	// 以下為項目偽代碼:判斷是否首次登錄,運行過幫助引導val landingVersion = SPUtils.AppSP().get(ConstValue.VER_IS_THIS_VERSION_OPEN_BEFORE, 0) as? Int?if ((landingVersion ?: 0) >= 4) {RouterPath.APP_MAIN_ACT //首頁} else {RouterPath.MAIN_LANDING_ACT //引導頁}.also {startRouterAndFinish(it) { putBoolean("firstStart", true) }}}
}

activity_splash_l

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"android:orientation="vertical"tools:ignore="MissingDefaultResource"><ImageViewandroid:id="@+id/background_type_1"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitXY"android:src="@drawable/drawable_app_launch"android:visibility="visible" /><RelativeLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="right"android:layout_marginTop="62dp"android:layout_marginEnd="15dp"android:background="@drawable/shape_splash_btn_jump_bg"android:paddingHorizontal="12dp"android:paddingVertical="4dp"><TextViewandroid:id="@+id/tv_jump"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="跳過"android:textColor="#FFFFFF"android:textSize="12dp" /></RelativeLayout>
</FrameLayout>

AI提供方案

自從使用AI后,覺得很多基礎性的知識沒有了記Blog的必要,更多的可能還行記錄項目中遇到的問題

CountDownTimer + 生命周期感知

考慮到了橫豎屏場景,兼容場景更多一些

核心要點

  1. onPause()中取消計時器:阻止它在后臺觸發 onFinish()跳轉。
  2. 保存剩余時間:在 onTick()中持續更新 remainingTimeInMillis變量。
  3. onResume()中恢復計時:根據保存的剩余時間重新開始計時。如果時間已到,直接跳轉。
  4. 處理配置變更:通過 onSaveInstanceState保存數據,防止屏幕旋轉等問題。
class CountdownActivity : AppCompatActivity() {private var countDownTimer: CountDownTimer? = nullprivate var remainingTimeInMillis: Long = 10000 // 總計時時間,例如10秒private val totalTimeInMillis: Long = 10000 // 保存總時間用于恢復override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_countdown)// 從保存的狀態中恢復時間(防止旋轉屏幕等配置變更)if (savedInstanceState != null) {remainingTimeInMillis = savedInstanceState.getLong("REMAINING_TIME", totalTimeInMillis)}startCountdown(remainingTimeInMillis)}override fun onSaveInstanceState(outState: Bundle) {super.onSaveInstanceState(outState)// 保存當前剩余時間,防止配置變更(如屏幕旋轉)導致時間重置outState.putLong("REMAINING_TIME", remainingTimeInMillis)}private fun startCountdown(millisInFuture: Long) {// 每次啟動新計時器前,取消舊的countDownTimer?.cancel()countDownTimer = object : CountDownTimer(millisInFuture, 1000) {override fun onTick(millisUntilFinished: Long) {// 更新UI,顯示剩余時間remainingTimeInMillis = millisUntilFinishedval seconds = millisUntilFinished / 1000textView_countdown.text = "剩余時間: ${seconds}秒"}override fun onFinish() {// 只有在Activity處于前臺時,才執行跳轉邏輯proceedToNextStep()}}.start()}private fun proceedToNextStep() {// 執行你的下一步操作,例如跳轉頁面val intent = Intent(this, NextActivity::class.java)startActivity(intent)finish()}override fun onPause() {super.onPause()// Activity進入后臺時,立即取消計時器(防止onFinish在后臺被調用)countDownTimer?.cancel()// 注意:這里我們只是取消了計時器,并沒有改變remainingTimeInMillis的值// 所以remainingTimeInMillis還保存著最新的剩余時間}override fun onResume() {super.onResume()// Activity回到前臺時,檢查剩余時間if (remainingTimeInMillis <= 0) {// 如果時間已經耗盡,直接跳轉proceedToNextStep()} else {// 如果時間還有剩余,重新啟動一個計時器,從剩余時間開始startCountdown(remainingTimeInMillis)}}override fun onDestroy() {super.onDestroy()// 徹底銷毀Activity時,釋放計時器資源countDownTimer?.cancel()}
}

ViewModel + LiveData

符合當下主流框架、組件,適用性、兼容性高,但是對于未使用過的朋友,需要一點時間學下組件

Android Architecture Components 架構組件

  • 組件化之路 - Lifecycle一知半解
  • 組件化之路 - LiveData一知半解
  • 組件化之路 - ViewModel一知半解
  • 組件化之路 - LiveData + ViewModel一知半解

優勢

  • 生命周期感知: ViewModel獨立于UI生命周期,配置變更時數據不會丟失。
  • 關注點分離:計時邏輯在 ViewModel中,UI控制只在Activity中。
  • 更健壯:使用 Coroutines處理后臺任務,更加現代和安全。

創建 ViewModel

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launchclass CountdownViewModel : ViewModel() {private val _remainingTime = MutableLiveData<Long>()val remainingTime: LiveData<Long> = _remainingTimeprivate val _countdownFinished = MutableLiveData<Boolean>()val countdownFinished: LiveData<Boolean> = _countdownFinishedprivate var countdownJob: Job? = nullprivate var initialDuration: Long = 0Lfun startCountdown(duration: Long) {initialDuration = duration_remainingTime.value = duration_countdownFinished.value = falsecountdownJob?.cancel() // 取消之前的任務countdownJob = viewModelScope.launch {var timeLeft = durationwhile (timeLeft > 0 && isActive) {delay(1000)timeLeft -= 1000_remainingTime.postValue(timeLeft) // 使用postValue確保在主線程更新}if (isActive && timeLeft <= 0) {_countdownFinished.postValue(true)}}}fun pauseCountdown() {countdownJob?.cancel()}// 獲取當前剩余時間,用于在UI層判斷fun getCurrentTime(): Long = _remainingTime.value ?: initialDurationoverride fun onCleared() {super.onCleared()pauseCountdown()}
}

在 Activity/Fragment 中使用 ViewModel

class CountdownActivity : AppCompatActivity() {private lateinit var viewModel: CountdownViewModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_countdown)// 初始化ViewModelviewModel = ViewModelProvider(this).get(CountdownViewModel::class.java)// 觀察剩余時間并更新UIviewModel.remainingTime.observe(this) { timeMillis ->val seconds = timeMillis / 1000textView_countdown.text = "剩余時間: ${seconds}秒"}// 觀察倒計時是否結束viewModel.countdownFinished.observe(this) { isFinished ->if (isFinished) {proceedToNextStep()}}// 如果是第一次創建,開始計時if (savedInstanceState == null) {viewModel.startCountdown(10000)}}private fun proceedToNextStep() {val intent = Intent(this, NextActivity::class.java)startActivity(intent)finish()}override fun onPause() {super.onPause()// 進入后臺時暫停計時viewModel.pauseCountdown()}override fun onResume() {super.onResume()val currentTime = viewModel.getCurrentTime()if (currentTime <= 0) {// 如果ViewModel中記錄的時間已經用完,直接跳轉proceedToNextStep()} else {// 否則,重新開始計時(從剩余時間開始)viewModel.startCountdown(currentTime)}}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/96186.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/96186.shtml
英文地址,請注明出處:http://en.pswp.cn/web/96186.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

無標記點動捕技術:重塑展廳展館的沉浸式數字交互新時代

在元宇宙浪潮的持續推進下&#xff0c;虛擬數字人正逐漸成為連接虛實世界的重要媒介。在展廳展館中&#xff0c;數字人不僅能夠扮演導覽員、講解員角色&#xff0c;更可通過情感化交互提升參觀體驗&#xff0c;使文化傳播更具感染力和沉浸感。虛擬人的引入&#xff0c;為傳統展…

輕松Linux-7.Ext系列文件系統

天朗氣清&#xff0c;惠風和煦&#xff0c;今日無事&#xff0c;遂來更新。 1.概述 總所周知&#xff0c;我們存的數據都是在一個叫硬盤的東西里面&#xff0c;這個硬盤又像個黑盒&#xff0c;這章就來簡單解析一下Linux中文件系統。 現在我們用的大都是固態硬盤&#xff0c;…

Matlab機器人工具箱使用4 蒙特卡洛法繪制工作區間

原理&#xff1a;利用rand隨機數&#xff0c;給各個關節設置隨機關節變量&#xff0c;通過正運動學得到末端位姿變換矩陣&#xff0c;然后利用變換矩陣2三維坐標標記出末端坐標&#xff0c;迭代多次就可以構成點云。教程視頻&#xff1a;【MATLAB機器人工具箱10.4 機械臂仿真教…

【項目】在AUTODL上使用langchain實現《紅樓夢》知識圖譜和RAG混合檢索(三)知識圖譜和路由部分

首先在數據集 - 開放知識圖譜下載紅樓夢的知識圖譜&#xff0c;這個網站上有各種各樣的知識圖譜&#xff0c;可以挑你感興趣的做( ? ?ω?? ) 這個知識圖譜的作者們已經將三元組抽取出來了&#xff0c;我們可以直接用&#xff0c;如果你對三元組是如何生成的感興趣&#xf…

pycharm 最新版上一次編輯位置

2025nipycharm方法一&#xff1a;用快捷鍵&#xff08;最方便&#xff09;跳回上一次編輯位置&#xff1a;Windows/Linux: Ctrl Alt ←macOS: ? Option ←跳到前一次位置&#xff1a;Windows/Linux: Ctrl Alt →macOS: ? Option →方法二&#xff1a;顯示工具欄按鈕在…

前端性能監控與優化:從 Lighthouse 到 APM

在當今競爭激烈的數字環境中&#xff0c;用戶對Web應用性能的要求日益提高。一個緩慢或響應遲鈍的應用不僅會流失用戶&#xff0c;更可能損害品牌形象和商業價值。因此&#xff0c;前端性能的監控與優化已成為前端開發不可或缺的關鍵環節。本文將深入探討從基礎的性能評估工具L…

TC_Motion多軸運動-電子齒輪

目錄 電子齒輪 【基本概念】 【應用示例】 【開發總結】 END 電子齒輪 【基本概念】 定義:通過軟件方法實現機械齒輪的速比調節功能(兩個軸成線性比例旋轉) 優點 免維護,告別機械損耗 易調節,任意修改齒輪比 精度高,無機械背隙 應用場景 多臺電機拖動同一負載,要求多臺…

CentOS 7 下載教程

訪問阿里云鏡像站 阿里巴巴開源鏡像站 選擇centos 點這個 選擇7版本 進入isos目錄 點這個 選擇這個版本 因為這個鏡像的日期更新 推薦下載 DVD 版&#xff1a;包含完整系統和常用軟件&#xff0c;無需額外聯網安裝組件Minimal 版&#xff1a;精簡版&#xff0c;僅包含基礎系…

MAC在home下新建文件夾報錯“mkdir: test: Operation not supported”

在Mac電腦中&#xff0c;home文件夾下不能直接mkdir&#xff0c;sudo 也還是不行&#xff0c;提示“mkdir: test: Operation not supported”。網上找的解決方案不好使&#xff0c;因為沒有關閉系統完整性保護關閉系統完整性保護查看SIP當前的狀態csrutil status如果是開啟狀態…

交叉導軌從測試儀到工作臺的精密運動控制

在精密儀器領域微米級的運動精度與納米級的穩定性往往是決定設備性能上限的核心指標。而支撐這一技術鴻溝跨越的&#xff0c;往往隱匿于機械結構的“毫厘之間”——交叉導軌。以下是其在不同精密儀器中的具體應用&#xff1a;光學測試儀&#xff1a;光學測試儀主要用于各種高精…

內網穿透的應用-Navidrome與cpolar本地搭建跨網絡訪問的云音樂服務器

文章目錄前言1. 安裝Docker2. 創建并啟動Navidrome容器3. 公網遠程訪問本地Navidrome3.1 內網穿透工具安裝3.2 創建遠程連接公網地址3.3 使用固定公網地址遠程訪問前言 音樂收藏存在平臺版權限制、音質壓縮和訪問不便等問題。Navidrome 開源音樂服務器與 cpolar 內網穿透服務的…

FastAPI 訪問不了API文檔或配置不生效的解決方法

FastAPI中文教程 本文背景 FastAPI框架自帶交互式api文檔,通過路由/docs或者/redoc 訪問&#xff0c;但是FastAPI 的文檔界面&#xff08;如 /docs 和 /redoc&#xff09;依賴于外部的 JavaScript 和 CSS 庫&#xff0c;如果項目部署環境網絡不佳或者無法訪問外網的時候&…

IAR 集成開發環境入門指南:字體設置與調試實戰

一、IAR 的基本使用教程1. IAR 顏色字體大小設置打開設置路徑&#xff1a;點擊頂部菜單欄 Tools → 選擇 Options&#xff0c;打開 IDE 配置窗口。進入字體顏色設置界面&#xff1a;在彈出的 “IDE Options” 窗口中&#xff0c;雙擊展開 Editor 選項&#xff0c;然后點擊 Colo…

10:00開始面試,10:06就出來了,問的問題有點變態。。。

從小廠出來&#xff0c;沒想到在另一家公司又寄了。到這家公司開始上班&#xff0c;加班是每天必不可少的&#xff0c;看在錢給的比較多的份上&#xff0c;就不太計較了。沒想到8月一紙通知&#xff0c;所有人不準加班&#xff0c;加班費不僅沒有了&#xff0c;薪資還要降40%,這…

Flink 狀態管理的核心能力

我們來看一個復雜的實際案例&#xff1a;阿里巴巴菜鳥的實時物流追蹤系統。 該系統處理來自多個電商平臺&#xff08;天貓、淘寶、速賣通&#xff09;的訂單包裹&#xff0c;通過一個復雜的處理流程&#xff1a; 合并與去重&#xff1a;通過聚合操作將不同來源的訂單合并并去重…

基于go語言的云原生TodoList Demo 項目,驗證云原生核心特性

以下是一個基于 Go 語言 的云原生 TodoList Demo 項目&#xff0c;涵蓋 容器化、Kubernetes 編排、CI/CD、可觀測性、彈性擴縮容 等核心云原生特性&#xff0c;代碼簡潔且附詳細操作指南&#xff0c;適合入門學習。項目概覽 目標&#xff1a;實現一個支持增刪改查&#xff08;C…

手機能看、投屏 / 車機不能看與反向鏈接驗證類似嗎?

有一定關聯&#xff0c;但兩者的技術邏輯并非完全等同 ——“手機能看、投屏 / 車機不能看” 的核心原因更復雜&#xff0c;反向鏈接驗證是其中一種可能的限制手段&#xff0c;但不是唯一甚至不是最主要的手段。要理清這個問題&#xff0c;需要先拆解 “投屏 / 車機播放受限” …

25年9月通信基礎知識補充1:NTN-TDL信道建模matlab代碼(satellite-communications toolbox學習)

看文獻過程中不斷發現有太多不懂的基礎知識&#xff0c;故長期更新這類blog不斷補充在這過程中學到的知識。由于這些內容與我的研究方向并不一定強相關&#xff0c;故記錄不會很深入請見諒。 【通信基礎知識補充10】25年9月通信基礎知識補充1&#xff1a;NTN-TDL信道建模matlab…

洛谷P3370 【模板】字符串哈希 (哈希表)詳解

題目如下&#xff1a;&#xff08;注&#xff1a;解此題我只需左手一根指頭&#xff0c;哈哈哈哈哈哈哈&#xff09;注意&#xff0c;哈希表的好處是能大幅度減少尋找遍歷的時間可能有人不理解哈希值&#xff0c; 這里哈希的模的值一般得是比較大的質數&#xff0c;如標準的100…

光子芯片驅動的胰腺癌早期檢測:基于光學子空間神經網絡的高效分割方法(未做完)

光子芯片驅動的胰腺癌早期檢測:基于光學子空間神經網絡的高效分割方法 1 論文核心概念 本文提出了一種基于集成光子芯片的光學子空間神經網絡(Optical Subspace Neural Network, OSNN),用于胰腺癌的早期檢測與圖像分割。其核心思想是利用光子芯片的高并行性、低延遲和低能…