最近想着如何把一些小的技能知识和细节收拾起来。参阅别人的博客,我给这类文章起了一个姓名叫“技能碎周报”。首要用来收拾和共享日常开发中遇到的小的知识点和感悟。关于能够独立写成一篇文章的技能总结,我仍是依照老的办法以一篇独立的文章的形式收拾处理。

1、三个进步代码质量的技巧

首要共享几个日常开发过程中总结的能够进步代码质量的技巧。

1.1 利用条件判别的切断效应

所谓的切断效应便是在 or/and 判别条件(也便是 Java 代码中的 ||&&)判别的时分,假如前面的一部分能够决定这个表达式的结果,后边的条件就不会履行了。比方,

fun sample(node: Node)
    // 判别条件 1
    if (node == null || node.name == 'sample') {
    }
    // 判别条件 2
    if (node != null && node.name == 'sample') {
    }
}

|| 条件中,假如前面的 node == null 为 true,那么后边的逻辑就不会履行了。在 && 条件中,假如前面的 node != null 为 false,那么后边的逻辑就不会履行了。

咱们能够充分利用条件判别的这个特性,把更简单犯错的或许性能比较差的条件放在后边,这样只要前面的能够决定整个表达式的结果,后边的条件就不履行了。因此,能够下降犯错的概率,进步程序的性能。

1.2 有 if 必有 else

当你仍是一个代码的新手的时分,一个下降代码逻辑过错的思维办法便是 有 if 必有 else. 这能够很大程度上下降自己漏掉某些判别条件的概率。即便有些情况不需要处理,增加一行编译开发和剖析问题的日志对进步自己的编码效率也是有协助的。比方,

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!")
                        // 假如一切子播送现已全部撤销,撤销大局播送监听
                        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 合理利用命名规矩

一个好的命名习气能够增加自己代码的可读性并下降犯错的概率。所以,一般的大厂对变量、办法、类和包名的界说都有自己的规范。

这儿我举一个在 Android 中的比方。比方,我经常看到一些代码在界说一个控件的时分运用比方 image 这样的姓名。这类姓名的缺点是从命名上,你看不出它具体是一个控件仍是一个资源图片等。

假如咱们运用控件的缩写作为变量的前缀,比方比方 ImageView 类型的控件,命名的时分能够用 iv 开头;TextView 类型的控件命名的时分以 tv 开头。这样假如上述 image 是一个控件,那么它的命名应该是 ivImage。这样,经过命名咱们就能够判别出这儿的 image 或许是一个 Bitmap 或许图片的 url 等而不是一个控件。

2、开源运用源码片段三则

这儿首要是从开源软件的源码中抠出一些有价值的代码。

2.1 调用体系控件打印 PDF 逻辑

调用体系 API 打印 PDF,需要根据 WebView 履行。尽管,我在自己的项目中早就用过相似的功用。不过,需要留意的是当在后台履行打印操作的时分,咱们需要像下面这样,界说一个 WebView,然后需要留意当页面加载完毕,也便是 onPageFinished 被调用的时分再履行后续的调用体系 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 信息

一般开发的时分咱们一般用 AS 间接运用 ADB. 但实际上 ADB 有很多功用。我在之前的文章中也介绍过 ADB 用来做自动化点击玩游戏的做法。这儿介绍的一个 ADB 运用是经过 ADB 获取顶部的 Activity 信息,

adb shell dumpsys activity top

该指令会输出一堆 Activity 信息,或许输出多个 Activity 的信息,最顶部的排在最终。此外,还会输出 Activity 的布局信息,Fragment 信息等。不过,感觉输出的格式或许并不固定,比方有些排版就有问题,所以或许需要做多个版别适配。此外,非要追根到底的话,应该检查 ADB 的实现原理,自己写一份与设备通讯。

ADB 的有些功用现在在高版别的设备上面受限了,有些三方运用能够在自己的运用内获取其他运用的布局信息。不知道是不是也是用了上面这个原理。

