月度归档:2014年09月

android 开发 常用到的一些网络通信包

无线开发一定少不了与服务端进行交互,这自然离不开使用类似于HttpClient的工具包来发送和接受HTTP请求。常用的有一些这些:
1. Apache 的 HttpClient(Android2.3之前使用)
2. Android 简化扩展版 HttpUrlConnection
3. Google 推出的 Volley(在Android2.3之前使用HttpClient,之后使用HttpUrlConnection)
4. Git开源项目Okhttp (使用http+SPDY协议)
5. Android-async-http
6. Retrofit(默认使用Okhttp作为传输层)
7. Android Query
8. Android AsyncTask

HttpClient 与 HttpUrlConnection对比:

对于HttpClient 大家都比较熟悉,这里就不做多余阐述,为什么使用HttpUrlConnection呢?主要原因是因为HttpClient虽然稳定,但是太庞大了,在Android上不易做升级和扩展,所以才有了轻量级的HttpUrlConnection。HttpUrlConnection相比与HttpClient做的扩展点可以参考下面的博客。
参考:
http://blog.csdn.net/guolin_blog/article/details/12452307
http://android-developers.blogspot.com/2011/09/androids-http-clients.html

Volley中使用的也是HttpUrlConnection,那么Volley在HttpUrlConnection上有没有改进呢?还有待查阅相关资料。

Android自带的AsyncTask:不推荐使用。

弊端:
1. 不支持环境改变;
2. 不能取消网络请求;
3. 没有简单的方法来做并发API调用;
4. 每个时刻只能有一个AsyncTask任务可以运行;
5. ancle()方法并不会起作用;
6. Activity被销毁,还保持Activity的引用时,可能导致内存泄露;
7. Activity reCreated时,可能导致AsyncTask的结果丢失;
8. 串行或并行的不确定性,依赖API Level的不同而不同;
AsyncTas阴暗的一面:http://bon-app-etit.blogspot.in/2013/04/the-dark-side-of-asynctask.html

OkHttp、Volley、Retrofit三者对比:

Volley的特点:
1. Volley的优势在于处理小文件的http请求;
2. 在Volley中也是可以使用Okhttp作为传输层;参考:https://plus.google.com/+JakeWharton/posts/eJJxhkTQ4yU
3. Volley在处理高分辨率的图像压缩上有很好的支持;
4. NetworkImageView在GC的使用模式上更加保守,在请求清理上也更加积极,networkimageview仅仅依赖于强大的内存引用,并当一个新请求是来自ImageView或ImageView离开屏幕时 会清理掉所有的请求数据。
5. Volley比Retrofit在内存错误处理上要更好。
Retrofit的特点:
1. 使用REST API时非常方便;
2. 传输层默认就使用OkHttp;
3. 支持NIO;
4. 拥有出色的API文档和社区支持
5. 速度上比volley更快;
OkHttp的特点:
支持SPDY(请求头压缩、并行请求、强制SSL、服务端推送);

三者的性能基线:
E5454286-839B-4882-9576-852546B742C4
其中Volley与Retrofit的对比,参考:http://instructure.github.io/blog/2013/12/09/volley-vs-retrofit/
三者对比参考:http://stackoverflow.com/questions/16902716/comparison-of-android-networking-libraries-okhttp-retrofit-volley

Advertisements

Android 高效的 Layout

我们知道随着我们业务越来越负责,UI布局也会越来越复杂,大量的布局信息必定会带来一定的性能损耗,那么我们怎么才能写出高效的布局呢?

1. 使用 <include> <merge> <viewStub>标签。

https://developer.android.com/training/improving-layouts/optimizing-layout.html
2. LinearLayout中减少使用layout_weight参数,因为使用此属性会导致子元素被测量两次。

3. 学会测量Layout的层次结构,优化布局
https://developer.android.com/training/improving-layouts/optimizing-layout.html

4. 使得Layout宽而浅,而不是窄而深(在Hierarchy Viewer的Tree视图里面体现)
连接真机时,HierarchyVIewer报错了:无法加载设备上的窗口数据

