技術(shù)碎周報(bào)第 1 期 (2022.08.30)

最近想著如何把一些小的技術(shù)知識(shí)和細(xì)節(jié)整理起來(lái)。參考別人的博客伤锚,我給這類(lèi)文章起了一個(gè)名字叫“技術(shù)碎周報(bào)”擅笔。主要用來(lái)整理和分享日常開(kāi)發(fā)中遇到的小的知識(shí)點(diǎn)和感悟。對(duì)于能夠獨(dú)立寫(xiě)成一篇文章的技術(shù)總結(jié),我還是按照老的方式以一篇獨(dú)立的文章的形式整理處理猛们。

1念脯、三個(gè)提升代碼質(zhì)量的技巧

首先分享幾個(gè)日常開(kāi)發(fā)過(guò)程中總結(jié)的能夠提升代碼質(zhì)量的技巧。

1.1 利用條件判斷的截?cái)嘈?yīng)

所謂的截?cái)嘈?yīng)就是在 or/and 判斷條件(也就是 Java 代碼中的 ||&&)判斷的時(shí)候弯淘,如果前面的一部分能夠決定這個(gè)表達(dá)式的結(jié)果绿店,后面的條件就不會(huì)執(zhí)行了。比如庐橙,

fun sample(node: Node)
    // 判斷條件 1
    if (node == null || node.name == 'sample') {

    }

    // 判斷條件 2
    if (node != null && node.name == 'sample') {

    }
}

|| 條件中惯吕,如果前面的 node == null 為 true,那么后面的邏輯就不會(huì)執(zhí)行了怕午。在 && 條件中废登,如果前面的 node != null 為 false,那么后面的邏輯就不會(huì)執(zhí)行了郁惜。

我們可以充分利用條件判斷的這個(gè)特性堡距,把更容易出錯(cuò)的或者性能比較差的條件放在后面,這樣只要前面的能夠決定整個(gè)表達(dá)式的結(jié)果兆蕉,后面的條件就不執(zhí)行了羽戒。因此,可以降低出錯(cuò)的概率虎韵,提升程序的性能易稠。

1.2 有 if 必有 else

當(dāng)你還是一個(gè)代碼的新手的時(shí)候,一個(gè)降低代碼邏輯錯(cuò)誤的思維方式就是 有 if 必有 else. 這可以很大程度上降低自己漏掉某些判斷條件的概率包蓝。即便有些情況不需要處理驶社,增加一行編譯開(kāi)發(fā)和分析問(wèn)題的日志對(duì)提高自己的編碼效率也是有幫助的。比如测萎,

fun unregisterReceiver(receiver: BroadcastReceiver) {
    when (processes[receiver]) {
        true -> {
            synchronized(lock) {
                val action = actions.remove(receiver)
                if (action != null) {
                    val globalReceiver = globals[action]
                    if (globalReceiver != null) {
                        receivers[globalReceiver]?.remove(receiver)
                        L.i("Receiver [$receiver] unregistered!")
                        // 如果所有子廣播已經(jīng)全部取消亡电,取消全局廣播監(jiān)聽(tīng)
                        if (receivers[globalReceiver]?.isEmpty() == true) {
                            globals.remove(action)
                            receivers.remove(globalReceiver)
                            UtilsApp.getApp().unregisterReceiver(globalReceiver)
                        }
                    } else {
                        L.e("Failed to unregister receiver [$receiver]: " +
                                "global receiver not found! WARN: This might lead to memory leak!")
                    }
                } else {
                    L.e("Failed to unregister receiver [$receiver]: " +
                            "action not found! WARN: This might lead to memory leak!")
                }
            }
        }
        false -> {
            LocalBroadcastManager.getInstance(UtilsApp.getApp())
                .unregisterReceiver(receiver)
        }
        else -> {
            L.e("Failed to unregister receiver[$receiver]: " +
                    "process not found! WARN: This might lead to memory leak!")
        }
    }
    processes.remove(receiver)
}

1.3 合理利用命名規(guī)則

一個(gè)好的命名習(xí)慣可以增加自己代碼的可讀性并降低出錯(cuò)的概率。所以硅瞧,一般的大廠對(duì)變量份乒、方法、類(lèi)和包名的定義都有自己的規(guī)范腕唧。

這里我舉一個(gè)在 Android 中的例子或辖。比如,我經(jīng)吃娼樱看到一些代碼在定義一個(gè)控件的時(shí)候使用諸如 image 這樣的名字颂暇。這類(lèi)名字的缺點(diǎn)是從命名上,你看不出它具體是一個(gè)控件還是一個(gè)資源圖片等月腋。

