Android Weekly Issue #428
Kotlin Flow Retry Operator with Exponential Backoff Delay
這是講協程Flow系列文章中的一篇.
對于重試的兩個運算子:
- retryWhen
- retry
retryWhen的使用:
.retryWhen { cause, attempt ->
if (cause is IOException && attempt < 3) {
delay(2000)
return@retryWhen true
} else {
return@retryWhen false
}
}
retry:
.retry(retries = 3) { cause ->
if (cause is IOException) {
delay(2000)
return@retry true
} else {
return@retry false
}
}
可以把時間指數延長:
viewModelScope.launch {
var currentDelay = 1000L
val delayFactor = 2
doLongRunningTask()
.flowOn(Dispatchers.Default)
.retry(retries = 3) { cause ->
if (cause is IOException) {
delay(currentDelay)
currentDelay = (currentDelay * delayFactor)
return@retry true
} else {
return@retry false
}
}
.catch {
// error
}
.collect {
// success
}
}
Fragments: Rebuilding the Internals
Fragment在Android 10已經廢棄, 現在不在framework中了, 只在AndroidX中有.
這個Fragment 1.3.0-alpha08版本的發布, 有一些關于FragmentManager內部狀態的重要更新.
解決了很多issue, 簡化了fragment的生命周期, 還提供了一個FragmentManager多個back stacks的支持.
核心就是這個FragmentStateManager類.
這個FragmentStateManager負責:
- 轉換Fragment的生命周期狀態.
- 跑影片和轉場.
- 處理延遲轉換.
Postponed fragments
關于狀態的確定, 有一個case是一個難點: postponed fragments.
這是一個以前就有的東西, 通常跟shared element transition影片有關系.
postponed fragment有兩個特點:
- view創建了, 但是不可見.
- lifecycle頂多到
STARTED.
只有呼叫這個方法: startPostponedEnterTransition()之后, fragment的transition才會跑, view會變成可見, fragment會移動到RESUMED.
所以有這個bug: Postponed Fragments leave the Fragments and FragmentManager in an inconsistent state bug.
這個issue相關聯的還有好幾個issues.
在容器層面解決問題
用一個SpecialEffectsController(以后名字可能會改)來處理所有影片轉場相關的東西.
這樣FragmentManager就被解放出來, 不需要處理postponed的邏輯, 而是交給了container, 這樣就避免了FragmentManager中狀態不一致的問題.
新的StateManager構架
原先: 一個FragmentManager總管所有.
現在: FragmentManager和各個FragmentStateManager的實體交流.
- The
FragmentManageronly has state that applies to all fragments. - The
FragmentStateManagermanages the state at the fragment level. - The
SpecialEffectsControllermanages the state at the container level.
總體
這個改動新發布, 實驗階段, 總體來說是應該沒有行為改變的.
如果有行為改變, 對你的程式造成了影響, 也可以暫時關閉(FragmentManager.enableNewStateManager(false)), 并且報告個issue.
A Framework For Speedy and Scalable Development Of Android UI Tests
講了一整套的測驗實踐.
沒有用Appium, 用的UI Automator和Espresso.
Basic Coroutine Level 1
Kotlin協程的概念.
Android Lint Framework — An Introduction
Android Lint的介紹.
創建一個Lint規則, 保證每個人都用專案自定義的ImageView, 而不是原生的ImageView.
具體做法:
- 首先從創建一個叫做
custom-lint的module. 需要依賴lint-api和lint-checks:
compileOnly "com.android.tools.lint:lint-api:$androidToolsVersion"
compileOnly "com.android.tools.lint:lint-checks:$androidToolsVersion"
這里用了compileOnly是因為不想lint API在runtime available.
- 之后創建自定義規則. 每個lint check的實作都叫一個detector. 需要繼承
Detector, 并且利用Scanners來做掃描. 報告錯誤需要定義Issue. 還可以創建LintFx, 作為quick fix.
class ImageViewUsageDetector : LayoutDetector() {
// Applicable elements
override fun visitElement(context: XmlContext, element: Element) {
context.report(
issue = ISSUE,
location = context.getElementLocation(element),
message = REPORT_MESSAGE,
quickfixData = https://www.cnblogs.com/mengdd/archive/2020/08/31/computeQuickFix()
)
}
private fun computeQuickFix(): LintFix {
return LintFix.create()
.replace().text(SdkConstants.IMAGE_VIEW)
.with(TINTED_IMAGE_VIEW)
.build()
}
// Issue, implementation, and other constants
}
- 然后把定義好的自定義規則注冊.
class Registry: IssueRegistry() {
override val issues: List<Issue>
get() = listOf(ImageViewUsageDetector.ISSUE)
override val api: Int = CURRENT_API
}
- 創建入口, 在
build.gradle檔案中:
// Configure jar to register our lint registry
jar {
manifest {
attributes("Lint-Registry-v2": "com.tintedimagelint.lint.Registry")
}
}
- 加上依賴和一些配置.
android {
// Configurations above
lintOptions {
lintConfig file('../analysis/lint/lint.xml')
htmlOutput file("$project.buildDir/reports/lint/lint-reports.html")
xmlOutput file("$project.buildDir/reports/lint/lint-reports.xml")
abortOnError false
}
//Configurations below
}
dependencies {
// Dependencies above
// Include custom lint module as a lintCheck
lintChecks project(":custom-lint")
// Dependencies below
}
Codelabs for new Android game technologies
關于Android Game新技術的Codelabs:
- 資源打包: Play Asset Delivery -> https://codelabs.developers.google.com/codelabs/unity-gamepad/#0
- 幀率和影像: Android Performance Tuner -> https://codelabs.developers.google.com/codelabs/android-performance-tuner-unity/#0
都是Unity的game.
Android Vitals - When did my app start?
系列文章之六, 我的app啥時候啟動的?
看個結論吧:
Here's how we can most accurately measure the app start time when monitoring cold start:
- Up to API 24: Use the class load time of a content provider.
- API 24 - API 28: Use
Process.getStartUptimeMillis(). - API 28 and beyond: Use
Process.getStartUptimeMillis()but filter out weird values (e.g. more than 1 min to get toApplication.onCreate()) and fallback to the timeContentProvider.onCreate()is called.
Comparing Three Dependency Injection Solutions
比較三種依賴注入的解決方案.
- 手寫方式.
- Koin.
- Dagger Hilt.
Avoiding memory leaks when using Data Binding and View Binding
使用Data Binding和View Binding的時候, 注意記憶體泄漏問題.
Google建議在Fragment中使用binding時, 要在onDestroyView中置為null:
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
有個博客中介紹的方法, 可以簡化成這樣:
private val binding: FragmentFirstBinding by viewBinding()
Fragment還有一個引數的構造, 可以傳入布局id:
class FirstFragment : Fragment(R.layout.fragment_first) {
private val binding: FragmentFirstBinding by viewBinding()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Any code we used to do in onCreateView can go here instead
}
}
冷知識: DataBinding實作了ViewBinding.
public abstract class ViewDataBinding extends BaseObservable implements ViewBinding
所以ViewBinding和DataBinding方法通用.
Anti-patterns of automated software testing
關于測驗的一些anti-patterns.
推薦閱讀.
Using bytecode analysis to find unused dependencies
關于這個庫: https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin的說明.
Code
- https://github.com/jmfayard/refreshVersions: 一個依賴版本管理的gradle插件.
后記
好久沒在博客園發過這個系列.
其實一直還有在更, 只不過寫得比較散亂隨意, 所以丟在了簡書:
https://www.jianshu.com/c/e51d4d597637
最近有點忙, 不太有時間寫博客, 積攢了好多話題都是沒有完成的.
看博客兩個月沒更了, 拿這篇刷一下存在感.
是想多寫點真正厲害有價值的原創的.
先韜光養晦, 積累一下.
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/106767.html
標籤:其他
上一篇:Android連載17-使用SharedPreference來保存以及讀取資料
下一篇:從APP的啟動說起
