最近想著如何把一些小的技術(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)得及介紹,
如圖所示亡问,目前已經(jīng)發(fā)布的文章大概有 93 篇官紫,包含許多初級(jí)和高級(jí)的文章。如果需要的話可以到網(wǎng)站來(lái)看看~
地址是州藕,
https://www.fullstack.fan
以上是第一期的內(nèi)容万矾,如果覺(jué)得好的話,就來(lái)關(guān)注我哦 ?