如果我們使用控件的縮寫(xiě)作為變量的前綴蟀架,比如比如 ImageView 類(lèi)型的控件,命名的時(shí)候可以用 iv 開(kāi)頭榆骚;TextView 類(lèi)型的控件命名的時(shí)候以 tv 開(kāi)頭片拍。這樣如果上述 image 是一個(gè)控件,那么它的命名應(yīng)該是 ivImage妓肢。這樣捌省,通過(guò)命名我們就可以判斷出這里的 image 可能是一個(gè) Bitmap 或者圖片的 url 等而不是一個(gè)控件。

2碉钠、開(kāi)源應(yīng)用源碼片段三則

這里主要是從開(kāi)源軟件的源碼中摳出一些有價(jià)值的代碼纲缓。

2.1 調(diào)用系統(tǒng)控件打印 PDF 邏輯

調(diào)用系統(tǒng) API 打印 PDF,需要基于 WebView 執(zhí)行喊废。雖然祝高,我在自己的項(xiàng)目中早就用過(guò)類(lèi)似的功能。不過(guò)污筷,需要注意的是當(dāng)在后臺(tái)執(zhí)行打印操作的時(shí)候工闺,我們需要像下面這樣,定義一個(gè) WebView瓣蛀,然后需要注意當(dāng)頁(yè)面加載完畢陆蟆,也就是 onPageFinished 被調(diào)用的時(shí)候再執(zhí)行后續(xù)的調(diào)用系統(tǒng) API 的操作,

fun create() {
    val webView = WebView(context)
    webView.loadDataWithBaseURL(baseURL, content, mimeType, encoding, null)
    webView.webViewClient = object : WebViewClient() {

        override fun onPageFinished(view: WebView?, url: String?) {
            val printDocumentAdapter = webView.createPrintDocumentAdapter(file.nameWithoutExtension)
            generatePDF(printDocumentAdapter)
        }
    }
}

private fun generatePDF(printDocumentAdapter: PrintDocumentAdapter) {
    val postPDFPrinter = PostPDFPrinter(file, printDocumentAdapter, printAttributes, onResult)
    postPDFPrinter.print()
}

源碼地址:

https://github.com/CostCost/Notally/blob/master/Post/src/main/java/com/omgodse/post/PostPDFGenerator.kt

2.2 獲取頂部的 Activity 信息

通常開(kāi)發(fā)的時(shí)候我們一般用 AS 間接使用 ADB. 但實(shí)際上 ADB 有很多功能惋增。我在之前的文章中也介紹過(guò) ADB 用來(lái)做自動(dòng)化點(diǎn)擊玩游戲的做法叠殷。這里介紹的一個(gè) ADB 應(yīng)用是通過(guò) ADB 獲取頂部的 Activity 信息,

adb shell dumpsys activity top

該指令會(huì)輸出一堆 Activity 信息诈皿,可能輸出多個(gè) Activity 的信息林束,最頂部的排在最后。此外稽亏,還會(huì)輸出 Activity 的布局信息诊县,F(xiàn)ragment 信息等。不過(guò)措左,感覺(jué)輸出的格式可能并不固定依痊,比如有些排版就有問(wèn)題,所以可能需要做多個(gè)版本適配怎披。此外胸嘁,非要追根到底的話,應(yīng)該查看 ADB 的實(shí)現(xiàn)原理凉逛,自己寫(xiě)一份與設(shè)備通信性宏。

ADB 的有些功能現(xiàn)在在高版本的設(shè)備上面受限了,有些三方應(yīng)用可以在自己的應(yīng)用內(nèi)獲取其他應(yīng)用的布局信息状飞。不知道是不是也是用了上面這個(gè)原理毫胜。

如果需要了解如何在 Android 內(nèi)執(zhí)行 ADB 指令的話书斜,可以參考下面的代碼,

https://github.com/Shouheng88/AndroidUtils/blob/master/utils/src/main/java/me/shouheng/utils/device/ShellUtils.java

源碼地址:

https://github.com/CostCost/AppActivityName/blob/master/src/com/zgh/util/Main.java

2.3 在任務(wù)欄里隱藏 Activity

接下來(lái)的兩個(gè)和下面的這個(gè)開(kāi)源軟件有關(guān)酵使。源碼地址

https://github.com/zhanghai/TextSelectionWebSearch

這是挺有趣的軟件荐吉,它的功能是在長(zhǎng)按某個(gè)文本之后出現(xiàn)一個(gè)自定義的搜索選擇,點(diǎn)擊之后跳轉(zhuǎn)到我們指定的瀏覽器對(duì)文本進(jìn)行搜索口渔。這里涉及到兩個(gè)知識(shí)點(diǎn)样屠。其中一個(gè)是在系統(tǒng)的任務(wù)欄里面隱藏自己的 Activity.

