Android网络请求心路历程

同步&异步

那三个概念仅存在于多线程编程中。
android中私行认同唯有2个主线程,也叫UI线程。因为View绘制只可以在那一个线程内进行。
之所以只要您阻塞了(有些操作使这么些线程在此间运转了N秒)这一个线程,这中间View绘制将不只怕展开,UI就会卡。所以要恪尽幸免在UI线程举行耗时操作。
网络请求是一个金榜题名耗时操作。
经过地点的Utils类举行互联网请求惟有一行代码。

NetUtils.get("http://www.baidu.com");//这行代码将执行几百毫秒。

假定你如此写

        @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String response = Utils.get("http://www.baidu.com");
    }

就会死。。
那就是同台方式。直接耗时操作阻塞线程直到数据接收已毕然后重返。Android不容许的。
异步格局:

         //在主线程new的Handler,就会在主线程进行后续处理。
    private Handler handler = new Handler();
    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text);
        new Thread(new Runnable() {
            @Override
            public void run() {
                    //从网络获取数据
                final String response = NetUtils.get("http://www.baidu.com");
                    //向Handler发送处理操作
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                            //在UI线程更新UI
                        textView.setText(response);
                    }
                });
            }
        }).start();
    }

在子线程举办耗时操作,完结后经过Handler将更新UI的操作发送到主线程执行。那就叫异步。Handler是三个Android线程模型中要害的事物,与网络无关便不说了。关于Handler不精通就先去谷歌一下。
至于Handler原理一篇不错的小说

但这么写好丢人。异步日常伴随者他的好基友回调
这是通过回调封装的Utils类。

    public class AsynNetUtils {
        public interface Callback{
            void onResponse(String response);
        }

        public static void get(final String url, final Callback callback){
            final Handler handler = new Handler();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    final String response = NetUtils.get(url);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onResponse(response);
                        }
                    });
                }
            }).start();
        }

        public static void post(final String url, final String content, final Callback callback){
            final Handler handler = new Handler();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    final String response = NetUtils.post(url,content);
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.onResponse(response);
                        }
                    });
                }
            }).start();
        }
    }

接下来接纳格局。

    private TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.webview);
        AsynNetUtils.get("http://www.baidu.com", new AsynNetUtils.Callback() {
            @Override
            public void onResponse(String response) {
                textView.setText(response);
            }
        });

是否优雅很多。
嗯,1个蠢到哭的网络请求方案成型了。
弱质的地方有众多:

  • 每一次都new Thread,new Handler消耗过大
  • 从未有过极度处理体制
  • 并未缓存机制
  • 一贯不健全的API(请求头,参数,编码,拦截器等)与调试情势
  • 没有Https

依照在此以前查好的攻略,大家买了飞机场旅游客车的票,准备前往预订好的小吃摊。就在那时,大家经历了第1回乌龙,以前租好的WiFi被果果的水杯漏水泡坏了!那下大家两是真的不太淡定了,毕竟身在外国,手机什么的不大概打电话,只好通过微信联系,假若WiFi坏了就相当于我们多个跟家里失联了,那不过个大事啊!一想到那,我拉着果果一下冲下了地铁,跟司机肯定过票可以坐下一班了解后,马上去机场又租了二个WiFi,就算多花了钱,但终于是解决了难题了。

Retrofit&RestAPI

Retrofit巨大的简化了网络请求的操作,它应该说只是三个Rest
API管理库,它是直接动用OKHttp进行互联网请求并不影响你对OkHttp进行布置。终究都以Square集团出品。
RestAPI是一种软件设计风格。
服务器作为财富存放地。客户端去哀求GET,PUT,
POST,DELETE能源。并且是无状态的,没有session的加入。
运动端与服务器交互最首要的就是API的宏图。比如那是3个业内的记名接口。

Paste_Image.png

你们应当看的出那么些接口对应的央浼包与响应包大概是何等体统吗。
恳请形式,请求参数,响应数据,都很清楚。
行使Retrofit那个API可以直观的反映在代码中。

Paste_Image.png

下一场使用Retrofit提须求您的这一个接口的兑现类
就能直接进行互联网请求得到结构数据。

瞩目Retrofit2.0相较1.9展开了大量不协作更新。google上半数以上科目都以依照1.9的。这里有个2.0的教程。

学Corey展开异步请求是采取Call。Retrofit最强大的地点在于帮忙RxJava。就像是本人上图中回到的是二个Observable。HavalxJava上手难度相比高,但用过就再也离不开了。Retrofit+OkHttp+HavalxJava合作框架打出成吨的输出,这里不再多说。

