我的 Android 开发实战经验总结

字数4440 阅读5137 评论43 

曾经一直想写一篇总结 Android 开发经验的文章,预计当时的我还达不到某种水平,所以思路跟不上,下笔又捉襟见肘。近日,思路较为明朗。于是又一次操起键盘開始码字一番。

先声明一下哈,本人不是大厂的程序员。去年毕业前。就一直在当前创业小团队从事自己热爱的打码事业至今。以下总结是建立在我当前的技术水平和认知上写的,如有不同看法欢迎留下评论互相交流。

1.理解抽象,封装变化

眼下 Android 平台上绝大部分开发都是用着 Java ,而跟 Java 这样一门面向对象的语言打交道。不免要触碰到 抽象 和 封装 的概念。我身边接触过的一些开发人员。有一部分还对这些概念停留在写一个抽象类、接口、或者一个方法(或抽象方法)。

至于为什么。我不大清楚是他们表达不出来,还是不理解。

以下我也不高谈阔论,直接举样例来解释我所理解的抽象。

//Activity 间使用 Intent 传递数据的两种写法 以下均是伪代码形式。请忽略一些细节

//写法一

//SrcActivity 传递数据给 DestActivity
Intent intent = new Intent(this,DestActivity.class);
intent.putExtra("param", "clock");
SrcActivity.startActivity(intent);

//DestActivity 获取 SrcActivity 传递过来的数据
String param = getIntent.getStringExtra("param");

//写法二

//SrcActivity 传递数据给 DestActivity
Intent intent = new Intent(this,DestActivity.class);
intent.putExtra(DestActivity.EXTRA_PARAM, "clock");
SrcActivity.startActivity(intent);

//DestActivity 获取 SrcActivity 传递过来的数据
public final static String EXTRA_PARAM = "param";
String param = getIntent.getStringExtra(EXTRA_PARAM);

写法一。存在的问题是,假设 SrcActivity 和 DestActivity 哪个把 "param" 打错成 "para" 或者 "paran" 。传递的数据都无法成功接收到。而写法二则不会出现此类问题,由于两个 Activity 之间传递数据仅仅须要知道 EXTRA_PARAM 变量就可以。至于 EXTRA_PARAM 变量究竟是 "param" 、 "para" 、"paran" 这一点并不须要关心,这就是一种对可能发生变化的地方进行抽象封装的体现。它所带来的优点就是减少手抖出错的概率,同一时候方便我们进行改动。

基于抽象和封装,Java 本身非常多 API 在设计上就有这种体现,如 Collections 中的非常多排序方法:


Collections中的排序API

这些方法都是基于 List 这个抽象的列表接口进行排序。至于这是一个用什么样的数据结构实现 List(ArrayList 还是 LinkedList),排序方法本身并不关心。看,是不是体现了 JDK 的设计人员的一种抽象编程的思维,由于 List 的详细实现可能有千万种。假设每一类 List 都要写一套排序方法,预计要哭瞎了。

小结:把easy出现变化的部分进行抽象,就是对变化的一种封装。

2.选好"车轮"

一个项目的开发。我们不可能一切从0做起,假设真是这样,那相同要哭瞎。

因此,善于借用已经做好的 "车轮" 很重要,如:

网络訪问框架:okhttp、retrofit、android-async-http、volley
图片载入框架:Android-Universal-Image-Loader、Glide、Fresco、Picasso
缓存框架:DiskLruCache、 Robospice
Json解析框架:Gson、Fastjson、Jackson
事件总线:EventBus、Otto
ORM框架:GreenDAO、Litepal
还有其它各种各样开源的自己定义控件、动画等。除了以上提到的开源框架,也包含一些不开源的SDK
数据统计:友盟统计,百度统计...
奔溃搜集:腾讯bugly、bugtags...
云存储:七牛...
即使通讯:环信、融云、阿里百川...
推送:小米推送、腾讯推送、百度推送...
安全加固:360加固宝、爱加密...