可以通過(guò)為 Activity 增加 excludeFromRecents 屬性實(shí)現(xiàn)該 Activity 不展示到系統(tǒng)的任務(wù)欄。該屬性并不會(huì)僅僅影響被設(shè)置的 Activity. 由此該 Activity 啟動(dòng)的后續(xù)同屬一個(gè)堆棧的一系列 Activity 都不會(huì)出現(xiàn)在“最近打開(kāi)”的任務(wù)欄缺脉。也就是說(shuō)該屬性是對(duì) Task 起作用的痪欲,而不僅僅是某個(gè) Activity.

另一個(gè)是需要設(shè)置 android:noHistory="true"。設(shè)置該屬性后攻礼,該 Activity 在堆棧中不留歷史痕跡业踢。默認(rèn)的值是 false. 舉例說(shuō)明,假設(shè)有三個(gè) Activity 分別是:A礁扮,B陨亡,C. 這三個(gè) Activity 可以依次順序啟動(dòng)下一個(gè)Activity. 次日如如果在 AndroidManifest.xml 中配置 B 的屬性為:android:noHistory="true"。其他兩個(gè)不做特別設(shè)置深员,僅僅作為一般的 Activity 處理负蠕。可以觀察到倦畅,A 啟動(dòng)后遮糖,從 A 跳轉(zhuǎn)到 B,再?gòu)?B 跳轉(zhuǎn)到 C叠赐。進(jìn)入 C 后欲账,此時(shí)如果按返回鍵,將直接進(jìn)入 A芭概,而不是 B赛不。綜上,可以這么理解 android:noHistory="true" 對(duì) Activity 行為的影響:當(dāng)該 Activity 屏幕不可見(jiàn)時(shí)罢洲,相當(dāng)于 Android 系統(tǒng)調(diào)用 Activity 的 finish()方法結(jié)束了該Activity踢故。

源碼地址:

https://github.com/CostCost/TextSelectionWebSearch/blob/master/app/src/main/AndroidManifest.xml

該開(kāi)源軟件的另外一個(gè)知識(shí)點(diǎn)下期和另一個(gè)類(lèi)似功能的知識(shí)點(diǎn)一起總結(jié) :)

3、Kotlin 使用心得和踩坑總結(jié)兩則

3.1 拓展方法的使用心得:能在類(lèi)中添加方法時(shí)就不要使用拓展方法

我有時(shí)候在 review 別人的代碼的時(shí)候看到惹苗,有的同學(xué)很喜歡使用 Kotlin 的拓展方法和拓展字段的特性殿较。比如,

val CountdownRingDrawable.isCountingDown: Boolean
    get() = currentRemain > 0

這沒(méi)什么問(wèn)題桩蓉,但是當(dāng)我們擁有這個(gè)類(lèi)的權(quán)限淋纲,可以對(duì)它直接進(jìn)行修改,并且新增的特性和方法是通用的的時(shí)候院究,不建議使用拓展字段和拓展方法洽瞬。主要原因是拓展的字段和拓展仍然游離于類(lèi)本身之外本涕,不便于該類(lèi)相關(guān)的方法的統(tǒng)一收攏。如果只針對(duì)自己的需求新增一個(gè)拓展方法或?qū)傩曰锴裕梢钥紤]使用 Kotlin 的特性菩颖。

3.2 缺省函數(shù)處理機(jī)制引發(fā)的異常

如下兩個(gè)方法,方法 2 是在方法 1 的基礎(chǔ)上為了兼容老的方法新增的一個(gè)方法对供,

// 方法 1
fun xxxx(param1: String?): String {
    // ...
}

// 方法 2
fun xxxx(param1: String?, param2: Boolean = false, param3: String = ""): String {
}

如果所有模塊都是源碼編譯則不存在任何問(wèn)題位他。但是如果調(diào)用以上方法的某個(gè)模塊以編譯為 jar 則會(huì)出現(xiàn)方法找不到的異常氛濒。這是因?yàn)閷?shí)際上 Kotlin 在處理缺省參數(shù)函數(shù)的時(shí)候會(huì)新增一個(gè)靜態(tài)方法产场,并在該方法內(nèi)部通過(guò)參數(shù)判斷的方式,當(dāng)某個(gè)參數(shù)沒(méi)傳的時(shí)候就使用默認(rèn)值舞竿,

public final String xxxx(@Nullable String param1, boolean param2, @NotNull String param3) {
    Intrinsics.checkNotNullParameter(param3, "param3");
    return "";
}

