Android Weekly Notes #483

Android Weekly Issue #483

Hands on Jetpack AppSearch

Android 12的新feature: App Search.

MAD Skills series: Hilt under the hood

在Hilt中, 三個(gè)最重要的注解:

  • @AndroidEntryPoint.
  • @InstallIn.
  • @HiltAndroidApp.

解釋hilt是如何工作的.

對(duì)多個(gè)module的Classpath aggregation有圖片說(shuō)明.

Effective Kotlin Item 49: Consider using inline value classes

Kotlin 1.3是inline class, 1.5改為value class, 作為更大的功能點(diǎn).

inline class Name(private val value: String) {
    // ...
}

這段代碼編譯后變?yōu)?

// Code
val name: Name = Name("Marcin")

// During compilation replaced with code similar to:
val name: String = "Marcin"

方法都會(huì)被作為靜態(tài)方法:

@JvmInline
value class Name(private val value: String) {
    // ...

    fun greet() {
        print("Hello, I am $value")
    }
}

// Code
val name: Name = Name("Marcin")
name.greet()

// During compilation replaced with code similar to:
val name: String = "Marcin"
Name.`greet-impl`(name)

兩種流行的使用場(chǎng)景:

  • To indicate a unit of measure. 測(cè)量單位. 舉例: 時(shí)間單位.
@JvmInline
value class Minutes(val minutes: Int) {
    fun toMillis(): Millis = Millis(minutes * 60 * 1000)
    // ...
}

@JvmInline
value class Millis(val milliseconds: Int) {
    // ...
}

interface User {
    fun decideAboutTime(): Minutes
    fun wakeUp()
}

interface Timer {
    fun callAfter(timeMillis: Millis, callback: () -> Unit)
}

fun setUpUserWakeUpUser(user: User, timer: Timer) {
    val time: Minutes = user.decideAboutTime()
    timer.callAfter(time) { // ERROR: Type mismatch
        user.wakeUp()
    }
}

這樣就不會(huì)搞混. 強(qiáng)制我們使用正確的單位.

我們可以定義這樣的extension properties:

inline val Int.min 
    get() = Minutes(this)

inline val Int.ms 
    get() = Millis(this)

val timeMin: Minutes = 10.min
  • To use types to protect user from value misuse.
    使用場(chǎng)景, 把id包裝在inline class里:
@JvmInline
value class StudentId(val studentId: Int)

@JvmInline
value class TeacherId(val teacherId: Int)

@JvmInline
value class SchoolId(val studentId: Int)

@Entity(tableName = "grades")
class Grades(
   @ColumnInfo(name = "studentId") 
   val studentId: StudentId,
   @ColumnInfo(name = "teacherId") 
   val teacherId: TeacherId,
   @ColumnInfo(name = "schoolId") 
   val schoolId: SchoolId,
    // ...
)

需要注意的是當(dāng)inline class實(shí)現(xiàn)了接口或者接受可空參數(shù)以后, 它就不被inline了.

Testing Hybrid Jetpack Compose Apps

View和Compose混合應(yīng)用的UI測(cè)試.

Thinking functionally in Kotlin

函數(shù)式: 基本的積木塊是函數(shù).

舉的例子: 判斷一個(gè)點(diǎn)是否在射程以內(nèi).

A Simple Framework For Mobile System Design Interviews

mobile開(kāi)發(fā)面試.

面試流程和每個(gè)環(huán)節(jié)的設(shè)計(jì).

Utilizing ADB for daily tasks

日常使用adb.

自動(dòng)填寫(xiě)兩個(gè)字段實(shí)現(xiàn)登錄:

adb shell input text user1 && adb shell input tap x y && adb shell input text password1 && adb shell input tap x y

截圖:

adb shell screencap $PATH_IN_DEVICE

錄屏:

adb shell screenrecord $PATH_IN_DEVICE

測(cè)試deep link:

adb shell am start
        -W -a android.intent.action.VIEW
        -d <URI> <PACKAGE>
        
adb shell am start
        -W -a android.intent.action.VIEW
        -d "example://gizmos" com.example.android

Jetpack Compose Side-Effects II — rememberCoroutineScope

LaunchedEffect可以在composable內(nèi)部啟動(dòng)一個(gè)協(xié)程. 并且在composition退出時(shí)清理.

LaunchedEffect的一些限制:

  • LaunchedEffect是一個(gè)composable, 它只能在composable方法里調(diào)用.
  • 用LaunchedEffect, 你不能控制coroutine的生命周期.