互连网请求学习到那边作者觉得已经到顶了。。

而是机场的大巴还真是可爱啊!

图片管理方案

再则说图片存储。不要存在自身服务器上边,徒增流量压力,还从未图片处理作用。
推荐七牛Ali云存储(没用过其他π__π
)。它们都有很关键的一项图片处理。在图纸Url上添加参数来对图片举办一些拍卖再传输。
于是乎(七牛的处理代码)

    public static String getSmallImage(String image){
        if (image==null)return null;
        if (isQiniuAddress(image)) image+="?imageView2/0/w/"+IMAGE_SIZE_SMALL;
        return image;
    }

    public static String getLargeImage(String image){
        if (image==null)return null;
        if (isQiniuAddress(image)) image+="?imageView2/0/w/"+IMAGE_SIZE_LARGE;
        return image;
    }

    public static String getSizeImage(String image,int width){
        if (image==null)return null;
        if (isQiniuAddress(image)) image+="?imageView2/0/w/"+width;
        return image;
    }

既可以加速请求速度,又能压缩流量。再协作Fresco或Glide。完美的图样加载方案。
而是那就必要你把拥有图片都存放在七牛或Ali云,那样也不错。

图形/文件上传也都是运用它们第③方存储,它们都有SDK与官方文档教你。
只是图片一定要削减过后上传。上传1-2M大的高清照片没意义。

大雨淅淅沥沥

互联网请求是android客户端很要紧的有的。上面从入门级初步介绍下本人Android互连网请求的施行进程。希望能给刚接触Android网络部分的爱侣一些增援。
正文包罗:

二零一六年圣诞节前两周,果果突然问作者想不想出去玩,小编自然满怀和颜悦色的许诺了,然后我们俩就从头研讨旅行的目标地。探究来研究去,都认为国内秋天可玩的不多,于是暗搓搓的想着怎么能出国玩一趟。

Get&Post

网络请求中大家常用键值对来传输参数(少部分api用json来传递,终究不是主流)。
因此地方的牵线,可以见到纵然Post与Get本意二个是表单提交壹个是伸手页面,但本质并没有怎么分别。上边说说参数在那2者的职位。

  • Get方式
    在url中填入参数:

      http://xxxx.xx.com/xx.php?params1=value1&params2=value2
    

竟然使用路由

    http://xxxx.xx.com/xxx/value1/value2/value3

这么些就是web服务器框架的事了。

  • Post方式
    参数是经过编码放在请求体中的。编码包涵x-www-form-urlencoded
    form-data
    x-www-form-urlencoded的编码格局是那般:
    tel=13637829200&password=123456
    form-data的编码格局是这么:
    —-WebKitFormBoundary7MA4YWxkTrZu0gW
    Content-Disposition: form-data; name=”tel”

      13637829200
      ----WebKitFormBoundary7MA4YWxkTrZu0gW
      Content-Disposition: form-data; name="password"
    
      123456
      ----WebKitFormBoundary7MA4YWxkTrZu0gW
    

x-www-form-urlencoded的优越性就很领悟了。但是x-www-form-urlencoded不得不传键值对,不过form-data可以传二进制

因为url是存在于请求行中的。
于是Get与Post不相同本质就是参数是位于请求行中大概放在请求体
当然无论用哪一种都能放在请求头中。一般在请求头中放一些发送端的常量。

有人说:

  • Get是明文,Post隐藏
    移步端不是浏览器,不用https全都是当着。
  • Get传递数据上限XXX
    胡说。有限定的是浏览器中的url长度,不是Http协议,移动端请求无影响。Http服务器部分有限制的装置一下即可。
  • Get中文必要编码
    是真的…要注意。URLEncoder.encode(params, "gbk");

抑或提议用post规范参数传递格局。并不曾什么更可以,只是大家都如此社会更和谐。

上边说的是请求。上边说响应。
恳请是键值对,但重回数据我们常用Json。
对此内存中的社团数据,肯定要用数据描述语言将目的序列化成文本,再用Http传递,接收端并从文本还原成结构数据。
对象(服务器)<–>文本(Http传输)<–>对象(移动端) 。

服务器重回的多寡一大半都以扑朔迷离的结构数据,所以Json最契合。
Json解析库有很多谷歌(Google)的Gson,阿里的FastJson
Gson的用法看这里

果果的护照一办下来,大家就在网上订购了饭馆和机票,小欢乐哦。

Volley&OkHttp

