android网络框架及网络的优化

1、网络通讯

              http(比如okhttp框架)

             httpUrlConnection安卓的基础网络(volley框架)

            httpClient也是安卓的基础网络但是已经不推荐使用新的api不支持了(android-async-http框架)

            socket一般使用框架AndroidAsync

             webservice一般使用ksoap2-android框架   soap是一种协议

             webview能加载显示网页,可以将其视为一个浏览器。它使用了WebKit渲染引擎加载显示网页

 

 

  1. volley是一个简单的异步http库,仅此而已。缺点是不支持同步,这点会限制开发模式;不能post大数据,所以不适合用来上传文件。
  2. android-async-http。与volley一样是异步网络库,但volley是封装的httpUrlConnection,它是封装的httpClient,而android平台不推荐用HttpClient了,所以这个库已经不适合android平台了。
  3. okhttp是高性能的http库,支持同步、异步,而且实现了spdy、http2、websocket协议,api很简洁易用,和volley一样实现了http协议的缓存。picasso就是利用okhttp的缓存机制实现其文件缓存,实现的很优雅,很正确,反例就是UIL(universal image loader),自己做的文件缓存,而且不遵守http缓存机制。
  4. retrofit与picasso一样都是在okhttp基础之上做的封装,项目中可以直接用了。

okhttp和retrofit职责不同:

  • • Retrofit主要负责应用层面的封装,就是说主要面向开发者,方便使用,比如请求参数,响应数据的处理,错误处理等等。
  • • OkHttp主要负责socket部分的优化,比如多路复用,buffer缓存,数据压缩等等。

另外

  • AndroidAsync这个网络库使用了nio的方式实现的。okhttp没有提供nio selector的方式,不过nio更适合大量连接的情况,对于移动平台有点杀鸡用牛刀的味道。
  • picasso、uil都不支持inbitmap,项目中有用到picasso的富图片应用需要注意这点。

 

 

 

在Android中几种网络编程的方式:
(1)针对TCP/IP的Socket、ServerSocket,使用第三方框架AndroidAsync处理
(2)针对直接URL的HttpURLConnection
(3)针对android客户端的httpClient,不过在api21  以上不能使用
(4)Http可以使用okhttp框架
(5)使用Web Service。可以用Ksoap2去实现Webservice 
  (6) 直接使用WebView视图组件显示网页。

 (7)html使用jsoup

 

 

4、 网络优化

网络优化主要从三个方面进行:1. 速度;2. 成功率;3. 流量。

4.1 Gzip压缩

HTTP协议上的Gzip编码是一种用来改进WEB应用程序性能的技术,用来减少传输数据量大小,减少传输数据量大小有两个明显的好处:

  • 可以减少流量消耗;
  • 可以减少传输的时间。

4.2 IP直连与HttpDns;

DNS解析的失败率占联网失败中很大一种,而且首次域名解析一般需要几百毫秒。针对此,我们可以不用域名,才用IP直连省去 DNS 解析过程,节省这部分时间。

另外熟悉阿里云的小伙伴肯定知道HttpDns:HttpDNS基于Http协议的域名解析,替代了基于DNS协议向运营商Local DNS发起解析请求的传统方式,可以避免Local DNS造成的域名劫持和跨网访问问题,解决域名解析异常带来的困扰。

4.3 图片处理

4.3.1 图片下载

  • 使用WebP格式;同样的照片,采用WebP格式可大幅节省流量,相对于JPG格式的图片,流量能节省将近 25% 到 35 %;相对于 PNG 格式的图片,流量可以节省将近80%。最重要的是使用WebP之后图片质量也没有改变。
  • 使用缩略图;App中需要加载的图片按需加载,列表中的图片根据需要的尺寸加载合适的缩略图即可,只有用户查看大图的时候才去加载原图。不仅节省流量,同时也能节省内存!之前使用某公司的图片存储服务在原图链接之后拼接宽高参数,根据参数的不同返回相应的图片。

4.3.2 图片上传