假如需要了解如何在 Android 内履行 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 在任务栏里隐藏 Activity

接下来的两个和下面的这个开源软件有关。源码地址

https://github.com/zhanghai/TextSelectionWebSearch

这是挺有趣的软件,它的功用是在长按某个文本之后呈现一个自界说的查找挑选,点击之后跳转到咱们指定的浏览器对文本进行查找。这儿涉及到两个知识点。其中一个是在体系的任务栏里边隐藏自己的 Activity.

能够经过为 Activity 增加 excludeFromRecents 特点实现该 Activity 不展现到体系的任务栏。该特点并不会仅仅影响被设置的 Activity. 由此该 Activity 发动的后续同属一个仓库的一系列 Activity 都不会呈现在“最近打开”的任务栏。也便是说该特点是对 Task 起作用的,而不仅仅是某个 Activity.

另一个是需要设置 android:noHistory="true"。设置该特点后,该 Activity 在仓库中不留历史痕迹。默认的值是 false. 举例说明,假设有三个 Activity 分别是:A,B,C. 这三个 Activity 能够依次次序发动下一个Activity. 次日如假如在 AndroidManifest.xml 中配置 B 的特点为:android:noHistory="true"。其他两个不做特别设置,仅仅作为一般的 Activity 处理。能够观察到,A 发动后,从 A 跳转到 B,再从 B 跳转到 C。进入 C 后,此刻假如按回来键,将直接进入 A,而不是 B。综上,能够这么理解 android:noHistory="true" 对 Activity 行为的影响:当该 Activity 屏幕不行见时,相当于 Android 体系调用 Activity 的 finish() 办法完毕了该Activity。

源码地址:

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

该开源软件的别的一个知识点下期和另一个相似功用的知识点一起总结 :)

3、Kotlin 运用心得和踩坑总结两则

3.1 拓宽办法的运用心得:能在类中添加办法时就不要运用拓宽办法

我有时分在 review 别人的代码的时分看到,有的同学很喜欢运用 Kotlin 的拓宽办法和拓宽字段的特性。比方,

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

这没什么问题,可是当咱们具有这个类的权限,能够对它直接进行修改,并且新增的特性和办法是通用的的时分,不建议运用拓宽字段和拓宽办法。首要原因是拓宽的字段和拓宽仍然游离于类本身之外,不便于该类相关的办法的统一收拢。假如只针对自己的需求新增一个拓宽办法或特点,能够考虑运用 Kotlin 的特性。

3.2 缺省函数处理机制引发的反常

如下两个办法,办法 2 是在办法 1 的基础上为了兼容老的办法新增的一个办法,

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

假如一切模块都是源码编译则不存在任何问题。可是假如调用以上办法的某个模块以编译为 jar 则会呈现办法找不到的反常。这是由于实际上 Kotlin 在处理缺省参数函数的时分会新增一个静态办法,并在该办法内部经过参数判别的办法,当某个参数没传的时分就运用默认值,

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 注解。此刻的反编译结果如下。也便是当加了这个注解之后,Kotlin 会根据原来的办法新增一系列重载办法。不过后边这种办法的一个缺点是,或许会导致类的办法量暴增。

@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、介绍我的技能博客

怎么说我也是浸淫在互联网职业多年,总结和收拾了大量的文章。由于微信大众号每天发布文章上限问题,以及有些文章过于久远,估计发出来很多读者也没什么爱好,所以,我把这些文章都放到了技能博客上面。其实这个博客做成有一段时间了,仅仅一向没来得及介绍,

技术碎周报第 1 期 (2022.08.30)

如图所示,现在现已发布的文章大概有 93 篇,包含许多初级和高档的文章。假如需要的话能够到网站来看看~

地址是,

https://www.fullstack.fan

以上是第一期的内容,假如觉得好的话,就来关注我哦 ♥