Volley&OkHttp应该是以后最常用的互连网请求库。用法也特别相似。都以用构造请求进入请求队列的点子管理互连网请求。

先说Volley:
Volley可以透过这个库开展器重.
Volley在Android 2.3及以上版本,使用的是HttpU瑞鹰LConnection,而在Android
2.2及以下版本,使用的是HttpClient。
Volley的主干用法,网上资料无数,那里推荐郭霖大神的博客
Volley存在3个缓存线程,1个网络请求线程池(暗许四个线程)。
Volley这样直接用支付成效会相比低,笔者将自作者动用Volley时的各类技术封装成了一个库RequestVolly.
自家在这一个库准将构造请求的方法封装为了函数式调用。维持贰个大局的伸手队列,拓展一些有益于的API。

不过再怎么封装Volley在效益拓展性上一味不大概与OkHttp相比。
Volley截至了履新,而OkHttp得到了官方的认可,并在不断优化。
故此作者最后替换为了OkHttp

OkHttp用法见这里
很友好的API与详尽的文档。
那篇小说也写的很详细了。
OkHttp使用Okio举办数据传输。都是Square家的。
但并不是直接用OkHttp。Square公司还出了二个Retrofit库同盟OkHttp战斗力翻倍。

果果也抓紧时间去办了护照,不过护照办下去至少要求两周,那就意味着大家从马时间办签注了,导致我们只好采用免签大概落地签的国家了。考虑到我们连个女孩子第5回走出国门,而且意国语水平及其一般,再加上想自由行,所以最后敲定了毛里求斯当作目标地。

HTTP请求&响应

既然说从入门级初叶就说说Http请求包的布局。
五次呼吁就是向目的服务器发送一串文本。什么样的公文?有上面结构的公文。
HTTP请求包结构

请求包

例子:

    POST /meme.php/home/user/login HTTP/1.1
    Host: 114.215.86.90
    Cache-Control: no-cache
    Postman-Token: bd243d6b-da03-902f-0a2c-8e9377f6f6ed
    Content-Type: application/x-www-form-urlencoded

    tel=13637829200&password=123456

请求了就会接收响应包(即使对面存在HTTP服务器)
HTTP响应包结构

响应包

例子:

    HTTP/1.1 200 OK
    Date: Sat, 02 Jan 2016 13:20:55 GMT
    Server: Apache/2.4.6 (CentOS) PHP/5.6.14
    X-Powered-By: PHP/5.6.14
    Content-Length: 78
    Keep-Alive: timeout=5, max=100
    Connection: Keep-Alive
    Content-Type: application/json; charset=utf-8

    {"status":202,"info":"\u6b64\u7528\u6237\u4e0d\u5b58\u5728\uff01","data":null}

Http请求格局有

方法 描述
GET 请求指定url的数据,请求体为空(例如打开网页)。
POST 请求指定url的数据,同时传递参数(在请求体中)。
HEAD 类似于get请求,只不过返回的响应体为空,用于获取响应头。
PUT 从客户端向服务器传送的数据取代指定的文档的内容。
DELETE 请求服务器删除指定的页面。
CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
OPTIONS 允许客户端查看服务器的性能。
TRACE 回显服务器收到的请求,主要用于测试或诊断。

常用唯有Post与Get。

因为都依旧学生,所以采用的都是相比便利的艳羡航班,凌晨1点从马尔默起程。那时候西安的候机室大概一向不什么样人了,有也都以相同航班的,看起来都像是打了鸡血的血拼团的人,大家五人多少某个格格不入。

Fresco&Glide

不用想也驾驭它们都做了要命周详的优化,重复造轮子的行事很蠢。
Fresco是非死不可公司的黑科技(science and technology)。光看功效介绍就见到非常有力。使用方法官方博客说的够详细了。
真三级缓存,变换后的BItmap(内存),变换前的原有图片(内存),硬盘缓存。
在内存管理上达成了极致。对于重度图片应用的APP应该是分外好的。
它一般是一贯动用SimpleDraweeView来替换ImageView,呃~侵入性较强,依赖上它apk包直接大1M。代码量惊人。

于是作者更爱好Glide,小编是bumptech。那个库被周边的采纳在google的开源项目中,包括二零一六年google
I/O大会上发布的官方app。
这里有详实介绍。直接采纳ImageView即可,无需初阶化,极简的API,丰硕的进行,链式调用都以本人欣赏的。
添加的拓展指的就是这个
此外小编也用过Picasso。API与Glide大约一模一样,效用略少,且有5个月未修复的BUG。

