Bootstrap

Android网络操作和优化相关

人性的弱点在于习惯于学习精确的东西而不善于总体的把握。

写在前面的话

Android程序最重要的模块就是网络部分,如何从网络上下载数据,如何将处理过的数据上传至网络,往往是android程序的关键环节。前几天偶一朋友遇到这么一个问题:如何使用volley实现文件上传。最后问题解决了,小伙伴不禁有些飘飘然,大有一番天下之事皆逃不过我的魔掌的感觉。这时候coder君问了他几个问题,大家可以一起思考下:

  • TCP/IP协议、SOCKET、HTTP协议、HTTPS协议都是做什么的,他们之间有关系吗
  • 你是如何管理网络使用情况的
  • 常用的HTTP Client有哪些,该如何选择
  • 假如你需要修改的你网络请求框架,你要动多少代码
  • 你是怎么进行网络优化的

小伙伴支支吾吾,其实他熟悉的只是Android中网络相关的那些点中的一小部分,不免有些盲人摸象的感觉,你呢?

网络通信机制

网络由下往上分为:物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

IP协议对应于网络层,TCP协议对应于传输层,而HTTP协议对应于应用层,三者从本质上来说没有可比性,socket则是对TCP/IP协议的封装和应用。也可以说,TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。

TCP/IP协议

网络编程的目的就是直接或间接地通过网络协议与其他计算机进行通讯。

网络编程中有两个主要的问题,一个是如何准确的定位网络上一台或多台主机;另一个就是找到主机后如何可靠高效的进行数据传输。目前使用最广泛的因特网协议是TCP/IP协议。

在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机。而TCP层则提供面向应用的可靠的或非可靠的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。

Socket

我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。

能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

Socket跟TCP/IP协议没有必然的联系。Socket编程接口在设计的时候,就希望也能适应其他的网络协议。所以说,Socket的出现只是使得程序员更方便地使用TCP/IP协议栈而已,是对TCP/IP协议的抽象,

Http协议

HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。

HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

HTTP提供了封装或者显示数据的具体形式。Socket提供了网络通信的能力。

Https协议

HTTPS(全称:Hypertext Transfer Protocol over Secure Socket Layer),是以安全为目标的HTTP通道,简单讲是HTTP的安全版。即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。 它是一个URI scheme(抽象标识符体系),句法类同http:体系。用于安全的HTTP数据传输。https:URL表明它使用了HTTP,但HTTPS存在不同于HTTP的默认端口及一个加密/身份验证层(在HTTP与TCP之间)。被广泛用于万维网上安全敏感的通讯,例如交易支付方面。

Android网络权限

<uses-permission android:name="android.permission.INTERNET" /><-- 允许应用程序打开网络套接字 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><-- 允许应用程序访问网络连接信息 -->

选择一个HTTP Client

大多数连接网络的 Android app 会使用 HTTP 来发送与接收数据。Android 提供了三种 HTTP client:HttpURLConnection、Apache HttpClient和okhttp。二者均支持 HTTPS、流媒体上传和下载、可配置的超时、IPv6 与连接池(connection pooling)。

  • Java.net包中的HttpURLConnection类

    HttpUrlConnection是JDK里提供的联网API,我们知道Android SDK是基于Java的,所以当然优先考虑HttpUrlConnection这种最原始最基本的API,其实大多数开源的联网框架基本上也是基于JDK的HttpUrlConnection进行的封装罢了

  • HttpClient

    HttpClient是开源组织Apache提供的Java请求网络框架,其最早是为了方便Java服务器开发而诞生的,是对JDK中的HttpUrlConnection各API进行了封装和简化,提高了性能并且降低了调用API的繁琐,Android因此也引进了这个联网框架,我们再不需要导入任何jar或者类库就可以直接使用,值得注意的是Android官方已经宣布不建议使用HttpClient了,我们再开发的时候尽量少用吧,但是用了也无妨!

  • okhttp

    http是现在主流应用使用的网络请求方式, 用来交换数据和内容, 有效的使用HTTP可以使你的APP变的更快和减少流量的使用。

    OkHttp是一个很棒HTTP客户端:

    • 支持SPDY,可以合并多个到同一个主机的请求

    • 使用连接池技术减少请求的延迟(如果SPDY是可用的话)

    • 使用GZIP压缩减少传输的数据量

    • 缓存响应避免重复的网络请求

    当你的网络出现拥挤的时候,就是OKHttp大显身手的时候,它可以避免常见的网络问题,如果你的服务是部署在不同的IP上面的,如果第一个连接失败,OkHTtp会尝试其他的连接。这对现在IPv4+IPv6中常见的把服务冗余部署在不同的数据中心上也是很有必要的。OkHttp将使用现在TLS特性(SNI ALPN)来初始化新的连接,如果握手失败,将切换到TLS 1.0。

    使用OkHttp很容易,同时支持异步阻塞请求和回调.

    如果你使用OkHttp ,你不用重写你的代码, okhttp-urlconnection模块实现了 java.net.HttpURLConnection 中的API, okhttp-apache模块实现了HttpClient中的API