图片(文件)的上传失败率比较高,不仅仅因为大文件,同时带宽、时延、稳定性等因素在此场景下的影响也更加明显;

  • 避免整文件传输,采用分片传输;
  • 根据网络类型以及传输过程中的变化动态的修改分片大小;
  • 每个分片失败重传的机会。

备注:图片上传是一项看似简单、共性很多但实际上复杂、需要细分的工作。移动互联网的场景和有线的场景是有很多区别的,例如移动网络的质量/带宽经常会发生“跳变”,但有线网络却是“渐变”。

图片上传其它细节请参见《移动App性能评测与优化》一书。

4.4 协议层的优化

使用最新的协议,Http协议有多个版本:0.9、1.0、1.1、2等。新版本的协议经过再次的优化,例如:

  • Http1.1版本引入了“持久连接”,多个请求被复用,无需重建TCP连接,而TCP连接在移动互联网的场景下成本很高,节省了时间与资源;
  • Http2引入了“多工”、头信息压缩、服务器推送等特性。

新的版本不仅可以节省资源,同样可以减少流量;我对Http2并没有实际接入经验,此处仅从原理进行分析。

4.5 请求打包

合并网络请求,减少请求次数。对于一些接口类如统计,无需实时上报,将统计信息保存在本地,然后根据策略统一上传。这样头信息仅需上传一次,减少了流量也节省了资源。

4.6 网络缓存

对服务端返回数据进行缓存,设定有效时间,有效时间之内不走网络请求,减少流量消耗。对网络的缓存可以参见HttpResponseCache。

备注:我们也可以自定义缓存的实现,一些网络库例如:Volley、Okhttp等都有好的实践供参考。

4.7 网络状态

根据网络状态对网络请求进行区别对待,2G与Wifi状态下网络质量肯定是不一样的,那对应的网络策略也应该是不一样的。例如:在Wifi场景下可以进行数据的预取、一些统计的集中上传等;而在2G场景下此类操作以及网络请求的次数策略都应该调低。网络状态可以由TelephonyManager.getNetworkType()方法获取到。

android网络框架及网络的优化_第1张图片

备注:还可以使用Facebook的开源库network-connection-class来做网络状态的判断。

4.8 其它

  • 断点续传,文件、图片等的下载,采用断点续传,不浪费用户之前消耗过的流量;

  • 重试策略,一次网络请求的失败,需要多次的重试来断定最终的失败,可以参考Volley的重试机制实现。

  • Protocol Buffer
    Protocol Buffer是Google的一种数据交换的格式,它独立于语言,独立于平台。相较于目前常用的Json,数据量更小,意味着传输速度也更快。

    具体的对比可以参见:《Protobuffer和json深度对比》。

  • 尽量避免客户端的轮询,而使用服务器推送的方式;

  • 数据更新采用增量,而不是全量,仅将变化的数据返回,客户端进行合并,减少流量消耗;

5、 其它

  • 对于网络优化,实际上和内存优化一样,是一项投入巨大的事情。提升网络的成功率尤为困难。因此建议优先进行流量优化,减少干扰项;
  • 弱网不仅仅指代网络不好,移动互联网的网络带宽很容易出现“跳变”,下一秒的传送速度可能降到前一秒的几十分之一;而且即便是信号满格也传不出一个字节;
  • 对于真正的弱网,可以使用抓包工具进行模拟,也有聪明的小伙伴使用wifi精灵进行限速;
  • Facebook的开源项目augmented-traffic-control可以模拟不同的网络环境,针对带宽、时延抖动、丢包率、错包率、包重排序率等方面,堪称弱网调试神器;
  • 针对网络请求对电量的损耗,本文暂时不提,在下一篇文章中细说。


 

 

 

