第二十一章 Android中常见的HTTP请求网络方式

作者: 忆念成风 | 来源:发表于2017-10-11 17:58 被阅读218次

      我们在实际的开发过程中,请求网络数据是必不可少的。市场上所有的APP都不可避免的会用到请求数据。这一章主要是介绍下自用的一些比较常见的请求方式。例如:HttpURLConnection,HttpClient(目前被抛弃了),OKHttp,Volley,xutils请求,还有一些rxandroid不怎么用,这里就不介绍了。

    1. 初步认识HTTP协议

      Http协议内容对于我们来说,写很多本书都可以了,我们这边只是初步认识下,了解一些协议的内容。在Http协议中,主要的就是两个端,客户端(Client)和服务端(Server)。一个完整的http需要需要经历两个过程:客户端发送请求到服务端,服务端响应并返回结果到客户端。如下图所示:

    Http协议工作

    打开浏览器,查看客户端的请求(以百度为例):F12->Network->F5->Name第一个->Headers

    查看客户端发送请求
    • 客户端——>服务端:
      客户端向服务器发送请求主要包含以下信息:请求的Url地址、请求头以及可选的请求体,打开百度首页。
    请求的内容
    • 请求的URl(Request URL)
        上图的中的url地址就是:百度的地址,后面可以没有参数,也可以通过?和&添加键值对,一般情况下,URL的长度不能超过2048个字符,即2KB,超过此限制的话服务器可能就不识别。

    • 请求头(Request Headers)
        请求头其实也是一些键值对,不过这些键值通常都是W3C定义了的一些标准的Http请求头的名称,请求头包含了客户端想告诉服务端的一些元数据信息,注意是元数据,而不是数据,比如请求头User-Agent会告诉服务器这条请求来自于什么浏览器,再比如请求头Accept-Encoding会告诉服务器客户端支持的压缩格式。除了这些标准的请求头,我们还可以添加自定义的请求头。

    • 请求体(Request Body)
        之前我们提到,URL的最大长度就是2048个字符,如果我们发送的数据很大,超过了2KB怎么办?我们可以将很大的数据放到请求体中,GET请求不支持请求体,只有POST请求才能设置请求体。请求体中可以放置任意的字节流,从而可以很方便地发送任意格式的数据,服务端只需要读取该输入流即可。

    • 服务端——>客户端:
      服务器接收数据请求之后会返回相应的处理信息给客户端

    响应数据返回
    • 响应头(Response Headers)

      响应头包含了服务器想要告诉客户端的一些元数据信息,注意不是数据,是元数据,比如通过响应头Content-Encoding告诉客户端服务器所采用的压缩格式,响应头Content-Type告诉客户端响应体是什么格式的数据,再比如服务端可以通过多个Set-Cookie响应头向客户端写入多条Cookie信息,等等。刚刚提到的几个请求头都是W3C规定的标准的请求头名称,我们也可以在服务端向客户端写入自定义的响应头。

    • 响应体 (Response Body)
        响应体是服务端向客户端传输的实际的数据信息,本质就是一堆字节流,可以表示文本,也可以表示图片或者其他格式的信息,如下所示:
      响应体的内容
    • GET vs POST

      Http协议支持的操作有GET、POST、HEAD、PUT、TRACE、OPTIONS、DELETE,其中最最常用的还是GET和POST操作,下面我们看一下GET和POST的区别。

    GET:

    1. GET请求可以被缓存。
    2. 键值对添加在URL上,随着GET请求一起发送,一旦URL被拦截,发送的键值对信息被暴露,安全性低。
    3. 发送到数据长度有限,不能超过2048个字符
    4. 只能获取数据,不能参与其他的操作。

    POST:

    1. POST请求从不会被缓存。
    2. POST请求的URL中追加键值对参数,不过这些键值对参数不是随着URL发送的,而是被放入到请求体中发送的,这样安全性稍微好一些。
    3. 可以用POST请求发送敏感信息
    4. 由于可以在请求体中发送任意的数据,所以理论上POST请求不存在发送数据大小的限制。
    5. 当执行增减、删除、修改等操作时,应该使用POST请求,而不应该使用GET请求。

    到这里就是协议的一些初步认识。

    2. HttpURLConnection

      在过去,Android上发送HTTP主要是HttpURLConnection和HttpClient这两种方式。不过因为HttpClient存在一系列的问题(API数量过多,向下兼容困难等),所以Android团队不建议我们使用这种方式,在Android 6.0系统中被完全移除,所以这个功能就别用了。这里介绍下HttpURLConnection的用法,HttpURLConnection的主要代码就这些:

                       URL url= new URL("https://www.baidu.com");
                       HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                        //HttpURLConnection默认就是用GET发送请求,所以下面的setRequestMethod可以省略
                        connection.setRequestMethod("GET");
                        //HttpURLConnection默认也支持从服务端读取结果流,所以下面的setDoInput也可以省略
                        connection.setDoInput(true);
                        //禁用网络缓存
                        connection.setUseCaches(false);
                        //在对各种参数配置完成后,通过调用connect方法建立TCP连接,但是并未真正获取数据
                        //conn.connect()方法不必显式调用,当调用conn.getInputStream()方法时内部也会自动调用connect方法
                        connection.connect();
                        connection.setConnectTimeout(3000);
                        connection.setReadTimeout(3000);
                        //调用getInputStream方法后,服务端才会收到请求,并阻塞式地接收服务端返回的数据
                        InputStream inputStream = connection.getInputStream();
                        //对获取的输入流进行读取
                        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                        StringBuilder builder=new StringBuilder();
                        String line;
                        while((line= reader.readLine())!=null){
                            builder.append(line);
                        }
    

      首先得到一个URL对象,然后传入需要传入的地址,再调用openConnection()的方法,打开连接。接下来就是自己的一些要求定制,设置POST或者GET的方式发送请求,设置连接超时时间,设置读取超时时间。得到一个读取builder.toString()的字符串。然后对这个字符串进行处理操作,别忘了,在对异常处理完成之后记得添加关闭操作。下面是贴出完整的代码:
    1.activity_de_fault_http_urlconnection.xml

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.demo.myhttpurlconnection.defaulthtu.DeFaultHttpURLConnectionActivity">
    
    
        <TextView
            android:id="@+id/tv_post_show"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:textSize="16sp"
    
            />
    
    </android.support.constraint.ConstraintLayout>
    
    
    1. DeFaultHttpURLConnectionActivity ,请求网络是耗时操作,所以要另开线程,然后再在主线程中显示。
    
    public class DeFaultHttpURLConnectionActivity extends AppCompatActivity {
    
        private TextView tv_post_show;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_de_fault_http_urlconnection);
            tv_post_show = (TextView) findViewById(R.id.tv_post_show);
             sendRequestWithHttpURLConnection();
        }
    
        private void sendRequestWithHttpURLConnection() {
            //开启线程来请求网络
            new Thread(new Runnable() {
                private BufferedReader reader;
                private HttpURLConnection connection;
    
                @Override
                public void run() {
                    try {
                        URL url= new URL("https://www.baidu.com");
                        connection = (HttpURLConnection) url.openConnection();
                        //HttpURLConnection默认就是用GET发送请求,所以下面的setRequestMethod可以省略
                        connection.setRequestMethod("GET");
                        //HttpURLConnection默认也支持从服务端读取结果流,所以下面的setDoInput也可以省略
                        connection.setDoInput(true);
                        //禁用网络缓存
                        connection.setUseCaches(false);
    
                        //在对各种参数配置完成后,通过调用connect方法建立TCP连接,但是并未真正获取数据
                        //conn.connect()方法不必显式调用,当调用conn.getInputStream()方法时内部也会自动调用connect方法
                        connection.connect();
    
                        connection.setConnectTimeout(3000);
                        connection.setReadTimeout(3000);
                        //调用getInputStream方法后,服务端才会收到请求,并阻塞式地接收服务端返回的数据
                        InputStream inputStream = connection.getInputStream();
                        //对获取的输入流进行读取
                        reader = new BufferedReader(new InputStreamReader(inputStream));
                        StringBuilder builder=new StringBuilder();
                        String line;
                        while((line= reader.readLine())!=null){
                            builder.append(line);
                        }
                       showRespone(builder.toString());
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally {
                        if(reader!=null){
                            try {
                                reader.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                        if (connection!=null){
                            connection.disconnect();
                        }
                    }
                }
            }).start();
    
        }
    
        private void showRespone(final String s) {
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
                     tv_post_show.setText(s);
                 }
             });
        }
    }
    
    
    效果如图所示

    2. OkHttp

      OkHttp是目前市场上使用的比较多的一个网络请求方式了。它不仅在接口封装上做的简单易用,底层的实现也很特别。比起HttpURLConnection有过之而无不及。

    2.1 使用OkHttp

    在使用OkHttp 之前,我们需要添加依赖包:

       compile 'com.squareup.okhttp3:okhttp:3.9.0'
    

      添加完依赖之后会自动下载两个库,一个是OkHttp库,另一个是Okio库,后者是hi前者的通信基础。代码很简单首先是创建一个OkHttpClient实例,然后再创建一个Request对象,自己添加想要的方法来丰富这个内容。之后调用OkHttpClient的newCall(request)方法来获得返回的对象。

    
    public class OkHttpActivity extends AppCompatActivity {
    
        private TextView tv_ok_show;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_ok_http);
            tv_ok_show = (TextView) findViewById(R.id.tv_ok_show);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //创建一个OkHttpClient实例
                    OkHttpClient client = new OkHttpClient();
                    //需要发送请求就需要创建空的Request对象,在这里面添加方法来丰富这个内容
                    Request  request =  new Request.Builder()
                            .url("http://www.baidu.com")
                            .build();
                    //发出请求之后还需要获取返回的数据
                    try {
                        Response  response = client.newCall(request).execute();
                        //这个就是得到的返回数据
                        String responseData = response.body().string();
                        tv_ok_show.setText(responseData);
    
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
    
    

    这里只是列出了一些最基本的用法,还有很多需要自己去探究。
    布局文件很简单就一个TextView,这里就不贴出来了,效果如上图所示。

    3. Volley

    3.1 Volley的基本用法

      Volley这种网络请求方式也是很普遍的。很多程序员喜欢用这种数据通讯库,这里简单介绍下:
    实现一个Volley的请求操作只需要三步:

    1. 创建一个RequestQueue对象
    2. 创建一个StringRequest对象
    3. 将StringRequest对象添加到RequestQueue对象中

    下面是对这三步的分析:
    1). 创建一个RequestQueue对象,RequestQueue是一个缓存队列,可以缓存所有的HTTP请求,然后按照一定的算法并发地发出这些请求。RequestQueue内部的设计就是非常合适高并发的,因此我们不必为每一次HTTP请求都创建一个RequestQueue对象,这是非常浪费资源的,基本上在每一个需要和网络交互的Activity中创建一个RequestQueue对象就足够了。

    //创建一个RequestQueue的对象
     RequestQueue  requestQueue  = Volley.ewRequestQueue(getApplicationContext()); 
    

    2). 创建一个StringRequest对象,这里new出一个StringRequest对象,构造函数有四个,第一个是设置GET或者POST的请求方式,第二个是URL地址,第三个是返回数据成功的响应,第四个是返回数据失败的响应。

      StringRequest  stringRequest  = new StringRequest(Request.Method.GET,"http://www.weather.com.cn/data/cityinfo/101010100.html",
                    new Response.Listener<String>() {
                        @Override
                        public void onResponse(String response) {
                            Log.e("tag","responese="+response);
                            tv_volley_show.setText(response);
                        }
                    },
                    new Response.ErrorListener() {
                        @Override
                       public void onErrorResponse(VolleyError error) {
                             Log.e("tag","error="+error);
                       }
            });
    

    注意:以下是Volley的异常列表:

    1. AuthFailureError:如果在做一个HTTP的身份验证,可能会发生这个错误。
    2. NetworkError:Socket关闭,服务器宕机,DNS错误都会产生这个错误。
    3. NoConnectionError:和NetworkError类似,这个是客户端没有网络连接。
    4. ParseError:在使用JsonObjectRequest或JsonArrayRequest时,如果接收到的JSON是畸形,会产生异常。
    5. SERVERERROR:服务器的响应的一个错误,最有可能的302或者4xx或5xx HTTP状态代码。
    6. TimeoutError:Socket超时,服务器太忙或网络延迟会产生这个异常。默认情况下,Volley的超时时间为2.5秒。如果得到这个错误可以使用RetryPolicy。

    3). 将StringRequest对象添加大RequestQueue对象中

     //将StringRequest对象添加大RequestQueue对象中
      requestQueue.add(stringRequest);
    

    当然在使用GET请求的时候,我们会直接拼接到URL上,如果是post请求的话,需要添加参数的时候怎么办?这时候就需要重写Volley的getParams()的方法。

      //创建一个StringRequest对象
      StringRequest  stringRequest  = new StringRequest(Request.Method.GET,"http://www.baidu.com",
                    new Response.Listener<String>() {
                        @Override
                        public void onResponse(String response) {
                            Log.e("tag","responese="+response);
                            tv_volley_show.setText(response);
                        }
                    },
                    new Response.ErrorListener() {
                        @Override
                       public void onErrorResponse(VolleyError error) {
                            Log.e("tag","error="+error);
    
                       }
            }){
                @Override
                protected Map<String, String> getParams() throws AuthFailureError {
                    Map<String, String> map = new HashMap<String, String>();
                    map.put("params1", "value1");
                    map.put("params2", "value2");
                    return map;
                }
            };
    
    3.2 Volley 中的 JsonRequest的用法

      客户端以普通的post方式进行提交,服务端返回json串。类似于StringRequest,JsonRequest也是继承自Request类的,不过由于JsonRequest是一个抽象类,因此我们无法直接创建它的实例,那么只能从它的子类入手了。JsonRequest有两个直接的子类,JsonObjectRequest和JsonArrayRequest,从名字上你应该能就看出它们的区别了吧?一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。

    至于它们的用法也基本上没有什么特殊之处,先new出一个JsonObjectRequest对象,如下所示:

     JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://www.weather.com.cn/data/cityinfo/101010100.html", null,
                    new Response.Listener<JSONObject>() {
                        @Override
                        public void onResponse(JSONObject response) {
                            Log.d("TAG", response.toString());
                            tv_volley_show.setText(response.toString());
                        }
                    }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.e("TAG", error.getMessage(), error);
                }
            });
    
            requestQueue.add(jsonObjectRequest);
    

    返回的结果:

    {"weatherinfo":{"city":"北京","cityid":"101010100","temp1":"-2℃","temp2":"16℃","weather":"晴","img1":"n0.gif","img2":"d0.gif","ptime":"18:00"}}
    

      服务器返回给我们的数据确实是JSON格式的,并且onResponse()方法中携带的参数也正是一个JSONObject对象,之后只需要从JSONObject对象取出我们想要得到的那部分数据就可以了。

    3.3 注意:

      注意,当设置提交方式为POST的时候,我们必须会添加一些参数进来拼接,但是会出一个小问题,就是调用getParams()添加参数没有作用。这时候我们必须得构成JSONObject当做实参传入JsonObjectRequest对象里 ,所以getParams()这个方法是无效的。这时候需要这样写:

    RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());  
     
    Map<String, String> map = new HashMap<String, String>();    
    map.put("name1", "value1");    
    map.put("name2", "value2");  
    JSONObject jsonObject = new JSONObject(params);  
    JsonRequest<JSONObject> jsonRequest = new JsonObjectRequest(Method.POST,httpurl, jsonObject,  
        new Response.Listener<JSONObject>() {  
            @Override  
            public void onResponse(JSONObject response) {  
                Log.d(TAG, "response -> " + response.toString());  
      
            }  
        }, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                Log.e(TAG, error.getMessage(), error);  
        }  
        }) 
            
    };  
    requestQueue.add(jsonRequest);  
    
    3.4 实际项目中这样操作

    1.首先定义一个NormalRequest类

    public class NormalRequest  extends Request<JSONObject> {
    
        private Map<String,String> mMap;
        private Response.Listener<JSONObject> mListener;
    
    
        public NormalRequest(String url, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener,Map<String, String> map) {
             super(Method.POST,url, errorListener);
             this.mListener= listener;
             this.mMap= map;
        }
        //mMap是已经按照前面的方式,设置了参数的实例
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            return mMap;
        }
    
        //此处为response返回值需要json数据,和JsonObjectRequest类一样即可
        @Override
        protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {
            try {
                String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
    
                return Response.success(new JSONObject(jsonString),HttpHeaderParser.parseCacheHeaders(response));
            } catch (UnsupportedEncodingException e) {
                return Response.error(new ParseError(e));
            } catch (JSONException je) {
                return Response.error(new ParseError(je));
            }
        }
    
        @Override
        protected void deliverResponse(JSONObject response) {
            mListener.onResponse(response);
        }
    }
    
    

    2.然后调用的时候

      RequestQueue  requestQueue  = Volley.newRequestQueue(getApplicationContext);
      //因为没找到有用的接口,所以拼接的时候参数要自己添加
            //实际调用
            Map<String, String> params = new HashMap<String, String>();
            params.put("name1", "value1");
            params.put("name2", "value2");
            Request<JSONObject> request = new NormalRequest(url,
                    new Response.Listener<JSONObject>() {
                        @Override
                        public void onResponse(JSONObject response) {
                            Log.d("TAG", response.toString());
                            tv_volley_show.setText(response.toString());
                        }
                    }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Log.d("TAG", error.toString());
                }
            }, params);
    
            requestQueue.add(request);
    
    

    4. xutils

      xUtils 是一个工具类的集合体,其中包含许多实用的Android工具,主要包含四大模块:
    1、ViewUtils的模块
    2、HttpUtils的模块
    3、BitmapUtils的模块
    4、DBUtils的模块

    4.1 ViewUtils 模块

    Android中的ioc框架,完全注解方式就可以进行UI、资源和事件绑定;新的事件绑定方式,使用混淆工具混淆后仍可正常工作;目前支持常用的20种事件绑定。
    【备注:】
      控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则,用来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。
      控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。
       IoC中最基本的Java技术就是“反射”编程。通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。反射的应用是很广泛的,象Hibernate、Spring中都是用“反射”做为最基本的技术手段。
      在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普遍应用开来的原因。但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。

    4.1.1 Viewutils模块的使用

    完全注解方式就可以进行UI绑定和事件绑定。无需findViewById和setClickListener等。

    1. View控件注解:
    // xUtils的view注解要求必须提供id,以使代码混淆不受影响。
    @ViewInject(R.id.textView)
    TextView textView;
    
    //@ViewInject(vale=R.id.textView, parentId=R.id.parentView)
    //TextView textView;
    
    1. 字符串资源的注解
    @ResInject(id = R.string.label, type = ResType.String)
    private String label;
    
    1. View控件的OnClick回调方法的注解:
    // 取消了之前使用方法名绑定事件的方式,使用id绑定不受混淆影响
    // 支持绑定多个id @OnClick({R.id.id1, R.id.id2, R.id.id3})
    // or @OnClick(value={R.id.id1, R.id.id2, R.id.id3}, parentId={R.id.pid1, R.id.pid2, R.id.pid3})
    // 更多事件支持参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。
    @OnClick(R.id.test_button)
    public void testButtonClick(View v) { // 方法签名必须和接口中的要求一致
        ...
    }
    
    

    4、在Activity中注入:

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ViewUtils.inject(this); //注入view和事件
    }
    

    5、在Fragment中注入:

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.bitmap_fragment, container, false); // 加载fragment布局
        ViewUtils.inject(this, view); //注入view和事件
    }
    

    6、在PreferenceFragment中注入:

    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ViewUtils.inject(this, getPreferenceScreen()); //注入view和事件
    }
    

    7、其他重载
    // inject(View view);
    // inject(Activity activity)
    // inject(PreferenceActivity preferenceActivity)
    // inject(Object handler, View view)
    // inject(Object handler, Activity activity)
    // inject(Object handler, PreferenceGroup preferenceGroup)
    // inject(Object handler, PreferenceActivity preferenceActivity)

    4.2 HttpUtils模块
    4.2.1 HttpUtils的功能
    1. 支持同步,异步方式的请求;
    2. 支持大文件上传,上传大文件不会OOM;
    3. 支持GET,POST,PUT,MOVE,COPY,DELETE,HEAD,OPTIONS,TRACE,CONNECT请求;
    4. 下载支持301/302重定向,支持设置是否根据Content-Disposition重命名下载的文件;
    5. 返回文本内容的请求(默认只启用了GET请求)支持缓存,可设置默认过期时间和针对当前请求的过期时间。
    4.2.2 HttpUtils使用:
    1. 普通GET方法:
    HttpUtils http = new HttpUtils();
    http.send(HttpRequest.HttpMethod.GET,
        "http://www.lidroid.com",
        new RequestCallBack<String>(){
            @Override
            //第一个参数:下载或上传文件的总长度,第二个参数:下载或上传文件的当前进度,第三个参数:是否为上传
            public void onLoading(long total, long current, boolean isUploading) {
                testTextView.setText(current + "/" + total);
            }
    
            @Override
            public void onSuccess(ResponseInfo<String> responseInfo) {
                textView.setText(responseInfo.result);
            }
    
            @Override
            public void onStart() {
            }
    
            @Override
            public void onFailure(HttpException error, String msg) {
            }
    });
    

    【回调方法说明:】
    1). onStart() 开始执行之前的回调方法
    2). onSuccess(ResponseInfo<T> responseInfo) 请求成功的回调方法
    3). onFailure(HttpException error, String msg) 请求失败的回调方法
    4). onLoading(long total, long current, boolean isUploading) 正在执行的回调方法
    5). onCancelled() 取消请求的回调方法

    1. 使用HttpUtils上传文件 或者 提交数据 到服务器(post方法)
    RequestParams params = new RequestParams();
    params.addHeader("name", "value");
    params.addQueryStringParameter("name", "value");
    
    // 只包含字符串参数时默认使用BodyParamsEntity,
    // 类似于UrlEncodedFormEntity("application/x-www-form-urlencoded")。
    params.addBodyParameter("name", "value");
    
    // 加入文件参数后默认使用MultipartEntity("multipart/form-data"),
    // 如需"multipart/related",xUtils中提供的MultipartEntity支持设置subType为"related"。
    // 使用params.setBodyEntity(httpEntity)可设置更多类型的HttpEntity(如:
    // MultipartEntity,BodyParamsEntity,FileUploadEntity,InputStreamUploadEntity,StringEntity)。
    // 例如发送json参数:params.setBodyEntity(new StringEntity(jsonStr,charset));
    params.addBodyParameter("file", new File("path"));
    ...
    
    HttpUtils http = new HttpUtils();
    http.send(HttpRequest.HttpMethod.POST,
        "uploadUrl....",
        params,
        new RequestCallBack<String>() {
    
            @Override
            public void onStart() {
                testTextView.setText("conn...");
            }
    
            @Override
            public void onLoading(long total, long current, boolean isUploading) {
                if (isUploading) {
                    testTextView.setText("upload: " + current + "/" + total);
                } else {
                    testTextView.setText("reply: " + current + "/" + total);
                }
            }
    
            @Override
            public void onSuccess(ResponseInfo<String> responseInfo) {
                testTextView.setText("reply: " + responseInfo.result);
            }
    
            @Override
            public void onFailure(HttpException error, String msg) {
                testTextView.setText(error.getExceptionCode() + ":" + msg);
            }
    });
    

    【文件上传的send()方法参数说明:】
    第一个参数:请求方法,在此使用HttpMethod.POST
    第二个参数:上传请求的网络路径
    第三个参数:上传的数据,包含基本信息和上传文件信息
    第四个参数:请求的回调接口

    1. 使用HttpUtils下载文件:【支持断点续传,随时停止下载任务,开始任务】
    HttpUtils http = new HttpUtils();
    HttpHandler handler = http.download("http://apache.dataguru.cn/httpcomponents/httpclient/source/httpcomponents-client-4.2.5-src.zip",
        "/sdcard/httpcomponents-client-4.2.5-src.zip",
        true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
        true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
        new RequestCallBack<File>() {
    
            @Override
            public void onStart() {
                testTextView.setText("conn...");
            }
    
            @Override
            public void onLoading(long total, long current, boolean isUploading) {
                testTextView.setText(current + "/" + total);
            }
    
            @Override
            public void onSuccess(ResponseInfo<File> responseInfo) {
                testTextView.setText("downloaded:" + responseInfo.result.getPath());
            }
    
            @Override
            public void onFailure(HttpException error, String msg) {
                testTextView.setText(msg);
            }
    });
    ...
    //调用cancel()方法停止下载
    handler.cancel();
    

    【文件下载download()方法参数说明:】
    第一个参数url:网络资源地址
    第二个参数target: 本地存储位置,如/mnt/sdcard/aa.mp3
    第三个参数autoResume:是否继续下载(断点续传)
    第四个参数autoRename:从请求返回信息中获取文件名,下载完成后是否重命名
    第五个参数:异步请求的回调接口

    四、BitmapUtils模块:
    (一)、简介:
    1、加载Bitmap的时候无需考虑Bitmap加载过程中出现的OOM和Android容器快速滑动时候出现的图片错位等现象;
    2、支持加载网络图片和本地图片;
    3、内存管理使用LRU算法,更好的管理bitmap内存;
    4、可配置线程加载线程数量,缓存大小,缓存路径,加载显示动画等。

    4.3 BitmapUtils 使用:

    1、加载网络图片

    BitmapUtils bitmapUtils = new BitmapUtils(this);
    bitmapUtils.display(testImageView, "http://bbs.lidroid.com/static/image/common/logo.png");
    

    2、加载本地图片(路径以/开头, 绝对路径)

    BitmapUtils bitmapUtils = new BitmapUtils(this);
    bitmapUtils.display(testImageView, "/sdcard/test.jpg");
    

    3、加载assets中的图片(路径以assets开头)

    BitmapUtils bitmapUtils = new BitmapUtils(this);
    bitmapUtils.display(testImageView, "assets/img/wallpaper.jpg");
    

    4、使用ListView等容器展示图片时可通过PauseOnScrollListener控制滑动和快速滑动过程中时候暂停加载图片

    BitmapUtils bitmapUtils = new BitmapUtils(this);
    listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true));
    listView.setOnScrollListener(new PauseOnScrollListener(bitmapUtils, false, true, customListener));
    
    1. BitmapUtils中的config系列方法:
      1). BitmapGlobalConfig配置
      线程加载线程数量 bUtils.configThreadPoolSize(5); //配置线程池大小
      配置缓存

    路径:/data/data/{package}/cache/xx
    bUtils.configMemoryCacheEnabled(true)
    bUtils.configDefaultCacheExpiry(100*1024); //100k

    加载显示动画 bUtils.configDefaultImageLoadAnimation(Animation)

    2). BitmapDisplayConfig配置
    图片宽高
    bUtils.configDefaultBitmapMaxSize(int w,int h)
    bUtils.configDefaultBitmapMaxSize(BitmapSize bs)
    new BitmapSize(int w,int h) 指定宽和高
    BitmapCommonUtils.getScreenSize(context) 依据屏幕尺寸

    默认显示图片

    bUtils.configDefaultLoadingImage(int resId)
    bUtils.configDefaultLoadingImage(Bitmap b)
    bUtils.configDefaultLoadingImage(Drawable d)

    下载失败图片

    bUtils.configDefaultLoadFailedImage(int resId)
    bUtils.configDefaultLoadFailedImage(Bitmap b)
    bUtils.configDefaultLoadFailedImage(Drawable d)

    图片保存质量

    bUtils.configDefaultBitmapConfig(Bitmap.Config.RGB_565);

    4.4 DbUtils模块:
    4.4.1 DbUtils的功能
    1. Android中的ORM框架,一行代码就可以进行增删改查;
    2. 支持事务,默认关闭;
    3. 可通过注解自定义表名,列名,外键,唯一性约束,NOT NULL约束,CHECK约束等(需要混淆的时候请注解表名和列名);
    4. 支持绑定外键,保存实体时外键关联实体自动保存或更新;
    5. 自动加载外键关联实体,支持延时加载;
    6. 支持链式表达查询,更直观的查询语义,参考下面的介绍或sample中的例子。
    4.4.2 DbUtils 使用
    DbUtils db = DbUtils.create(this);
    User user = new User(); //这里需要注意的是User对象必须有id属性,或者有通过@ID注解的属性
    user.setEmail("wyouflf@qq.com");
    user.setName("wyouflf");
    db.save(user); // 使用saveBindingId保存实体时会为实体的id赋值
    
    // 查找
    Parent entity = db.findById(Parent.class, parent.getId());
    List<Parent> list = db.findAll(Parent.class);//通过类型查找
    
    Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=","test"));
    
    // IS NULL
    Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","=", null));
    // IS NOT NULL
    Parent Parent = db.findFirst(Selector.from(Parent.class).where("name","!=", null));
    
    // WHERE id<54 AND (age>20 OR age<30) ORDER BY id LIMIT pageSize OFFSET pageOffset
    List<Parent> list = db.findAll(Selector.from(Parent.class)
                                       .where("id" ,"<", 54)
                                       .and(WhereBuilder.b("age", ">", 20).or("age", " < ", 30))
                                       .orderBy("id")
                                       .limit(pageSize)
                                       .offset(pageSize * pageIndex));
    
    // op为"in"时,最后一个参数必须是数组或Iterable的实现类(例如List等)
    Parent test = db.findFirst(Selector.from(Parent.class).where("id", "in", new int[]{1, 2, 3}));
    // op为"between"时,最后一个参数必须是数组或Iterable的实现类(例如List等)
    Parent test = db.findFirst(Selector.from(Parent.class).where("id", "between", new String[]{"1", "5"}));
    
    DbModel dbModel = db.findDbModelAll(Selector.from(Parent.class).select("name"));//select("name")只取出name列
    List<DbModel> dbModels = db.findDbModelAll(Selector.from(Parent.class).groupBy("name").select("name", "count(name)"));
    ...
    
    List<DbModel> dbModels = db.findDbModelAll(sql); // 自定义sql查询
    db.execNonQuery(sql) // 执行自定义sql
    

    核心代码:

    private void initDbUtils() {
      DaoConfig config = new DaoConfig(getApplicationContext());
      config.setDbName("db_student.db");
      config.setDbVersion(3);
      config.setDbUpgradeListener(new DbUpgradeListener() {
       @Override
       public void onUpgrade(DbUtils db, int oldVersion, int newVersion) {
        if (newVersion > oldVersion) {
         try {
          db.dropDb();
         } catch (DbException e) {
          e.printStackTrace();
         }
        }
       }
      });
    
      dbUtils = DbUtils.create(config);
      dbUtils.configAllowTransaction(true);
      dbUtils.configDebug(true);
      try {
       dbUtils.createTableIfNotExist(Student.class);
      } catch (DbException e) {
       e.printStackTrace();
      }
     }
    

    DbUtils中常用的CRUD操作方法:

    1、insert:
    dbUtils.save(student);
    
    2、delete:
    方法1: dbUtils.delete(Student.class, WhereBuilder.b("_id", "=", id)); 
    方法2: dbUtils.deleteById(Student.class, id); 
    方法3: dbUtils.execNonQuery("delete from tb_student where _id=" +  id); 
    方法4: dbUtils.delete(student);
    
    3、select:
    dbUtils.findAll(Student.class);
    dbUtils.findAll(Selector.from( Student.class).where("_id", "=", id));
    dbUtils.findById();
    dbUtils.execQuery();
    
    4、update:
    dbUtils.update(student);
    dbUtils.update(student, WhereBuilder.b("_id", "=", id),  "username", "password", "age");
    
    4.5 其他功能:
    4.5.1 输出日志LogUtils:

    1、自动添加TAG,格式: className.methodName(L:lineNumber)
    2、可设置全局的LogUtils.allowD = false,LogUtils.allowI = false...,控制是否输出log。
    3、自定义log输出LogUtils.customLogger = new xxxLogger();
    LogUtils.d("wyouflf");

    4.5.2 权限:

    1、使用xUtils快速开发框架需要有以下权限:
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    4.5.3 混淆时注意事项:

    1、添加Android默认混淆配置${sdk.dir}/tools/proguard/proguard-android.txt

    2、不要混淆xUtils中的注解类型,添加混淆配置:-keep class * extends java.lang.annotation.Annotation { *; }
    对使用DbUtils模块持久化的实体类不要混淆,或者注解所有表和列名称@Table(name="xxx"),@Id(column="xxx"),@Column(column="xxx"),@Foreign(column="xxx",foreign="xxx");

    参考文章:http://blog.csdn.net/iispring/article/details/51474529
    参考文章:http://blog.csdn.net/qq_35550937/article/details/52069302

    相关文章

      网友评论

        本文标题:第二十一章 Android中常见的HTTP请求网络方式

        本文链接:https://www.haomeiwen.com/subject/osuuyxtx.html