如何管理网络的使用情况

如果我们的程序需要执行大量网络操作,那么应该提供用户设置选项,来允许用户控制程序的数据偏好。例如,同步数据的频率,是否只在连接到 WiFi 才进行下载与上传操作,是否在漫游时使用套餐数据流量等等。这样用户才不大可能在快到达流量上限时,禁止我们的程序获取后台数据,因为他们可以精确控制我们的 app 使用多少数据流量。

检查网络连接

在执行网络操作之前,检查设备当前连接的网络连接信息是个好习惯。这样可以防止我们的程序在无意间连接使用了非意向的网络频道。如果网络连接不可用,那么我们的应用应该优雅地做出响应。为了检测网络连接,我们需要使用到下面两个类:

  • ConnectivityManager:它会回答关于网络连接的查询结果,并在网络连接改变时通知应用程序。

  • NetworkInfo:描述一个给定类型(移动网络或 Wi-Fi等)的网络接口状态。

下面这个方法可以找到的第一个已连接的网络接口,如果返回null,则表示没有已连接的网络接口(意味着网络连接不可用):

public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager)getSystemServic(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
} 

管理网络的使用情况

我们可以实现一个偏好设置的选项,使用户能直接设置程序对网络资源的使用情况。例如:

  • 可以允许用户仅在连接到 Wi-Fi 时上传视频。

  • 可以根据诸如网络可用,时间间隔等条件来选择是否做同步的操作。

检测网络连接变化

要检测网络连接的变化需要用到BroadcastReceiver 的子类:NetworkReceiver。当设备网络连接改变时,NetworkReceiver 会监听到 CONNECTIVITY_ACTION,这时需要判断当前网络连接类型并相应的设置好 wifiConnected 与 mobileConnected。

这里需要注意的是在使用BroadcastReceiver的时候,不必要的声明注册会浪费系统资源。最好在 onCreate()中注册 BroadcastReceiver NetworkReceiver,在 onDestroy()中销毁它。这样做会比在 manifest 里面声明 更轻巧。

在一个单独的线程中执行网络操作

网络操作会遇到不可预期的延迟。为了避免造成不好的用户体验,确保总是在 UI 线程之外单独的线程中执行网络操作。关于Android中的线程和进程可以参考这里

封装网络请求

有了Http Client,我们需要考虑的是如何优雅的使用这些Http Client进行网络请求。这时候我们可以对Http Clent进行封装来满足我们的需求。封装的基本要点如下:

  • 支持自定义请求的Header
  • 支持Http的基本请求方法:GET、POST、PUT、DELETE等
  • 支持文件上传和下载
  • 可以加载图片
  • 支持多任务网络请求操作
  • 支持缓存
  • 支持请求回调
  • 支持session的保持

具体可以参考Volley源码分析Retrofit分析等,看看这些成名已久的框架是如何封装的。

网络请求框架

以下是曾经很火或者现在很多的网络请求框架:

  • Android Async HTTP
  • AndroidAsync
  • Volley
  • Retrofit

AsyncHttp是一个较高层的封装,底层使用的是HttpClient。

Volley是Google推出的Android异步网络请求框架和图片加载框架。底层网络请求可以使用不同的网络库来处理,比如OkHttp,HttpClient。

Retrofit是一个封装比较好的,相对更面向开发者的rest请求库,它的底层网络请求也可以使用不同的网络库来处理。