普通情况下,我在选择是否引入一些开源框架主要基于下面几个因素:

  • 借助搜索引擎,假设网上有一大波资料,说明使用的人多,出了问题好找解决方式;当然。假设普遍出现差评,就能够直接Pass掉了
  • 看框架的作者或团队。如 JakeWharton大神Facebook团队等。

    大神和大公司出品的框架质量相对较高。可保证兴许的维护和bug修复,不easy烂尾;

  • 关注开源项目的 commit密度,issue的提交、回复、关闭数量。watch数,start数,fork数等。像那种个基本不怎么提交代码、提issue又不怎么回复和修复的项目,最好就pass掉。

针对不开源SDK的选择,也主要基于下面几点去考虑:

  • 借助搜索引擎,查明口碑。
  • 非常多第三方SDK的官网首页都会告诉你。多少应用已经接入了此SDK,假设你看到有不少知名应用在上面,那这个SDK能够考虑尝试一下了。

    诸如。友盟官网:


接入友盟的App
  • 查看SDK使用文档、它们的开发人员社区、联系客服。

    好的SDK。使用文档肯定会具体指引你。出了问题。上开发人员社区提问,他们的开发project师也会社区上回答。实在不行仅仅能联系客服,假设客服的态度都让你不爽,那就能够考虑换别家的SDK了。

小结:选好 "车轮" 。事半功倍

3.抽象依赖第三方框架

为什么要抽象依赖于第三方框架呢?这里和第1点是互相照顾的,就是减少我们对详细某个框架的依赖性,从而方便我们高速切换到不同的框架去。讲到这里,你可能认为非常抽象。那我直接举一个载入图片的样例好了。

如果你当前为项目引入一个载入图片的框架 —— Android-Universal-Image-Loader,最简单的做法就是增加对应的依赖包后。在不论什么须要载入图片的地方写上以下这种代码段。

ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
// Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view 
//  which implements ImageAware interface)
imageLoader.displayImage(imageUri, imageView);
// Load image, decode it to Bitmap and return Bitmap to callback
imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() {
    @Override
    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
        // Do whatever you want with Bitmap
    }
});

这种做法最简单粗暴,可是带来的问题也最严重的。假设我有几十上百个地方都这么写,而在某一天。我听说Facebook出了个神器 Fresco。想要换掉 Android-Universal-Image-Loader ,你就会发现你须要丧心病狂的去修改几十上百个地方的代码,不仅工作量大,并且还easy出错。造成这种原因,就在于项目和载入图片的框架之间形成了强耦合。而实际上,项目本身不应该知道我详细用了哪个载入图片的框架。

正确的方式,应该是对框架做一个抽象的封装。以应对未来发生的变化。我直接举自己的开源项目 AndroidAlbum 中的一种封装做法好了。


AndroidAlbum

大致代码例如以下:

//1、声明 ImageLoaderWrapper 接口,定义一些抽象的载入接口方法

public interface ImageLoaderWrapper {

    /**
     * 显示 图片
     *
     * @param imageView 显示图片的ImageView
     * @param imageFile 图片文件
     * @param option    显示參数设置
     */
    public void displayImage(ImageView imageView, File imageFile, DisplayOption option);

    /**
     * 显示图片
     *
     * @param imageView 显示图片的ImageView
     * @param imageUrl  图片资源的URL
     * @param option    显示參数设置
     */
    public void displayImage(ImageView imageView, String imageUrl, DisplayOption option);

    /**
     * 图片载入參数
     */
    public static class DisplayOption {
        /**
         * 载入中的资源id
         */
        public int loadingResId;
        /**
         * 载入失败的资源id
         */
        public int loadErrorResId;
    }
}

// 2、将 UniversalAndroidImageLoader 封装成继承 ImageLoaderWrapper 接口的 UniversalAndroidImageLoader ,
//这里代码有点长,感兴趣能够查看项目源代码中的实现 https://github.com/D-clock/AndroidAlbum

// 3、做一个ImageLoaderFactory

public class ImageLoaderFactory {

    private static ImageLoaderWrapper sInstance;

    private ImageLoaderFactory() {

    }

    /**
     * 获取图片载入器
     *
     * @return
     */
    public static ImageLoaderWrapper getLoader() {
        if (sInstance == null) {
            synchronized (ImageLoaderFactory.class) {
                if (sInstance == null) {
                    sInstance = new UniversalAndroidImageLoader();//<link>https://github.com/nostra13/Android-Universal-Image-Loader</link>
                }
            }
        }
        return sInstance;
    }
}