對(duì)于這樣的場(chǎng)景, 我們有rememberCoroutineScope.

rememberCoroutineScope是一個(gè)返回scope的方法, 在composable中調(diào)用, 在composable離開(kāi)composition的時(shí)候自動(dòng)取消.

利用它我們就可以在callback里做事情.

我們還可以手動(dòng)取消.

Trackr comes to the Big Screen

一個(gè)任務(wù)管理應(yīng)用的大屏適配.

Code: https://github.com/android/trackr

Don't mock static: test Timber Logger with trees

如何測(cè)試log.

import timber.log.Timber

class TestTree : Timber.Tree() {
  val logs = mutableListOf<Log>()

  override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
    logs.add(Log(priority, tag, message, t))
  }

  data class Log(val priority: Int, val tag: String?, val message: String, val t: Throwable?)
}

加上這個(gè)輔助方法:

fun withTestTree(body: TestTree.() -> Unit) {
  val testTree = TestTree()
  Timber.plant(testTree)
  body(testTree)
  Timber.uproot(testTree)
}

最后是這樣:

"given service error when get all called then log warn" {

  //setup system under test
  withTestTree {
    val service = mockk<ItemsService> {
      every { getAll() } throws Exception("Something failed :(")
    }
    val systemUnderTest = SystemUnderTest(service)

    //execute system under test
    systemUnderTest.fetchData()

    //capture last logged event
    val lastLoggedEvent = logs.last()

    assertSoftly {
      lastLoggedEvent.message shouldContain "fetchData returned exception instead of empty list"
      lastLoggedEvent.priority shouldBe Log.WARN
    }
  }
}

文章所在的這個(gè)網(wǎng)站叫Kotlin Testing

Mocking Matchers API

mock library, 作者用的是mockito-kotlin

這個(gè)方法:

val product = mock<Product> {
    on { calculatePrice(any())} doAnswer  { i ->
        when (val discountId = i.getArgument<String>(0)) {
            null -> Price(10)
            discountId1 -> Price(5)
            discountId2 -> Price(4)
            else -> throw UnsupportedOperationException("$discountId is not mocked")
        }
    }
}

傳null的時(shí)候并不生效, 因?yàn)閼?yīng)該用anyOrNull而不是any.

然后作者對(duì)Mockito, mockito-kotlin, 和 mockk的argument matcher進(jìn)行了比較.

MockWebServer + HTTPS

MockWebServer的配置問(wèn)題.

用到了: okhttp-tls

Code

News

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末祷嘶,一起剝皮案震驚了整個(gè)濱河市略荡,隨后出現(xiàn)的幾起案子萨咕,更是在濱河造成了極大的恐慌,老刑警劉巖趾徽,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啥供,死亡現(xiàn)場(chǎng)離奇詭異忘伞,居然都是意外死亡撮执,警方通過(guò)查閱死者的電腦和手機(jī)微峰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)抒钱,“玉大人蜓肆,你說(shuō)我怎么就攤上這事∧北遥” “怎么了仗扬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蕾额。 經(jīng)常有香客問(wèn)我厉颤,道長(zhǎng),這世上最難降的妖魔是什么凡简? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任逼友,我火速辦了婚禮,結(jié)果婚禮上秤涩,老公的妹妹穿的比我還像新娘帜乞。我一直安慰自己,他們只是感情好筐眷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布黎烈。 她就那樣靜靜地躺著,像睡著了一般匀谣。 火紅的嫁衣襯著肌膚如雪照棋。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天武翎,我揣著相機(jī)與錄音烈炭,去河邊找鬼。 笑死宝恶,一個(gè)胖子當(dāng)著我的面吹牛符隙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垫毙,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼霹疫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了综芥?” 一聲冷哼從身側(cè)響起丽蝎,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎膀藐,沒(méi)想到半個(gè)月后屠阻,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體红省,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年栏笆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了类腮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臊泰。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蛉加,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出缸逃,到底是詐尸還是另有隱情针饥,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布需频,位于F島的核電站丁眼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏昭殉。R本人自食惡果不足惜苞七,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望挪丢。 院中可真熱鬧蹂风,春花似錦、人聲如沸乾蓬。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)任内。三九已至撵渡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間死嗦,已是汗流浹背趋距。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留越除,地道東北人棚品。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像廊敌,于是被迫代替她去往敵國(guó)和親铜跑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容