网络请求框架再封装

我在这些Tips让你的App更容易维护 简单提到过面向接口编程,这对于网络框架再封装很实用。

我们可以定义一套我们自己的网络请求接口,接口规定我们需要有什么能力的框架。调用的话我们可以直接调用接口方法就好了,这样不管我们换什么网络框架,只要这个框架有网络请求的能力就行。举个例子,定义接口如下:

public interface ISender {

    void send(METHOD method, String url, SenderCallback callback);

}

我们写一个类实现这个接口:

public class BaseSender implements ISender {
    @Override
    public synchronized void sender(METHOD method, final String url, final SenderCallback callback) {
        //volley,retrofit,balabala,用任意网络请求框架实现。

    }
}

然后是我们的SendCallback

public interface SendCallback {

    //请求成功回调
    void onSucceeed(String t);

    //请求失败回调
    void onError(String errorMsg);

}

最后是我们的调用:

private ISender mHttp;

public void getWeahter(String tag) {

    //调用接口里定义好的方法
    mHttp.sender();

}

这里传入我们需要的参数就好了。

数据解析

以下是比较流行的网络数据解析的库:

  • Gson
  • Jackson
  • FastJson
  • HtmlPaser
  • Jsoup

网络数据解析比较基础,这里就不过多描述了。

移动端网络优化

对于手机程序,网络操作相对来说是比较耗电的行为。优化网络操作能够显著节约电量的消耗。一个网络请求可以简单分为连接服务器获取数据两个部分。其中连接服务器前还包括 DNS 解析的过程;获取数据后可能会对数据进行缓存。那么我们如果要进行网络优化需要从这两个关键点入手。

以下是具体可以优化的点:

  • 不用域名,用IP直连

    省去 DNS 解析过程,DNS 全名 Domain Name System,解析意指根据域名得到其对应的IP地址。

  • 服务器合理部署

    服务器多运营商多地部署,一般至少含三大运营商、南中北三地部署。

  • 根据当前的网络环境选择当下最佳的策略

    主要的实施步骤有两步:第1是检测收集当前的网络环境信息,第2是根据当前收集到的信息进行网络请求行为的调整。

  • 预取

    我们需要预先判断用户在此次操作之后,后续零散的请求是否很有可能会马上被触发,可以把后面几分钟有可能会使用到的零散请求都一次集中执行完毕。

  • 连接复用

    节省连接建立时间,如开启 keep-alive。

  • 用捆绑批量访问的方式来减少访问的频率

    预先判定那些可能马上就会使用到的网络资源,捆绑一起集中进行网络请求。这点我们可以考虑按照提前预期后续1-2分钟的数据作为基准。

  • 分优先级、延迟部分请求

    首先我们需要区分哪些网络请求是需要及时返回结果的,哪些是可以延迟执行的。我们可以有针对性的把请求行为捆绑起来,延迟到某个时刻统一发起请求。

  • 对传输的数据进行压缩

    网络传输数据量的大小主要由两部分组成:图片与序列化的数据,那么我们需要做的就是减少这两部分的数据传输大小。

  • 多连接

    对于较大文件,如大图片、文件下载可考虑多连接。 需要控制请求的最大并发量,毕竟移动端网络受限。

  • 避免不必要的同步操作

    应用程序的一个基础功能是能够保持确保界面上呈现的信息是即时最新的,例如呈现最新的新闻,天气,信息流等等信息。但是,过于频繁的促使手机客户端应用去同步最新的服务器数据会对性能产生很大的负面影响,不仅仅使得CPU不停的在工作,内存,网络流量,电量等等都会持续的被消耗,所以在进行网络请求操作的时候一定要避免多度同步操作。

  • 做好网络数据的缓存

    从手机的缓存中直接读取数据肯定比从网络上获取数据要更加的便捷高效,特别是对于那些会被频繁访问到的数据,需要把这些数据缓存到设备上,以便更加快速的进行访问。

结语

关注博主是一种态度,评论博主是一种欣赏!!

最后,欢迎大家关注我的微信公众号:CoderTopia。

参考链接:

http://www.trinea.cn/android/mobile-performance-optimization/

http://hukai.me/android-performance-patterns-season-4/

http://hukai.me/android-performance-patterns-season-2/

;