原因:若需要使用hierarchyviewer,需要手机上开启对应的ViewServer服务。Hierarchy Viewer在连接手机时,手机上必须启动一个叫View Server的客户端与其进行socket通信。而在商业手机上,是无法开启View Server的,故Hierarchy Viewer是无法连接到普通的商业手机。
解决办法:
方法一:使用模拟器。
方法二:App中需要开启View Server,可以参考一下连个类的实现。

http://pan.baidu.com/s/1sjIulYl 提取码:wrc8

使用方法:
引入ViewServer.java 类到Util包中,在需要查看Layout层次结构的Activity类中,
加入以下代码:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        …
        ViewServer.get(this).addWindow(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        ViewServer.get(this).removeWindow(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        ViewServer.get(this).setFocusedWindow(this);
    }

5. 使用Lint工具来优化Layout
> 使用merge根框架 – 如果FramLayout仅仅是一个纯粹的(没有设置背景,间距等)布局根元素,我们可以使用merge标签来当作根标签;
> 无用的分支 – 如果一个layout并没有任何子组件,那么可以被移除,这样可以提高效率
> 无用的父控件 – 如果一个layout只有子控件,没有兄弟控件,并且不是一个ScrollView或者根节点,而且没有设置背景,那么我们可以移除这个父控件,直接把子控件提升为父控件
> 深层次的layout – 尽量减少内嵌的层级,考虑使用更多平级的组件 RelativeLayout or GridLayout来提升布局性能,默认最大的深度是10
Lint工具在android Studio中的使用:
Analyze → Inspect code 这个时候会自动启动Lint工具。

6. 虽然官方说LinearLayout 与 RelationLayout在性能上相差无几,但是能使用relative layout的地方尽量不要使用LinearLayout。

7. 有同事对比了RelationLayout 与 FrameLayout的性能,发现FrameLayout性能比RelationLayout更优。原因很简单:因为FrameLayout比RelationLayout更简单,没有那么多属性,所有效率更高。无论是使用那种布局,在都能满足需求的情况下,只要记住,使用属性跟少、更简单的布局方式一定是没错的。
http://stackoverflow.com/questions/22875453/framelayout-vs-relativelayout-for-overlays

8. 对于textview和imageview组成的layout,直接使用textview替换(drawableTop类似属性)

Android 之 Listview

ListView显示的三要素:

  • ListView,用来展示列表
  • Adapter,用来存在数据&将数据与视图进行绑定
  • 数据,将被映射的字符串、图片url等等。

关于Adapter ,请参考 Android 之 Adapter。

ListView 的工作原理:

Adapter的作用就是ListView界面与数据交互的桥梁,当列表里面每一项显示到页面上时,都会调用Adapter的getView方法。
系统需要回执ListView时,首先会调用getCount()函数,得到要绘制的这个列表额长度,然后开始从第一行开始绘制,每行的回执方法是调用getView函数。那么Android是不是为每一行都会新创建一个View呢?试想加入行数为几万行,内存肯定会爆掉的,所以Android官方早就想到了这一点,在ListView实现中添加了视图的缓存-Recycler,每当有行移除屏幕的可视区域时,这个被移除的行的View对象就会被添加到Recycler中,也就是在渲染新行时的那个参数convertView。

ListView的初始化:

疑问一:ListView绘制时是如何获取每行的View的呢?

首先ListView通过setAdapter方法,将Adapter与ListView关联起来,查看ListView的setAdater方法源码可知,setAdater将传递经来的Adapter的引用复制给了内部全局变量mAdapter,ListView在绘制每行的时候根据行号position调用父类AbsListView中的obtainView,obtainView首先会从recycler中获取是否有匹配的视图,如果存在的话,可以看到则调用adapter.getView方法,并传递了scrapView给convertView变量,否则传递的是null。

疑问二:ListView中数据发生变更了,我们一般会调用Adapter的 notifyDataSetChanged()方法, 那么视图是怎么发生变化的呢?

ListView 中的数据适配器Adapter 采用的是观察者模式(参考之前的”观察者模式“)

ListView在setAdapter时,会新建ApdateDataSetObserver,并注册此观察者。

AdapterDataSetObserver类实在AbsListView中定义:

AdapterView中AdapterDataSetObserver的实现如下(部分省略):

AdapterDataSetObserver 实现了DataSetObserver接口,并重写了onChanger方法,里面调用了requestLayout方法,此方法的作用是要求parent view 重新调用它的哦弄Measure onLayout方法重新布局视图,但不会重新绘制任何视图包括该调用者本身。
requestLayout的实现方法需要到View类中查看:可参考:http://blog.csdn.net/androiddevelop/article/details/8561076

疑问三:RecyclerBin的数据结构是这样的呢?当ListView有多个视图类型(在界面上就是有不同的样式和数据类型)又是怎么选择合适的convertView的呢?

首先看一下RecycleBin的类定义(AbsListView中内部类)

从注释中我们就可以得知 :
RecycleBin一共有两个存储结构分别是ActiveViews 和 ScrapViews
ActiveViews储存当前在界面(手机显示区域)中显示View,移出界面会存入ScrapViews
ScrapViews存储当前已经滑动出当前界面(手机显示区域)显示的View,这些view存储起来相当于回收,当再次请求的时候从此存储中取出反复使用。
当ListView中有N个视图类型时,RecycleBin会创建N个scrapView数组,每个类型一个view数组,后面在获取view时会先判断view的类型,然后到对应的数组中去取。

怎么从ScrapViews中获取可用的view视图呢?getScrapView  → retrieveFromScrap

retrieveFromScrap(这个不属于RecycleBin类,是属于外部类AbslistView中的方法)
   根据position,从mScrapView中找:
        1. 如果有view.scrappedFromPosition = position的,直接返回该view;
        2. 否则返回mScrapView中最后一个;
        3. 如果缓存中没有view,则返回null;
        下面,我们来分析下这三种情况在什么条件下满足?
         a. 第三种情况,这个最简单:
         一开始,listview稳定后,显示N个,此时mScrapView中是没有缓存view的,当我们向上滚动一小段距离(第一个此时仍显示部分),新的view将会显示,此时listview会调用Adapter.getView,但是缓存中没有,因此convertView是null,所以,我们得分配一块内存来创建新的convertView;
         b. 第二种情况:
         在a中,我们继续向上滚动,直接第一个view完全移出屏幕(假设没有新的item),此时,第一个view就会被detach,并被加入到mScrapView中;然后,我们还继续向上滚动,直接后面又将要显示新的item view时,此时,系统会从mScrapView中找position对应的View,显然,是找不到的,则将从mScrapView中,取最后一个缓存的view传递给convertView;
        c. 第一种情况:
        紧接着在b中(标示为橙色的文字后面),第一个被完全移出,加入到mScrapView中,且没有新增的item到listview中,此时,缓存中就只有第一个view;然后,我此时向下滑动,则之前的第一个item,将被显示出来,此时,从缓存中查找position对应的view有没有,当然,肯定是找到了,就直接返回了。

ListView使用过程中遇到的问题:

① Listview 滑动中背景错乱:
场景:我有一个listView作为菜单,当菜单选中时有一个指示器会展示出来,其他的会被隐藏,默认第一个菜单为选中状态,但是当滑动listview时,新加在出来的item中指示器也展示出来了。
原因:理解了上面的getView和RecycleBin的工作原理之后,这个问题不难定位,肯定是在渲染新行时从scropViews中没有匹配position位置的view,此时默认返回了最后一个view(也就是场景中对应的第一个),这个view是被我们设置了指示器为可见状态,所以就引起了背景的错乱。
解决办法:

对于positon等于我们选择的下标时会指示器可见,当position不等于我们选择的下标时,防止convert被复用,我们需要设置指示器为不可见,也就是else中的代码,之前我只写了if中的代码,却忽略了else中的代码。
这个问题也可以参考:http://www.myexception.cn/mobile/1612364.html