//4、在全部须要载入图片的地方作例如以下的调用

ImageLoaderWrapper loaderWrapper = ImageLoaderFactory.getLoader();
ImageLoaderWrapper.DisplayOption displayOption = new ImageLoaderWrapper.DisplayOption();
displayOption.loadingResId = R.mipmap.img_default;
displayOption.loadErrorResId = R.mipmap.img_error;
loaderWrapper.displayImage(imagview, url, displayOption);

这样一来,切换框架所带来的代价就会变得非常小,这就是不直接依赖于框架所带来的优点。当然,以上仅仅是我比較简单的封装,你也能够进行更加仔细的处理。

小结:预留变更,不强耦合于第三方框架

4.从 MVC 到 MVP

说实话,在没接触 MVP 的架构之前,一直都是使用 MVC 的模式进行开发。

而随着项目越来越大。Activity或者 Fragment里面代码越来越臃肿,看的时候想吐,改的时候想屎...这里撇开其它各种各样的架构不谈,仅仅对照MVC 和 MVP 。


MVC
  • View:布局的xml文件
  • Controller:Activity、Fragment、Dialog等
  • Model:相关的业务操作处理数据(如对数据库的操作、对网络等的操作都应该在Model层里)

你会发现,假设 View 层仅仅包括了xml文件,那我们 Android 项目中对 View 层可做操作的程度并不大。顶多就是用include复用一下布局。

而 Activity 等简直就是一个奇葩,它尽管归属于 Controller 层,但实际上也干着 View 层的活(View 的初始化和相关操作都是在Activity中)。就是这样的既是 View 又是 Controller 的结构,违背了单一责任原则。也使得 Activity 等出现了上述的臃肿问题。


MVP
  • View:Activity、Fragment、Dialog、Adapter等,该层不包括不论什么业务逻辑
  • Presenter:中介。View 与 Model 不发生联系,都通过 Presenter 传递
  • Model:相关的业务操作处理数据(如对数据库的操作、对网络等的操作都应该在Model层里)

相比 MVC,MVP在层次划分上更加清晰了,不会出现一人身兼二职的情况(有些单元測试的童鞋,会发现单元測试用例更好写了)。在此处你能够看到 View 和 Model 之间是互不知道对方存在的,这样应对变更的优点更大。非常多时候都是 View 层的变化,而 Model 层发生的变化会相对较少,遵循 MVP 的结构开发后。改起来代码来也没那么蛋疼。


这里也有地方须要注意,由于大量的交互操作集中在 Presenter 层中,所以须要把握好 Presenter 的粒度。一个 Activity 能够持有多个 View 和 Presenter。这样也就能够避开一个硕大的 View 和 Presenter 的问题了。

推荐两个不错的 MVP 架构的项目给大家。还不明确的童鞋,能够自行体会一下其设计思想:

https://github.com/pedrovgs/EffectiveAndroidUI
https://github.com/antoniolg/androidmvp

小结:去加以实践的理解 MVP 吧

5.归档代码

把一些经常使用的工具类或业务流程代码进行归类整理。增加自己的代码库(还没有自己个人代码仓库的童鞋能够考虑建一个了)。如加解密、拍照、裁剪图片、获取系统全部图片的路径、自己定义的控件或动画以及其其它他一些经常使用的工具类等。

归档有助于提高你的开发效率,在遇到新项目的时候随手就可以引入使用。假设你想要更好的维护自己的代码库,最好还是在不泄露公司机密的前提下。把这个私人代码库加上具体文档给开源出去。这样能够吸引很多其它开发人员来使用这些代码,也能够获得对应的bug反馈,以便于着手定位修复问题。增强这个仓库代码的稳定性。

小结:合理归档代码,能够的话。加以开源维护

6.性能优化

关于性能优化的问题。大体都还是关注那几个方面:内存、CPU、耗电、卡顿、渲染、进程存活率等。

对于这些地方的性能优化思路和分析方法,网络上已经有非常多答案了。此处不做赘述。

