IT观察】网络通信、图片显示、数据库操作……Android程序员如何利用开源框架
每个Android 程序员都不是Android应用开发之路上孤军奋战的一个人,GitHub上浩如烟海的开源框架或类库就是前人为我们发明的轮子,有的轮子能提高软件性能,而有的轮子似乎是以牺牲性能为代价换取编程速度。擅长利用轮子的程序员已经遥遥领先,不擅长利用轮子的程序员总是嫌前人发明的轮子不够圆,自己造个方轮子上路后才发现落后了。
- 作者:玖哥来源:51CTO|2017-10-19 16:06
【51CTO.com原创稿件】每个Android 程序员都不是Android应用开发之路上孤军奋战的一个人,GitHub上浩如烟海的开源框架或类库就是前人为我们发明的轮子,有的轮子能提高软件性能,而有的轮子似乎是以牺牲性能为代价换取编程速度。擅长利用轮子的程序员已经遥遥领先,不擅长利用轮子的程序员总是嫌前人发明的轮子不够圆,自己造个方轮子上路后才发现落后了。
技术选型的考量点
来自不同行业、面向不同用户的App对功能特性、各方面性能的要求都是不同的,选择开源框架和类库不能掉以轻心,我们在选择开源框架之前务必要选择几个考量点进行技术选型。我们选择开源框架可以从以下三个方面考虑(排名有先后):
1、功能特性:提供的特性是否满足项目的需求。比如Picasso不支持GIF图和视频缩略图等,不适合视频应用。
2、编程效率:是否有默认参数,配置是否复杂。是否可以一键调用,不需要二次封装。API是否简洁易懂,可以望文生义。仅在module的build.gradle里依赖和还需要在project的build.gradle里依赖对程序员细心程度的要求是不一样的,并且需要开发者配置参数的UIL被Picasso取代也不足为奇。
3、性能:处理速度不能太慢,稳定性也不能太差,占用内存不应该太高,jar的体积可以不能太大。此外过大的jar包一定包含过多的方法数,积累起来会导致你的APP遇到64K问题。Fresco的体积就太大了点。
我为什么要把编程效率排在性能之前,因为程序员的工资每年都在上涨,经常出现薪资倒挂现象,而同等价格的Android设备的性能每18个月翻一番。再举一个极端的例子:“实现“3的3次方”有三种写法:
“3^3”、“3*3*3”、“(3+3+3)+(3+3+3)+(3+3+3)”,显然正常人都会用第一种方法,但第三种方法的性能才是最好的。
确定了技术选型的考量点以后,我们接下来要确定最常用的六大类开源框架的选型:
一、网络通信
Android程序员都是移动互联网开发者,很少有不依赖网络的App,网络权限几乎是每个App必然要申请的权限,网络通信框架的选择也是一个App项目中技术选型的最重要组成部分。
android-async-http
android-async-http是Android最经典的网络异步通信框架,它对Apache的HttpClient API的封装使得开发者可以简洁优雅地实现网络请求和响应。因为Android 6.0以后去掉了HttpClient,这就敲响了android-async-http的丧钟,目前已经很少有人使用这个框架。
OkHttp
OkHttp是Square出品的一个简洁、快速、高效的HTTP客户端,支持HTTP/2以及SPDY,扮演着传输层的角色。它既能在网络性能很差的情况下很好地工作,也能够避免常见的网络连接问题。OkHttp的常规用法:
- OkHttpClient okHttpClient = new OkHttpClient();
- Request request = new Request.Builder()
- .url("http://短连接")
- .build();
- Call call = okHttpClient.newCall(request);
- call.enqueue(new Callback() {
- @Override
- public void onFailure(Call call, IOException e) {
- e.printStackTrace();
- }
- @Override
- public void onResponse(Call call
- , Response response) throws IOException {
- }
- });
尽管名字带有“Http”,但OkHttp的功能并不局限于http请求。以前的版本可以通过插件来实现WebSocket长连接,而3.5以后的版本则直接支持WebSocket:
- OkHttpClient client = new OkHttpClient
- .Builder()
- .readTimeout(0,
- TimeUnit.MILLISECONDS)
- .build();
- Request request = new Request
- .Builder()
- .url("ws://ws打头的链接就是长连接")
- .build();
- WebSocket webSocket = client
- .newWebSocket(request, new WebSocketListener() {
- @Override
- public void onMessage(WebSocket webSocket, String text) {
- super.onMessage(webSocket, text);
- }
- @Override
- public void onFailure(WebSocket webSocket,
- Throwable t, Response response) {
- super.onFailure(webSocket, t, response);
- }
- });
Volley
Volley是Google开发的一个简化网络任务的库。Volley侧重于Request的队列的管理,适合请求、加载、缓存、多线程、同步等各类任务,尤其能使数据量小但通信频繁的场景中的网络通信更快、更简单、更健壮。Volley的常规用法:
- StringRequest stringRequest = new StringRequest(Request.Method.GET
- ,"http://普通Volley"
- , new Response.Listener<String>() {
- @Override
- public void onResponse(String response) {
- }
- }, new Response.ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- error.printStackTrace();
- }
- });
- Volley.newRequestQueue(this).add(stringRequest);
Volley在频繁访问服务器有很大的优势,OkHttp在性能、速度方面优势明显一,点。我们可以用OkHttp来扩展Volley,这样能让二者取长补短。
Retrofit
Retrofit并不是一个单纯的网络请求函数框架,而是能直接请求得到实体类的整套组合框架。Retrofit默认使用Gson作为JSON解析器,默认使用OkHttp实现网络请求,同时基于注解使得代码变得非常简洁,当然我们也可以将OkHttp和Gson换成其他的函数库。
【小结】我个人不建议使用有点过度封装的Retrofit,尤其当一个App的团队业务较复杂或人员变动太频繁而导致域名前缀、header、contentType等变化太多时。数据量小但通信频繁的App,例如有轮询等功能的时候适合用Volley,而中等以上体量的App则适合用OkHttp,但我更建议把OkHttp和Volley结合在一起使用。
二、图片缓存与显示
图片历来是Android手机中的吃内存大户,稍不留意就会出现OOM,所以能否做好图片的缓存是一个App性能好坏的决定性指标,对图片缓存与显示框架的技术选型不得不重视。
Universal-Image-Loader
Universal-Image-Loader(以下简称UIL)是Android平台老牌的图片缓存与显示框架,也是大多数3年以上的Android程序员入行以来接触的第一个框架。功能强大灵活且高度可自定义,因此在Picasso流行开来之前一直是最流行的Android图片加载库,目前仍有大量开发者使用。
Picasso
Picasso是著名的Square公司众多开源项目中的一个,最显著的优点就是体积非常轻小,仅有120KB,也因此常常成为众多深入研读源码的程序员第一个研读的框架。
- Picasso.with(this)
- .load("图片地址:url或path")
- .placeholder(R.mipmap.ic_launcher)
- .error(R.mipmap.ic_launcher)
- .resize(100,100)
- .centerCrop()
- .centerInside()
- .into(imageView);
Glide
Glide是Google官方推荐的用于Android平台上的图片加载和缓存库,被广泛应用在Google的开源项目中。Glide除了为包含图片的滚动列表()做了尽可能流畅的优化外,还支持GIF格式图片以及视频缩略图的显示。
- Glide.with(this)
- .load("图片地址:url或path")
- .placeholder(R.mipmap.ic_launcher)
- .error(R.mipmap.ic_launcher)
- .override(100,100)//此处与Picasso不同
- .centerCrop()
- .fitCenter()//此处与Picasso不同
- .into(imageView);
可见Glide和Picasso有90%的相似度,只是在细节上还是存在不少区别。
Glide提供了灵活的API可以让开发者方便地替换下载图片所用的网络函数库,默认情况下,它使用HttpUrlConnection作为网络请求模块,开发者也可以根据自己项目的实际需求灵活使用Google的Volley或者Square的OkHttp等函数库进行替换。
Fresco
Fresco是Facebook开源的功能强大的图片缓存与显示框架。图片缓存方案一般有两级缓存,分别是内存缓存和磁盘缓存。目前最流行的“三级缓存”说法是错误的,所谓“网络缓存”并不是Android端所控制的。在Fresco中增加了一级内存缓存,这使得Fresco成了加载大量大图之后OOM概率最低的图片缓存与显示框架。
Fresco无与伦比的加载速度和稳定性让它在2015年流行过一阵子,但强大的性能是以增加App 3.4M的体积换来的,并且Fresco使用有些复杂,导致了Fresco只适合大型App;而Picasso的功能太少,仅适用于小型App;综上,一般体量和功能的App建议选择Glide。
我们不要拘泥于用开源框架或自定义控件加载图片,在电商类应用中常见的类似淘宝详情页的那种很长很长的图片的url,建议直接用WebView显示:
- webView.loadUrl("淘宝或微博长长的图");
- WebSettings webSettings = webView.getSettings();
- webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
- webSettings.setJavaScriptEnabled(true);
- webView.setWebViewClient(new WebViewClient() {
- @Override
- public void onPageFinished(WebView view, String url) {
- super.onPageFinished(view, url);
- String javascript = "javascript:function ResizeImages() {" +
- "var myimg,oldwidth;" +
- "var maxwidth = document.body.clientWidth;" +
- "for(i=0;i <document.images.length;i++){" +
- "myimg = document.images[i];" +
- "if(myimg.width > maxwidth){" +
- "oldwidth = myimg.width;" +
- "myimg.width = maxwidth;" +
- "}" +
- "}" +
- "}";
- view.loadUrl(javascript);
- view.loadUrl("javascript:ResizeImages();");
- }
- });
三、数据库操作
Android的数据库是基于开源的SQLite实现的,适用于保存大量数据,不过和上文的JavaScript代码一样,SQL代码也是用String形式的,所以IDE没有自动纠错和代码补全功能,这就意味着我们需要一个不用写复杂的sql语句,而用简单的API即可完成创建和操纵数据的数据库操作框架。
ORMLite
ORMLite是目前使用人数最多的数据库操作框架,尽管在2013就已经停止维护,但因为操作简便仍然广受欢迎。
SugarORM
SugarORM是一个较冷门的数据库操作框架,性能暂且不论,单凭每次增加列必须在assests里新建一个SQL文件就够让人头疼了。
GreenDAO
GreenDAO是一个轻量级且快速的ORM框架,专门为Android高度优化和定制,它能够支持每秒数千条记录的CRUD操作。尽管读写速度是ORMLite的4倍,但复杂的学习过程和操作方式却令人望而却步。
Realm
Realm并不是一个基于SQLite的数据库框架,而是一个自有数据库引擎,并且带有数据库可视化操作工具的整套数据库操作解决方案。
Realm有一个显著的优点是跨平台,Android和iOS开发者无需考虑内部数据的架构,调用Realm提供的API即可轻松完成数据的交换。其次Realm为开发者提供了一个轻量级的数据库可视化操作工具,保证了开发者可以轻松查看数据库中的内容,并实现简单地插入和删除等操作。
Realm的速度也比其他框架快得多,不过Realm的体积高达4.2MB,一般项目可能无法接受。
【小结】综合性能考虑,包大小以及开源库的可持续发展等因素,建议对数据库要求较高的 App采用GreenDAO,对数据要求极其严格的App采用Realm,而一般的App采用ORMLite,毕竟编程效率才是王道。
四、JSON解析与生成
移动互联网产品与服务器端通信的数据格式,如果没有特殊需求的话,一般都使用JSON格式。Android系统也原生的提供了JSON解析的API,但是速度非常慢,而且没有提供简洁方便的接口来提高开发者的效率和降低出错的可能。
Gson
Gosn是Google出品的JSON解析函数库,可以将JSON字符串反序列化对应的Java对象,或者反过来将Java对象序列化为对应的JSON字符串,免去了开发者手动通过JSONObject和JSONArray将JSON字段逐个进行解析的烦恼,也减少了出错的可能性,增强了代码的质量。使用Gson解析时,对应的Java实体类无需使用注解进行标记,支持任意复杂Java对象包括没有源代码的对象。
- GsonPlayer gsonPlayer = new Gson()
- .fromJson("{\"age\":0,\"name\":\"姓名\"}"
- ,GsonPlayer.class);
上文提到的数据库框架适合存储大量数据,而存储少量数据时则可以用SharedPreferences保存JSON的形式:
- List list = new ArrayList();
- for (int i=0;i<10;i++){
- Map map = new HashMap();
- map.put("age",i);
- map.put("name",""+i);
- list.add(map);
- }
- String JSON = new Gson().toJson(list);
Jackson
Jackson是Java语言的一个流行的JSON函数库,并没有为Android优化定制过,因此函数保重包含很多非必要的API,用于Android平台会更显著的增大最终生成的APK的体积。
Fastjson
Fastjson是阿里巴巴出品的一个Java语言编写的高性能且功能完善的JSON函数库。它采用一种“假定有序快速匹配”的算法,号称是目前Java语言中最快的JSON库。Fastjson还存在一个专门为Android定制的版本,和标准版本相比,Android版本去掉了一些Dalvik不支持的功能,使得jar包更小。
- FastjsonPlayer fastjsonPlayer = JSON
- .parseObject("{\"age\":0,\"name\":\"姓名\"}"
- ,FastjsonPlayer.class);
【小结】在解析长JSON时Fastjson有明显的速度优势,但解析普通长度的JSON时Gson却略胜一筹。从速度、体积、文档和技术支持、官方推荐等方面综合考虑,Gson毫无疑问是最佳选择。
五、组件间通信
Android中Activity、Service、Fragment等组件以及不同进程之间的相互通信主要有Broadcast和Handler两种方法,但这些方法都比较繁琐。事件总线是一种比较简便的方法,其基本原理是发送者把消息发送到事件总线,然后事件总线在接收者中查找哪些方法注册了这个事件,如果某个方法注册了这个事件,就触发该方法。
EventBus
EventBus是专为一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间、组件与后台线程间的通信。用事件总线原理实现组件间通信可能会造成性能问题:EventBus在发布Event的时候就要做好准备可能并没有人接受这个Event, Subscribe的时候也要做好准备可能永远不会收到Event。Event无论顺序还是时间上都某种程度上不太可控。我们可以利用响应式编程来解决这个问题。
RxJava
RxJava是一个在 JVM 上使用可观测的序列来组成异步的、基于事件的程序的响应式编程框架。如果一个订阅者需要注册多个事件的时候,Rxjava需要一个个单独的注册,而EventBus则可以实现一个订阅者订阅多个事件,和一个事件对应多个订阅者。显然RxJava的性能更好,但EventBus操作更简便。
【小结】组件间通信框架还有Otto和Guava等许多种,但都在某些方面稍有欠缺,因此我们技术选型的范围就在EventBus和RxJava之间,建议小型App选择EventBus,体量中等以上的App选择RxJava或RxAndroid。
六、依赖注入
所谓依赖注入,就是目标类(目标类需要进行依赖初始化的类(JavaBean))中所依赖的其他的类的初始化过程,不是通过手动编码的方式创建(findViewById等),而是通过技术手段(注解和反射等)可以把其他的类的已经初始化好的实例自动注入到目标类中。
AndroidAnnotations
AndroidAnnotations是功能最丰富的依赖注入框架,但存在体积过于庞大、与其他利用注解的框架有冲突、输出太多logcat、需要生成一个以“_”结尾的新类都是与追求编程效率的初衷相违背的。
ButterKnife
ButterKnife具备AndroidAnnotations的大多数主要功能,并且使用简便,这是ButterKnife在Activity中的使用:
- public class ButterKnifeActivity extends AppCompatActivity {
- private Unbinder unbinder;
- @BindString(R.string.str_butter_knife)
- String butterKnifeStr;
- @BindDrawable(R.mipmap.ic_launcher)
- Drawable butterKnifeDrawable;
- @BindView(R.id.iv_butter_knife)
- ImageView butterKnifeIv;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_butter_knife);
- unbinder = ButterKnife.bind(this);
- butterKnifeIv.setImageDrawable(butterKnifeDrawable);
- }
- @OnClick(R.id.btn_butter_knife)
- public void onButterKnifeBtnClick(View view) {
- Toast.makeText(this,butterKnifeStr,Toast.LENGTH_SHORT).show();
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unbinder.unbind();
- }
- }
ButterKnife能增强代码的可读性,提高编程效率,但如果你用Android Studio搜索Android ButterKnife Zelezny插件后,你会发现写以上代码也是浪费时间之举,因为这些都能自动实现。
【小结】AndroidAnnotations和ButterKnife都是牺牲运行性能换取开发速度的工具,体积庞大的AndroidAnnotations对compile速度较大,而ButterKnife对runtime性能的影响略大于AndroidAnnotations。综合考虑之后,为了写出更高质量的代码,也为了debug更简便,我选择了ButterKnife。
【写在最后】
上述Android开源框架的合理利用能加快开发进度,也能提高代码可维护性。除了上述几大框架,Android开源框架还有多媒体、文件I/O、动画等多个大类,合理运用开源框架提高编程效率节省下来的时间可以用于读书、健身或陪陪自己的另一半,早日成为人生赢家。
欢迎扫码加群学习
【51CTO原创稿件,合作站点转载请注明原文作者和出处为51CTO.com】