解决问题通用方法论

267 阅读6分钟

本文公众号「AndroidTraveler」首发。

今天跟大家讲讲解决问题通用的一个方法论。

在实际开发过程中,我们不可避免的会遇到一些 bug。

那么对于 bug 或者我们没有遇到过的问题,怎么处理呢?

本篇层层递进,一步一步跟你讲解。

各位如果有其他方法或者补充的欢迎留言交流。

如果全篇看完,你发现自己平时就是这么处理的,那么恭喜,你有自己的一套处理问题的方法论。

本次讲解以本人 Android 开发(演示代码之类的)为例进行讲解,但是都是通用的。

1. 根据奔溃栈信息

这个其实是解决问题或者 bug 最直接的方式了。

尤其是奔溃栈信息可以直接定位到项目里面具体文件具体报错位置时。

比如下面的空指针异常:

2019-05-24 13:41:10.031 15019-15019/com.nesger.androidproject E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.nesger.androidproject, PID: 15019
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.nesger.androidproject/com.nesger.androidproject.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2957)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6944)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
        at com.nesger.androidproject.MainActivity.onCreate(MainActivity.java:13)
        at android.app.Activity.performCreate(Activity.java:7183)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1220)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2910)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6944) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374) 

可以很明显的看到是空指针异常导致的,而且根据奔溃栈信息可以定位到具体的文件,从下面这一行

        at com.nesger.androidproject.MainActivity.onCreate(MainActivity.java:13)

可以看出奔溃发生在 MainActivity.java 这个文件的 onCreate() 方法。具体在该文件 13 行。

具体的原因根据奔溃栈也可以看出是使用 null object 去调用 equals 方法。

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference

代码里面也确实是这样:

除了空指针,诸如 IndexOutOfBoundsException 也是常见的,都可以通过奔溃栈信息快速解决。

2. 善用搜索引擎

假设你的问题不能通过栈信息直接得到解决,那么接下来就是善用搜索引擎。

一般搜索引擎一级入口就是 Google、百度等。

实际上问题的解决方法是在二级入口,比如 Stack Overflow、简书、掘金等网站上。

所以如果你熟悉或者一级入口没有找到,可以直接在二级入口进行搜索。

搜索的时候注意一些小技巧,举个例子:
在 Android 里面一些实现可以在 xml 配置,也可以通过代码动态设置。

如果你的目标是要获取代码动态设置的,一般在你搜索的关键信息之后加上 programmatically

3. 通过官方渠道

官方渠道其实指的是官网或者官方论坛。

举些我学习 Flutter 过程中解决问题的例子。

在原生项目嵌入 Flutter 的时候,参考的是 Flutter 在 GitHub 上面的 WIKI。

在运行出现 crash 时,解决是通过 Flutter 在 GitHub 上面的 Issues。

所以我这里就是到 flutter 官方的 GitHub 项目上面去寻找问题的解决方法。

4. 深入源码分析

当上面三个方法都不能解决你的问题时,这个时候你就要结合具体业务进行具体分析,然后深入源码找到本质问题,进行对应处理。

比如我之前写过的一篇文章:

zxing 如何识别反转二维码

遇到的问题就是业务需要识别反转二维码。

前面提到的三个方法论都不能解决,属于很小众的需求。

这种情况下我就分析了一下需求。需求是二维码的识别,而二维码识别核心就是图像获取和解码。那么通过分析源码的解码过程,是不是就可以解决这个问题呢?最后证明是可以的。

又比如说我们的业务使用了 GraphQL,而官方 GraphQL 并没有提供对 Flutter 的直接支持。

这个时候我在 GitHub 上面发现了一个仓库:github.com/zino-app/gr…

这个仓库提供了 Flutter 对 GraphQL 调用的支持,但是实际测试过程中发现有个问题,那就是错误没有回调。

这个时候我通过深入源码,打印日志,发现错误确实有被 catch,但是没有回调回来,说明可能回调里面的代码有问题。

因此我通过实际例子测试并提了一个 PR,最终代码合并解决了这个问题,当然也帮助其他遇到这个问题提 issue 的小伙伴。

所以遇到问题你可以提 issue 被动等待作者解决,也可以主动出击,分析源码,通过实际例子证明代码存在 bug,需要修复。

PR:github.com/zino-app/gr…

5. 请教他人

这个之所以放在最后是因为,这个方法是一把双刃剑,用的好你会成长,用不好你会给自己加上枷锁。

我们先说说好的一方面: 假设你按照前面 4 个方法论依然没法解决这个问题,那么你可以请教他人来解决这个问题。如果他人知道这个问题的解决方法,那么你不止满足于这个问题的解决,事后你还需要进行总结。 总结为什么他人可以解决这个问题: 是经验问题? 还是有一些其他渠道你之前没有考虑? 是否更新完善上面的解决问题方法论? 通过这样的不断总结,你后面需要请教他人的次数会越来越少。或者真的你解决不了需要请教他人的时候,可能他人也不清楚。因为他的方法论你都熟悉了,而你也是如此去完善的。

接下来说说不好的一方面: 假设你遇到问题,你就请教他人,解决问题之后就不理了。后续有问题依然如此循环往复,那么结果你得到的就是依赖性。假设后面有类似问题存在,而别人没空回答你,你就不知道要怎么办了,因为你没有一套自己解决问题的方法论。而且因为你之前已经善于依赖别人解决问题了,因此你现在会很迷茫,不知道如何下手。再者,大家都很忙,你请教他人,他人不一定会回答你,至于什么时候回答你,以及答案是否正确,都是不可预知的。

假设我们要帮助别人,我还是提倡授人以渔而不是授人以鱼。

最后我再说一下关于请教他人这个问题:
别人没有义务帮你解决问题,所以如果别人没回答你的问题,这是正常的。
如果别人回答你的问题,解决你的问题你可以表示感谢,没有解决也不用吐槽。
最好的方式还是自己解决问题。

总结:
1. 根据奔溃栈信息
2. 善用搜索引擎
3. 通过官方渠道
4. 深入源码分析
5. 请教他人