我仅仅想说下面几点:

  • 不要过早的做性能优化。app先求能用再求好用。在需求都还没完毕的时候把大量时间花在优化上是本末倒置的。
  • 优化要用实际数据说话,借助測试工具进行检測(如:网易的Emmagee、腾讯的GT和APT,科大讯飞的iTest,Google的Battery Historian)。

    毕竟老板问你比曾经耗电减少多少,总不能回答减少了一些吧?

    ?

    ?

  • 不论什么不以减低性能损耗来做保活的手段。都是耍流氓。

小结:合理优化,数据量化

7.实践新技术

Rxjava、React Native、Kotlin...開始兴起后。身边有非常多开发人员会跟风直上。学习新技术的精神是非常值得鼓舞的,但没有经过一段时间实践观察,就擅自把新技术引入到商业项目中,则有失妥当。对于大公司的团队来说,会有专门团队或项目去研究这些新兴技术。以确定是否在自己的产品线开发中引入。

但作为小公司,是不是就意味着没有实践尝试新技术的机会呢?并非!

个人有下面几点建议:

  • 借助搜索引擎。看此项技术坑多不多,口碑不错可是坑多的话。则说明当前技术不成熟。能够耐心等待更新。
  • 考虑学习成本。学习成本太大且不easy招到懂这方面的开发人员的情况下,建议不要引入该技术。
  • 高仿一个项目并开源。假设你想引入 React Native 做商业开发,最好先高仿实现一个应用然后将其开源。这样一些对 RN 感兴趣的开发人员会执行你的代码并反馈 bug 给你。有助于你知道一些新技术的坑,并寻找对应的解决方式,终于确定是否引入该技术;
  • 减少入门门槛。

    实践新技术的过程尽量加以具体的文档记录,这会有助于减少项目组其它同事对新技术的入门门槛,能够的话,也将学习文档开源,获得很多其它开发人员对此份文档的反馈,也可纠正一些文档中的错误。

  • 结合实际业务。

    全部新技术的引入都要考虑是否符合当下的业务需求,我听过有些程序员想引入新技术的原因是由于认为这样的技术非常酷。网上说非常好用。非常啥啥啥...自己全然没弄过就人云亦云。有时候好无语,感觉在会用一些技术就像在炫技一样;

小结:空谈误国,实干兴邦

8.UML

UML,驯服代码和了解项目结构的利器,本人也在学习和体验其优点的路途上。无论遇到大小项目,有了它,能够更好的理清一些脉络结构。对付旧的庞大项目代码。或者有志阅读某些开源项目代码的开发人员。绝对是居家必备。

小结:工欲善其事,必先利其器

9.自造"车轮"

前面 2 提到,项目不可能从0開始,是须要引入非常多第三方框架的。这里并不与 2 互相违背。而是建议有想提高技术逼格的开发人员,能够在空闲时间去编码实现一个框架。假设你对网络訪问、图片载入方面非常有研究见解。最好还是把这些脑海里的思想落实成详细的代码。或许你会发现。你动手去实践的时候。考虑的东西会多得多,自己终于得到的也会很多其它。

特别建议那些看过非常多开源码。又至今未自己动手自撸一发的

小结:不要停留在 api 调用的层面

10.扩大技术圈

有空又经济能力承受得起的时候,最好还是去參加一些自己感兴趣的技术交流会。非常多都有大牛上台演讲。听听人家的解决方式,拓宽一下自己看问题的思路,也能够多參加一些含金量高的线上活动。

我有挺多开发人员朋友,就是參加活动的时候认识的,有时候遇到一些技术问题,还会互相探讨交换一下解决思路。

挺赞的!

小结:拓宽技术视野

11.写博客总结

这个可能没什么好说的,大家看了标题就懂了。它最大的优点在于:

  • 系统化记录自己的解决方式;
  • 方便日后自己回想。
  • 有问题也会有读者评论反馈,促进技术交流。
  • 增强自己书面表达能力;

小结:认真总结,不断完好

12.找个对象

程序员不要老是对着电脑。赶紧找个对象提升一下幸福感。据说幸福感高的程序员。编码效率高,出bug几率小...

总结:做个面向对象的程序猿

大概就想到这些了,以后要是再有想写的。另开新篇。絮絮叨叨写了这么多,最关键的还是自己要落实,千万不要听说过太多道理,却依旧过不好这一生哈!

!!。