// $FF: synthetic method
public static String xxxx$default(KotlinDefaultParameterTest var0, String var1, boolean var2, String var3, int var4, Object var5) {
    if ((var4 & 2) != 0) {
        var2 = false;
    }

    if ((var4 & 4) != 0) {
        var3 = "";
    }

    return var0.xxxx(var1, var2, var3);
}

解決這個(gè)問(wèn)題的一個(gè)方法是新增一個(gè)重載方法京景。另一個(gè)解決辦法是為上述方法增加 @JvmOverloads 注解。此時(shí)的反編譯結(jié)果如下骗奖。也就是當(dāng)加了這個(gè)注解之后确徙,Kotlin 會(huì)根據(jù)原來(lái)的方法新增一系列重載方法。不過(guò)后面這種方式的一個(gè)缺點(diǎn)是执桌,可能會(huì)導(dǎo)致類(lèi)的方法量暴增鄙皇。

@JvmOverloads
@NotNull
public final String xxxx(@Nullable String param1, boolean param2, @NotNull String param3) {
    Intrinsics.checkNotNullParameter(param3, "param3");
    return "";
}

// $FF: synthetic method
public static String xxxx$default(KotlinDefaultParameterTest var0, String var1, boolean var2, String var3, int var4, Object var5) {
    if ((var4 & 2) != 0) {
        var2 = false;
    }

    if ((var4 & 4) != 0) {
        var3 = "";
    }

    return var0.xxxx(var1, var2, var3);
}

@JvmOverloads
@NotNull
public final String xxxx(@Nullable String param1, boolean param2) {
    return xxxx$default(this, param1, param2, (String)null, 4, (Object)null);
}

@JvmOverloads
@NotNull
public final String xxxx(@Nullable String param1) {
    return xxxx$default(this, param1, false, (String)null, 6, (Object)null);
}

4、介紹我的技術(shù)博客

怎么說(shuō)我也是浸淫在互聯(lián)網(wǎng)行業(yè)多年仰挣,總結(jié)和整理了大量的文章伴逸。因?yàn)槲⑿殴娞?hào)每天發(fā)布文章上限問(wèn)題,以及有些文章過(guò)于久遠(yuǎn)膘壶,估計(jì)發(fā)出來(lái)很多讀者也沒(méi)什么興趣错蝴,所以,我把這些文章都放到了技術(shù)博客上面颓芭。其實(shí)這個(gè)博客做成有一段時(shí)間了顷锰,只是一直沒(méi)來(lái)得及介紹,

QQ截圖20220829222544.png

如圖所示亡问,目前已經(jīng)發(fā)布的文章大概有 93 篇官紫,包含許多初級(jí)和高級(jí)的文章。如果需要的話可以到網(wǎng)站來(lái)看看~

地址是州藕,

https://www.fullstack.fan

以上是第一期的內(nèi)容万矾,如果覺(jué)得好的話,就來(lái)關(guān)注我哦 ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末慎框,一起剝皮案震驚了整個(gè)濱河市良狈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌笨枯,老刑警劉巖薪丁,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遇西,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡严嗜,警方通過(guò)查閱死者的電腦和手機(jī)粱檀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)漫玄,“玉大人茄蚯,你說(shuō)我怎么就攤上這事∧烙牛” “怎么了渗常?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)汗盘。 經(jīng)常有香客問(wèn)我皱碘,道長(zhǎng),這世上最難降的妖魔是什么隐孽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任癌椿,我火速辦了婚禮,結(jié)果婚禮上菱阵,老公的妹妹穿的比我還像新娘踢俄。我一直安慰自己,他們只是感情好晴及,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布都办。 她就那樣靜靜地躺著,像睡著了一般抗俄。 火紅的嫁衣襯著肌膚如雪脆丁。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天动雹,我揣著相機(jī)與錄音槽卫,去河邊找鬼。 笑死胰蝠,一個(gè)胖子當(dāng)著我的面吹牛歼培,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茸塞,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼躲庄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了钾虐?” 一聲冷哼從身側(cè)響起噪窘,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎效扫,沒(méi)想到半個(gè)月后倔监,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體直砂,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年浩习,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了静暂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谱秽,死狀恐怖洽蛀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情疟赊,我是刑警寧澤郊供,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站听绳,受9級(jí)特大地震影響颂碘,放射性物質(zhì)發(fā)生泄漏异赫。R本人自食惡果不足惜椅挣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望塔拳。 院中可真熱鬧鼠证,春花似錦、人聲如沸靠抑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)颂碧。三九已至荠列,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間载城,已是汗流浹背肌似。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留诉瓦,地道東北人川队。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像睬澡,于是被迫代替她去往敵國(guó)和親固额。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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