说来也是巧,大家多少个都不爱看台湾电视剧,对大韩民国知识也并不胃痛,最大的问询推测就是近两年开满大街的高丽国烤肉和石锅拌饭了啊。然则苏梅岛当做南韩出名的恬淡度假胜地,再添加还是圣诞之间,想着下雪天和纪念日气氛必将也是很不利的。

三级缓存

网上常说三级缓存--服务器,文件,内存。但是本人觉得服务器不到底一级缓存,那就是多少源嘛。

  • 内存缓存
    率先内存缓存使用LruCache。LRU是Least Recently Used
    近来起码使用算法,那里确定1个大大小小,当Map里对象大小总和大于这些大时辰将使用功能低于的靶子释放。笔者将内存大小限制为经过可用内存的百分之十二.
    内存缓存里读拿到的数量就一贯回到,读不到的向硬盘缓存要多少。

  • 硬盘缓存
    硬盘缓存使用DiskLruCache。这一个类不在API中。得复制利用。
    看见LRU就了然了呢。小编将硬盘缓存大小设置为100M。

      @Override
      public void putBitmap(String url, Bitmap bitmap) {
          put(url, bitmap);
          //向内存Lru缓存存放数据时,主动放进硬盘缓存里
          try {
              Editor editor = mDiskLruCache.edit(hashKeyForDisk(url));
              bitmap.compress(Bitmap.CompressFormat.JPEG, 100, editor.newOutputStream(0));
              editor.commit();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
    
      //当内存Lru缓存中没有所需数据时,调用创造。
      @Override
      protected Bitmap create(String url) {
          //获取key
          String key = hashKeyForDisk(url);
          //从硬盘读取数据
          Bitmap bitmap = null;
          try {
              DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
              if(snapShot!=null){
                  bitmap = BitmapFactory.decodeStream(snapShot.getInputStream(0));
              }
          } catch (IOException e) {
              e.printStackTrace();
          }
          return bitmap;
      }
    

DiskLruCache的法则不再解释了(作者还化解了它存在的壹个BUG,向Log中添加的数码增删记录时,最后一条没有出口,导致最后一条缓存平昔失效。)

  • 硬盘缓存也不曾数量就回来空,然后就向服务器请求数据。

那就是整个流程。
但自己如此的拍卖方案如故有过多受制。

  • 图表未经压缩处理直接存储使用
  • 文本操作在主线程
  • 不曾健全的图片处理API

先前也认为那样已经够用好直到小编遇见上边俩。

然后就坐着大巴哼哧哼哧的通过全部毛里求斯,去往西部度假区。那是天幕下来了淅沥沥的中雨,空气潮湿了些,多个人都放松了下去,准备享受这一次美好的圣诞之行。

HttpClient & HttpURLConnection

HttpClient早被丢掉了,什么人更好那种题材也只有经历落后的面试官才会问。具体原因可以看这里

下边说说HttpU昂科雷LConnection的用法。
最初阶接触的就是那些。

    public class NetUtils {
        public static String post(String url, String content) {
            HttpURLConnection conn = null;
            try {
                // 创建一个URL对象
                URL mURL = new URL(url);
                // 调用URL的openConnection()方法,获取HttpURLConnection对象
                conn = (HttpURLConnection) mURL.openConnection();

                conn.setRequestMethod("POST");// 设置请求方法为post
                conn.setReadTimeout(5000);// 设置读取超时为5秒
                conn.setConnectTimeout(10000);// 设置连接网络超时为10秒
                conn.setDoOutput(true);// 设置此方法,允许向服务器输出内容

                // post请求的参数
                String data = content;
                // 获得一个输出流,向服务器写数据,默认情况下,系统不允许向服务器输出内容
                OutputStream out = conn.getOutputStream();// 获得一个输出流,向服务器写数据
                out.write(data.getBytes());
                out.flush();
                out.close();

                int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方法
                if (responseCode == 200) {

                    InputStream is = conn.getInputStream();
                    String response = getStringFromInputStream(is);
                    return response;
                } else {
                    throw new NetworkErrorException("response status is "+responseCode);
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (conn != null) {
                    conn.disconnect();// 关闭连接
                }
            }

            return null;
        }

        public static String get(String url) {
            HttpURLConnection conn = null;
            try {
                // 利用string url构建URL对象
                URL mURL = new URL(url);
                conn = (HttpURLConnection) mURL.openConnection();

                conn.setRequestMethod("GET");
                conn.setReadTimeout(5000);
                conn.setConnectTimeout(10000);

                int responseCode = conn.getResponseCode();
                if (responseCode == 200) {

                    InputStream is = conn.getInputStream();
                    String response = getStringFromInputStream(is);
                    return response;
                } else {
                    throw new NetworkErrorException("response status is "+responseCode);
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {

                if (conn != null) {
                    conn.disconnect();
                }
            }

            return null;
        }

        private static String getStringFromInputStream(InputStream is)
                throws IOException {
            ByteArrayOutputStream os = new ByteArrayOutputStream();
            // 模板代码 必须熟练
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            is.close();
            String state = os.toString();// 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码)
            os.close();
            return state;
        }
    }

专注互联网权限!被坑了有个别次。

<uses-permission android:name="android.permission.INTERNET"/>

当成有点出师不利的味道,但是这并不能影响我们八个小年青的心情,哪个人让我们年轻吧!

HTTP缓存机制

缓存对于移动端是十三分重大的存在。

  • 减少请求次数,减小服务器压力.
  • 地面数据读取速度更快,让页面不会空白几百飞秒。
  • 在无网络的状态下提供数据。

缓存一般由服务器控制(通过一些形式得以本地控制缓存,比如向过滤器添加缓存控制消息)。通过在伸手头添加上边几个字端:

Request

请求头字段 意义
If-Modified-Since: Sun, 03 Jan 2016 03:47:16 GMT 缓存文件的最后修改时间。
If-None-Match: "3415g77s19tc3:0" 缓存文件的Etag(Hash)值
Cache-Control: no-cache 不使用缓存
Pragma: no-cache 不使用缓存

Response

响应头字段 意义
Cache-Control: public 响应被共有缓存,移动端无用
Cache-Control: private 响应被私有缓存,移动端无用
Cache-Control:no-cache 不缓存
Cache-Control:no-store 不缓存
Cache-Control: max-age=60 60秒之后缓存过期(相对时间)
Date: Sun, 03 Jan 2016 04:07:01 GMT 当前response发送的时间
Expires: Sun, 03 Jan 2016 07:07:01 GMT 缓存过期的时间(绝对时间)
Last-Modified: Sun, 03 Jan 2016 04:07:01 GMT 服务器端文件的最后修改时间
ETag: "3415g77s19tc3:0" 服务器端文件的Etag[Hash]值

规范使用时按必要恐怕只含有其中一些字段。
客户端要依据这一个消息存储这一次请求音讯。
下一场在客户端发起呼吁的时候要检查缓存。遵从下边步骤:

浏览器缓存机制

注意服务器重返304趣味是数量尚未改变滚去读缓存消息。
一度年轻的本身为温馨写的网络请求框架添加完善了缓存机制,还神采飞扬,直到有一天自身见到了上边二个东西。(/TДT)/

飞机于中午六点左右低沉在济州国际机场,走下飞机的时候,心里还想着,终于出了一遍国呢看看海外的月球是否确实不均等吗!不过还没有走出机场,就闹出了一场乌龙事件。不知情是因为紧张仍然机场的标识确实不老聃楚,大家随后一行人一向往外走,不过走出了一道自动门后,突然意识行李没有取!出了那一个门就一定于是海关,是不可以再回到的,那时候大家操着一口中国味的塞尔维亚(Република Србија)语初叶跟门口的员工表明,不过一通解释后,发现人家并不会说斯洛伐克语,当时着实是都快急死了。幸好同行的人里有多少个还没跨出那道门,所以大家就厚着脸皮请人家支持把行李带了出去,之后也是一顿道谢。

互连网图片加载优化

对于图片的传输,似乎上面的登录接口的avatar字段,并不会一向把图纸写在回到内容里,而是给3个图片的地址。须要时再去加载。

比方你一向用HttpU冠道LConnection去取一张图纸,你办获得,然则没优化就只是个BUG不断demo。相对不可能规范使用。
在意网络图片有些特点:

  1. 它永远不会变
    2个链接对应的图纸一般永远不会变,所以当第3遍加载了图片时,就相应给予永久缓存,将来就不再互连网请求。
  • 它很占内存
    一张图片小的几十k多的几M高清无码。尺寸也是64*64到2k图。你无法就那样直接体现到UI,甚至无法直接放进内存。
  • 它要加载很久
    加载一张图纸须求几百ms到几m。那之间的UI占位图效能也是必须考虑的。

说说自身在上头提到的RequestVolley里做的图样请求处理(没错作者做了,这有的的代码可以去github里看源码)。