上传

  1.  /** 
  2.      *  
  3.      * @param params 
  4.      *            传递的普通参数 
  5.      * @param uploadFile 
  6.      *            需要上传的文件名 
  7.      * @param fileFormName 
  8.      *            需要上传文件表单中的名字 
  9.      * @param newFileName 
  10.      *            上传的文件名称,不填写将为uploadFile的名称 
  11.      * @param urlStr 
  12.      *            上传的服务器的路径 
  13.      * @throws IOException 
  14.      */  
  15.     public void uploadForm(Map params, String fileFormName,  
  16.             File uploadFile, String newFileName, String urlStr)  
  17.             throws IOException {  
  18.         if (newFileName == null || newFileName.trim().equals("")) {  
  19.             newFileName = uploadFile.getName();  
  20.         }  
  21.   
  22.         StringBuilder sb = new StringBuilder();  
  23.         /** 
  24.          * 普通的表单数据 
  25.          */  
  26.         for (String key : params.keySet()) {  
  27.             sb.append("--" + BOUNDARY + "\r\n");  
  28.             sb.append("Content-Disposition: form-data; name=\"" + key + "\""  
  29.                     + "\r\n");  
  30.             sb.append("\r\n");  
  31.             sb.append(params.get(key) + "\r\n");  
  32.         }  
  33.         /** 
  34.          * 上传文件的头 
  35.          */  
  36.         sb.append("--" + BOUNDARY + "\r\n");  
  37.         sb.append("Content-Disposition: form-data; name=\"" + fileFormName  
  38.                 + "\"; filename=\"" + newFileName + "\"" + "\r\n");  
  39.         sb.append("Content-Type: image/jpeg" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType  
  40.         sb.append("\r\n");  
  41.   
  42.         byte[] headerInfo = sb.toString().getBytes("UTF-8");  
  43.         byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");  
  44.         System.out.println(sb.toString());  
  45.         URL url = new URL(urlStr);  
  46.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  47.         conn.setRequestMethod("POST");  
  48.         conn.setRequestProperty("Content-Type",  
  49.                 "multipart/form-data; boundary=" + BOUNDARY);  
  50.         conn.setRequestProperty("Content-Length", String  
  51.                 .valueOf(headerInfo.length + uploadFile.length()  
  52.                         + endInfo.length));  
  53.         conn.setDoOutput(true);  
  54.   
  55.         OutputStream out = conn.getOutputStream();  
  56.         InputStream in = new FileInputStream(uploadFile);  
  57.         out.write(headerInfo);  
  58.   
  59.         byte[] buf = new byte[1024];  
  60.         int len;  
  61.         while ((len = in.read(buf)) != -1)  
  62.             out.write(buf, 0, len);  
  63.   
  64.         out.write(endInfo);  
  65.         in.close();  
  66.         out.close();  
  67.         if (conn.getResponseCode() == 200) {  
  68.             System.out.println("上传成功");  
  69.         }  
  70.   
  71.     }  

 

 

 

下载

private class DownThread extends Thread {

    @Override
    public void run() {
        super.run();
        String urlStr = "http://xcmserver.b0.upaiyun.com/v1.0/app/" + apatchName;

        try {

            File file = new File(newFilename);
            if (file.exists()) {
                urlStr = "http://xcmserver.b0.upaiyun.com/v1.0/app/" + apatchName.replace(".apatch", "new.apatch");
            }


            Log.i("DownLoadPatchThread", "正在下载中" + urlStr);
            // 构造URL
            URL url = new URL(urlStr);
            // 打开连接
            URLConnection con = url.openConnection();
            if (con != null) {
                // 输入流
                InputStream is = con.getInputStream();

                File filePath = new File(commonPath);
                if (!filePath.exists()) {
                    boolean bool = filePath.mkdirs();
                    if (bool) Log.e("DownLoadPatchThread", "创建文件夹");
                }


                //如果目标文件已经存在,则删除。产生覆盖旧文件的效果
                if (file.exists()) {
                    boolean bool = file.delete();
                    if (bool) Log.e("DownLoadPatchThread", "删除文件");
                }

                // 1K的数据缓冲
                byte[] bs = new byte[1024];
                // 读取到的数据长度
                int len;
                // 输出的文件流
                if (!StringUtils.isEmpty(newFilename)) {
                    OutputStream os = new FileOutputStream(newFilename);
                    // 开始读取
                    while ((len = is.read(bs)) != -1) {
                        os.write(bs, 0, len);
                    }
                    // 完毕,关闭所有链接
                    os.close();
                }
                is.close();
                Log.i("DownLoadPatchThread", "下载完毕");

                File file_out = new File(newFilename);
                //如果目标文件已经存在,则删除。产生覆盖旧文件的效果
                if (file_out.exists()) {
                    if (MyApplication.getInstance().getmPatchManager() != null) {
                        MyApplication.getInstance().getmPatchManager().removeAllPatch();
                        MyApplication.getInstance().getmPatchManager().addPatch(newFilename);
                        Log.i("DownLoadPatchThread", newFilename + "加载补丁完毕");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

断点续传

 

 

  1. public class DownLoadUtil {  
  2.     //构造方法略  
  3.     public void download(){  
  4.         List lists = threadDAO.get(fileInfo.getUrl());  
  5.         FileInfo info = null;  
  6.         if(lists.size() == 0){  
  7.             //第一次下载,创建子线程下载  
  8.             new MyThread(fileInfo).start();  
  9.         }else{  
  10.             //中间开始的  
  11.             info = lists.get(0);  
  12.             new MyThread(info).start();  
  13.         }  
  14.     }  
  15.   
  16.     class MyThread extends Thread{  
  17.         private FileInfo info = null;  
  18.         public MyThread(FileInfo threadInfo) {  
  19.             this.info = threadInfo;  
  20.         }  
  21.         @Override  
  22.         public void run() {  
  23.             //向数据库添加线程信息  
  24.             if(!threadDAO.isExits(info.getUrl())){  
  25.                 threadDAO.insert(info);  
  26.             }  
  27.             HttpURLConnection urlConnection = null;  
  28.             RandomAccessFile randomFile =null;  
  29.             InputStream inputStream = null;  
  30.             try {  
  31.                 URL url = new URL(info.getUrl());  
  32.                 urlConnection = (HttpURLConnection) url.openConnection();  
  33.                 urlConnection.setConnectTimeout(3000);  
  34.                 urlConnection.setRequestMethod("GET");  
  35.                 //设置下载位置  
  36.                 int start = info.getStart() + info.getNow();  
  37.                 urlConnection.setRequestProperty("Range","bytes=" + start + "-" + info.getLength());  
  38.   
  39.                 //设置文件写入位置  
  40.                 File file = new File(DOWNLOAD_PATH,FILE_NAME);  
  41.                 randomFile = new RandomAccessFile(file, "rwd");  
  42.                 randomFile.seek(start);  
  43.   
  44.                 //向Activity发广播  
  45.                 Intent intent = new Intent(ACTION_UPDATE);  
  46.                 finished += info.getNow();  
  47.   
  48.                 if (urlConnection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {  
  49.                     //获得文件流  
  50.                     inputStream = urlConnection.getInputStream();  
  51.                     byte[] buffer = new byte[512];  
  52.                     int len = -1;  
  53.                     long time = System.currentTimeMillis();  
  54.                     while ((len = inputStream.read(buffer))!= -1){  
  55.                         //写入文件  
  56.                         randomFile.write(buffer,0,len);  
  57.                         //把进度发送给Activity  
  58.                         finished += len;  
  59.                         //看时间间隔,时间间隔大于500ms再发  
  60.                         if(System.currentTimeMillis() - time >500){  
  61.                             time = System.currentTimeMillis();  
  62.                             intent.putExtra("now",finished *100 /fileInfo.getLength());  
  63.                             context.sendBroadcast(intent);  
  64.                         }  
  65.                         //判断是否是暂停状态  
  66.                         if(isPause){  
  67.                             threadDAO.update(info.getUrl(),finished);  
  68.                             return//结束循环  
  69.                         }  
  70.                     }  
  71.                     //删除线程信息  
  72.                     threadDAO.delete(info.getUrl());  
  73.                 }  
  74.             }catch (Exception e){  
  75.                 e.printStackTrace();  
  76.             }finally {//回收工作略  
  77.             }  
  78.         }  
  79.     }  
  80. }  

 

 

 

 

你可能感兴趣的:(android)