之前一直想写一篇总结 Android 开发经验的文章,估计当时的我还达不到某种水平,因此思路跟不上,下笔又捉襟见肘。近日,思路较为明朗,因而从新操起键盘开始码字一番。先声明一下哈,本人不是大厂的程序猿。去年毕业前,就一直在当前创业小团队从事本身热爱的打码事业至今。下面总结是创建在我当前的技术水平和认知上写的,若有不一样见解欢迎留下评论互相交流。java
目前 Android 平台上绝大部分开发都是用着 Java ,而跟 Java 这样一门面向对象的语言打交道,难免要触碰到 抽象 和 封装 的概念。我身边接触过的一些开发者,有一部分还对这些概念停留在写一个抽象类、接口、或者一个方法(或抽象方法)。至于为何,我不大清楚是他们表达不出来,仍是不理解。下面我也不高谈阔论,直接举例子来解释我所理解的抽象。android
//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" 这一点并不须要关心,这就是一种对可能发生变化的地方进行抽象封装的体现,它所带来的好处就是下降手抖出错的几率,同时方便咱们进行修改。git
基于抽象和封装,Java 自己不少 API 在设计上就有这样的体现,如 Collections 中的不少排序方法:程序员
这些方法都是基于 List 这个抽象的列表接口进行排序,至于这是一个用什么样的数据结构实现 List(ArrayList 仍是 LinkedList),排序方法自己并不关心。看,是否是体现了 JDK 的设计人员的一种抽象编程的思惟,由于 List 的具体实现可能有千万种,若是每一类 List 都要写一套排序方法,估计要哭瞎了。github
小结:把容易出现变化的部分进行抽象,就是对变化的一种封装。数据库
一个项目的开发,咱们不可能一切从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加固宝、爱加密...json
通常状况下,我在选择是否引入一些开源框架主要基于如下几个因素:api
针对不开源SDK的选择,也主要基于如下几点去考虑:缓存
小结:选好 "车轮" ,事半功倍
为何要抽象依赖于第三方框架呢?这里和第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 ,你就会发现你须要丧心病狂的去改动几十上百个地方的代码,不只工做量大,并且还容易出错。形成这样的缘由,就在于项目和加载图片的框架之间造成了强耦合,而实际上,项目自己不该该知道我具体用了哪一个加载图片的框架。
正确的方式,应该是对框架作一个抽象的封装,以应对将来发生的变化,我直接举本身的开源项目 AndroidAlbum 中的一种封装作法好了。
大体代码以下:
//一、声明 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; } } // 二、将 UniversalAndroidImageLoader 封装成继承 ImageLoaderWrapper 接口的 UniversalAndroidImageLoader , //这里代码有点长,感兴趣能够查看项目源码中的实现 https://github.com/D-clock/AndroidAlbum // 三、作一个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; } } //四、在全部须要加载图片的地方做以下的调用 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);
这样一来,切换框架所带来的代价就会变得很小,这就是不直接依赖于框架所带来的好处。固然,以上只是我比较简单的封装,你也能够进行更加细致的处理。
小结:预留变动,不强耦合于第三方框架
说实话,在没接触 MVP 的架构以前,一直都是使用 MVC 的模式进行开发。而随着项目愈来愈大,Activity或者 Fragment里面代码愈来愈臃肿,看的时候想吐,改的时候想屎...这里撇开其余各类各样的架构不谈,只对比MVC 和 MVP 。
你会发现,若是 View 层只包含了xml文件,那咱们 Android 项目中对 View 层可作操做的程度并不大,顶多就是用include复用一下布局。而 Activity 等简直就是一个奇葩,它虽然归属于 Controller 层,但实际上也干着 View 层的活(View 的初始化和相关操做都是在Activity中)。就是这种既是 View 又是 Controller 的结构,违背了单一责任原则,也使得 Activity 等出现了上述的臃肿问题。
相比 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 吧
把一些经常使用的工具类或业务流程代码进行归类整理,加入本身的代码库(尚未本身我的代码仓库的童鞋能够考虑建一个了)。如加解密、拍照、裁剪图片、获取系统全部图片的路径、自定义的控件或动画以及其其余他一些经常使用的工具类等。归档有助于提升你的开发效率,在遇到新项目的时候随手便可引入使用。若是你想要更好的维护本身的代码库,不妨在不泄露公司机密的前提下,把这个私人代码库加上详细文档给开源出去。这样可以吸引更多开发者来使用这些代码,也能够得到相应的bug反馈,以便于着手定位修复问题,加强这个仓库代码的稳定性。
小结:合理归档代码,能够的话,加以开源维护
关于性能优化的问题,大致都仍是关注那几个方面:内存、CPU、耗电、卡顿、渲染、进程存活率等。对于这些地方的性能优化思路和分析方法,网络上已经有不少答案了,此处不作赘述。我只想说如下几点:
小结:合理优化,数据量化
Rxjava、React Native、Kotlin...开始兴起后,身边有不少开发者会跟风直上。学习新技术的精神是很是值得鼓励的,但没有通过一段时间实践观察,就擅自把新技术引入到商业项目中,则有失稳当。对于大公司的团队来讲,会有专门团队或项目去研究这些新兴技术,以肯定是否在本身的产品线开发中引入。但做为小公司,是否是就意味着没有实践尝试新技术的机会呢?并非!我的有如下几点建议:
小结:空谈误国,实干兴邦
UML,驯服代码和了解项目结构的利器,本人也在学习和体验其好处的路途上。无论遇到大小项目,有了它,能够更好的理清一些脉络结构。对付旧的庞大项目代码,或者有志阅读某些开源项目代码的开发者,绝对是居家必备。
小结:工欲善其事,必先利其器
前面 2 提到,项目不可能从0开始,是须要引入不少第三方框架的。这里并不与 2 互相违背,而是建议有想提升技术逼格的开发者,能够在空暇时间去编码实现一个框架。若是你对网络访问、图片加载方面颇有研究看法,不妨把这些脑海里的思想落实成具体的代码。也许你会发现,你动手去实践的时候,考虑的东西会多得多,本身最终获得的也会更多。(特别建议那些看过不少开源代码,又至今未本身动手自撸一发的)
小结:不要停留在 api 调用的层面
有空又经济能力承受得起的时候,不妨去参加一些本身感兴趣的技术交流会。不少都有大牛上台演讲,听听人家的解决方案,拓宽一下本身看问题的思路,也能够多参加一些含金量高的线上活动。我有挺多开发者朋友,就是参加活动的时候认识的,有时候遇到一些技术问题,还会互相探讨交换一下解决思路。挺赞的!
小结:拓宽技术视野
这个可能没什么好说的,你们看了标题就懂了。它最大的好处在于:
小结:认真总结,不断完善
程序猿不要总是对着电脑,赶忙找个对象提高一下幸福感。听说幸福感高的程序猿,编码效率高,出bug概率小...
总结:作个面向对象的程序员
大概就想到这些了,之后要是再有想写的,另开新篇。絮絮不休写了这么多,最关键的仍是本身要落实,千万不要据说过太多道理,却依然过很差这一辈子哈!!!!