- There are two kinds of people :
- those who learn the hard way and those who learn by taking someone’s advice.
当前Android开发领域最佳实践方案,覆盖了最新的技术、架构等各种细节。
本文主要用来收集Android开发中积累的一些宝贵经验,这些经验中有一些约定熟成且经过检验的建议,有一些结合最新技术的实践。无论是菜鸟还是大神,都应该学会阅读别人的经验,并结合自己的思考转化成对自己有用的知识,这才是最快的成长之路。另外,对于这些建议,我会尽量翔实的进行说明以确保能够顺利快速应用到实际开发中。
介绍
下面以这篇文章:Building Android Apps — 30 things that experience made me learn the hard way为核心,对其中提出的每一点建议进行较为深入的分析探究,最终整理成一篇完整的文章。当然,本文还在不断更新中。
1. 第三方库
在你添加每一个third party library之前,请认真考虑是否真的需要这个library。
2. OverDraw
如果用户看不到,那就不要进行绘制(draw),或者说,不要
- 过度绘制 OverDraw
会导致GPU浪费,也会导致app的速度变慢。为了减少这种危害,我们可以利用Debug GPU Overdraw Tool来观察app里的绘制情况,然后可以使用Hierarchy Viewer来进行优化。
- OverDraw
3. 数据库
除非不得不,否则不要使用
- database
4. 65k methods limit
你很快就会遇到的,不过放心,multidexing会帮助你。
- Dalvik 65K methods limit
什么是
?我们知道,我们写完java code之后,dx tool会把java编译成Dalivik虚拟机能识别的
- Dalvik 65K methods limit
文件,这个文件里最多能够索引
- DEX
。关于这个有两点要注意:
- 65536个method
- 这些method是指能够
到的,而不是- 索引(reference)
的。或者说,如果你定义了一个方法,但这个方法并没有被调用,那么就不算在内。- 定义(define)
- 这些method不仅仅是开发人员自己写的,还包括所有第三方library里面的method。
所以,我们总共可以索引
个方法,包括自己写的和引入第三方库里的。
- 65536
那么,我们如何能快速知道我们的app里已经有多少个method了呢?
- bash script:dex-method-counts。这个工具可以快速计算,并且提供一个清晰的视图来阅读。
- dex.shby Jake Wharton。这个工具由于采用了递归算法,所以耗时比较长。(Jake大神还写了一篇有趣的分析文章Play Services 5.0 Is A Monolith Abomination,针对Play Services 5.0太大的问题进行了分析,有空时我再翻译下给各位。虽然Play Services 6.5已经模块化,更加轻量级了)。
现在,既然我们已经知道了自家app里的method数了,那么如何来处理这种情况呢?
- Multidex,官方提供的解决方案,这篇文章里有详细的使用方法,此不赘述。
- ProGuard
可以把code里unnecessary的method移除,压缩apk,当然还有- ProGuard
的奇效。- 代码混淆
- 再创建一个
。把app里可以独立的模块或code提取出来,放到一个独立的dex文件里,你可以使用Custom ClassLoader来加载这些类,然后使用- DEX File
或- 接口
来调用这些方法。不过,这个过程还是比较麻烦的。- 反射
5.RxJava+RxAndroid+Retrolambda
使用前可以通过这篇gist来了解
的结合用法。这个组合可以优雅的在不同线程中处理事务,同时能够方便的实现数据流动和及时响应,而且Retrolambda能够精简你的code。其中,核心的两个概念是
- RxJava, RxAndroid & Retrolambda
和
- Observables
,前者对外提供数据,后者监听并消费这些数据。另外,这里有一个看起来不错的项目Learning RxJava for Android by example,等空闲时再去阅读下code。
- Subscribers
6.Retrofit+ RxJava
利用Retrofit与RxJava结合,为你的app提供网络请求服务。
你可以参考这个超赞的例子,让你快速感受二者结合使用方法。
7. 按- feature
来分package,而不是按- layer
- feature
- layer
这是这篇文章提出的一个点,文中认为分package就像公司安排座位,要按照
来分而不是按照每个人的职位来分,即按照负责一个app的
- team
坐在一起,而不是把所有
- developer、designer、pm
坐在一起,所有
- developer
坐在一起。所以,原文作者认为把一个feature相关的如
- designer
- Activity
等都放在一起。
- adapter
不过,我认为按feature也有坏处,那就是
,拿adapter来讲,一个app里很多adapter是类似且可以复用的,如果我们把各个adapter拆倒各个角落里,就很难提取其中的关联来创建一个
- 复用
了。而且,
- BaseAdapter
,比如一个自定义view,那就很难界定应该放在哪个feature包里了。相反,我们把所有自定义view放在一起,这样也有助于我们发现某些自定义view的区别,然后在refactor时可以提取公用的东西来
- 不同feature之间也有很多公用的东西
。
- 复用
关于这点,欢迎读者给出自己意见。
8.加速Gradle
9. 架构:使用clean Architecture
这里有两篇优质文章:Architecting Android…The clean way?和Architecting Android…The evolution 分别介绍并用code实现了一个Clean架构。后面我也会专门分析下这种架构,因为对于任何一个project而言,最初的好的架构是非常重要的!所以,如果你想提高自己,那么
这一关是必经之路。
- 架构
10. 测试你的app
虽然做测试需要花费你不少时间,但一旦你完成了这一步,以后的开发会更加快速,app也会更加稳定。
这里有个哥们,对unit test进行了细致的点评。
11. 使用- 依赖注入
神器Dagger
- 依赖注入
如果你不知道什么是
,你可以先读一下这篇文章Dependency injection on Android: Dagger (Part 1),或者这篇依赖注入。简单来说,依赖注入替代了传统创建对象的
- 依赖注入
操作,当需要创建一个class的实例时,使用依赖注入从外部直接获取一个实例,具体这个实例是如何创建的不需要关心,由一个对象库统一管理每个对象的创建过程,并直接对外提供对象。这样做的好处是我们不用管实例是怎么创建的,这种抽象可以使得每个对象的创建过程变得可扩展性,只要在对象库里修改一次,那么所有用到这个实例的地方都随之变更。例如在测试时,我们希望某个mock某个对象的数据,就可以修改注入的对象。
- new
依赖注入有不少工具,不过Dagger2使用的是
方式而不是
- 编译时代码生成(code generation)
,所以它的性能比较出众。这篇文章有对Dagger2的实践和分析。
- 反射(reflection)
12.对EditText使用合适的输入类型
13. 关注新的开源library
你可以通过Android Arsenal来保持对开源项目的关注,同时利用这个工具dryrun来快速将开源项目跑在genymotion以看到实际效果。
14. Service
如果你创建了Service,那么一旦这个Service完成了自己的使命,就应该立即清理掉它
15. 使用AccountManager来统一管理用户的帐号密码。
16. 使用- 持续集成CI(Continuous Integration)
来编译并发布你的beta和release build
- 持续集成CI(Continuous Integration)
持续集成可以帮助你方便的编译并发布项目,不过,不要去搭建你们自己的CI服务器,因为你需要花费太多的时间来处理硬盘空间、安全问题和预防SSL攻击等问题。你可以尝试Jenkins、circleci、 travis 或者 shippable,这些价格并不贵,而且能帮你省很多事情。
17. 自动发布到Play Store
你可以使用这个工具gradle-play-publisher来帮助你自动上传apk到Play Store上
18. 开始考虑用svg替代png
理由很简单,android developer应该很熟悉每次导入一张图片时都需要生成四五种不同大小的png图片并放入到对应文件夹。与其维护这么多图片,显然使用一张svg图片更加方便。而且,google也在不断提供相关的支持,除了基本的Vector Drawable,从最新的Support Library我们也能看到google也在鼓励developer们使用svg。
不过,svg也有自己的限制,比如它比较适合小icon,因为它最终会生成bitmap加载以供显示,所以这需要一定的cpu支持。当然总体来讲svg还是更优的,至少大家可以不用再维护四五张不同尺寸的图片了。
19. 将一些library的类进行抽象,从而方便后期替换library
例如,一旦我们打log会使用
,但是,如果后面我们突然想换成
- Log.i()
就会很麻烦,需要一个个log找到来替换。但是,如果我们抽象出一个
- Timber.i()
来,全部调用
- AppLogger
来记log,那么我们只要简单的在
- AppLogger.i()
内部替换掉具体实现就可以了。
- AppLogger
20. 监控网络连接类型,区分- 移动数据流量
和- Wi-Fi
;同样,可以监控- 电量
和- 充电状态
- 移动数据流量
- Wi-Fi
- 电量
- 充电状态
对于不同网络类型,我们可以动态改变我们的UI,比如大图可以选择在
下才加载,而在
- Wi-Fi
则不加载。对于
- 移动数据流量
也是类似的逻辑。用户一定会很感谢app做的这种自适应的。
- 电量
21. User Interface is like a joke.If you have to explain it, it's not that good.
22. 先写slow但是right的代码,再去进行优化
小结
本文长期更新,会保持跟踪最新的技术和研究实践经验,为大家提供有效有用的经验,少走坑。
谢谢!
wingjay
欢迎加入【阿里求职付费群】:
- 及时推送集团内最新岗位空缺信息
- 定期分享针对阿里的最新Android面试题及相关面试经验
- 长期提供阿里集团内各岗位的内推。
详情:www.jianshu.com/p/655af849a…