网络框架-okhttp

okhttp是什么?简而言之就是一款优秀的网络框架。
能实现的功能?Get、Post请求,文件上传和下载等等….

从基本功能的调用,看看一篇关于okhttp的封装:

// http Get操作
OkHttpClient client = new OkHttpClient();

String run(String url) throws IOException {
  Request request = new Request.Builder()
      .url(url)
      .build();

  Response response = client.newCall(request).execute();
  return response.body().string();
}
//http Post提交Json数据
public static final MediaType JSON
    = MediaType.parse("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

String post(String url, String json) throws IOException {
  RequestBody body = RequestBody.create(JSON, json);
  Request request = new Request.Builder()
      .url(url)
      .post(body)
      .build();
  Response response = client.newCall(request).execute();
  return response.body().string();
}

由于okhttp要在子线程中进行操作,所以要对其进行封装,在github搜索okhttp回看到很多封装库,一个事鸿洋的okhttputils,另一个叫okhttp-OkGo,两个star都在3000以上以上了,可见都很好,可以看一下,依赖指定的jar包,或着clone下来,代码里可以看到几种功能的调用。

先看,前两天我看到的一个okhttp的封装工具:

/**
 * Description : OkHttp网络连接封装工具类
 * Author : lauren
 * Email  : lauren.liuling@gmail.com
 * Blog   : http://www.liuling123.com
 * Date   : 15/12/17
 */
public class OkHttpUtils {

    private static final String TAG = "OkHttpUtils";

    private static OkHttpUtils mInstance;
    private OkHttpClient mOkHttpClient;
    private Handler mDelivery;

    private OkHttpUtils() {
        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);
        mOkHttpClient.setWriteTimeout(10, TimeUnit.SECONDS);
        mOkHttpClient.setReadTimeout(30, TimeUnit.SECONDS);
        //cookie enabled
        mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
        mDelivery = new Handler(Looper.getMainLooper());
    }

    private synchronized static OkHttpUtils getmInstance() {
        if (mInstance == null) {
            mInstance = new OkHttpUtils();
        }
        return mInstance;
    }

    private void getRequest(String url, final ResultCallback callback) {
        final Request request = new Request.Builder().url(url).build();
        deliveryResult(callback, request);
    }

    private void postRequest(String url, final ResultCallback callback, List<Param> params) {
        Request request = buildPostRequest(url, params);
        deliveryResult(callback, request);
    }

    private void deliveryResult(final ResultCallback callback, Request request) {

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, final IOException e) {
                sendFailCallback(callback, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                try {
                    String str = response.body().string();
                    if (callback.mType == String.class) {
                        sendSuccessCallBack(callback, str);
                    } else {
                        Object object = JsonUtils.deserialize(str, callback.mType);
                        sendSuccessCallBack(callback, object);
                    }
                } catch (final Exception e) {
                    LogUtils.e(TAG, "convert json failure", e);
                    sendFailCallback(callback, e);
                }

            }
        });
    }

    private void sendFailCallback(final ResultCallback callback, final Exception e) {
        mDelivery.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onFailure(e);
                }
            }
        });
    }

    private void sendSuccessCallBack(final ResultCallback callback, final Object obj) {
        mDelivery.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onSuccess(obj);
                }
            }
        });
    }

    private Request buildPostRequest(String url, List<Param> params) {
        FormEncodingBuilder builder = new FormEncodingBuilder();
        for (Param param : params) {
            builder.add(param.key, param.value);
        }
        RequestBody requestBody = builder.build();
        return new Request.Builder().url(url).post(requestBody).build();
    }


    /**********************对外接口************************/

    /**
     * get请求
     * @param url  请求url
     * @param callback  请求回调
     */
    public static void get(String url, ResultCallback callback) {
        getmInstance().getRequest(url, callback);
    }

    /**
     * post请求
     * @param url       请求url
     * @param callback  请求回调
     * @param params    请求参数
     */
    public static void post(String url, final ResultCallback callback, List<Param> params) {
        getmInstance().postRequest(url, callback, params);
    }

    /**
     * http请求回调类,回调方法在UI线程中执行
     * @param <T>
     */
    public static abstract class ResultCallback<T> {

        Type mType;

        public ResultCallback(){
            mType = getSuperclassTypeParameter(getClass());
        }

        static Type getSuperclassTypeParameter(Class<?> subclass) {
            Type superclass = subclass.getGenericSuperclass();
            if (superclass instanceof Class) {
                throw new RuntimeException("Missing type parameter.");
            }
            ParameterizedType parameterized = (ParameterizedType) superclass;
            return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
        }

        /**
         * 请求成功回调
         * @param response
         */
        public abstract void onSuccess(T response);

        /**
         * 请求失败回调
         * @param e
         */
        public abstract void onFailure(Exception e);
    }

    /**
     * post请求参数类
     */
    public static class Param {

        String key;
        String value;

        public Param() {
        }

        public Param(String key, String value) {
            this.key = key;
            this.value = value;
        }

    }


}

如果不靠工具类,自己去过一遍,因为要在子线程中调用,可能会new一个Thread,启动一个线程,或着来一个asynctask,也方便了更新ui与主线程交互handler了,代码就不写了,刚刚说的很容易实现。由于okhttp提供了一个enqueue这个异步方法,因此省略了,start一个Thread的操作。网络请求成功后,由非主线程–>主线程,需要一个handler.sendMessage()或着handler.post(new Runnable()),runOnUiThread();不在acitivity里或着fragment中,还需要一个接口的监听去返回成功和失败两个方法。成功返回的是结果的值,实体类、String字符串等等。(不看代码我可能还想不出来那么多,哈哈,还是直接分析代码)

 private OkHttpUtils() {
       //okhttp初始化,配置信息,同时创建一个handler对象
        mOkHttpClient = new OkHttpClient();
        mOkHttpClient.setConnectTimeout(10, TimeUnit.SECONDS);
        mOkHttpClient.setWriteTimeout(10, TimeUnit.SECONDS);
        mOkHttpClient.setReadTimeout(30, TimeUnit.SECONDS);
        //cookie enabled
        mOkHttpClient.setCookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
        mDelivery = new Handler(Looper.getMainLooper());
    }
     //单例模式,但这样些不太好。
    private synchronized static OkHttpUtils getmInstance() {
        if (mInstance == null) {
            mInstance = new OkHttpUtils();
        }
        return mInstance;
    }


 //另一种方式的单例模式
 private static OkHttpUtils mInstance=null;
 private static OkHttpUtils getmInstance(){
  if(mInstance==null){
    synchronized(OkHttpUtils.class){
        if(mInstance==null){
          mInstance=new OkHttpUtils();
        }
    }
  }
return mInstance;
}

Get请求:

    /**
     * get请求
     * @param url  请求url
     * @param callback  请求回调
     */
     //get方法的调用,在里面对get操作进一步封装,同时指定一个回调
    public static void get(String url, ResultCallback callback) {
        getmInstance().getRequest(url, callback);
    }
//拼接得到request对象,提交网络操作在方法deliveryResult中进行
    private void getRequest(String url, final ResultCallback callback) {
        final Request request = new Request.Builder().url(url).build();
        deliveryResult(callback, request);
    }
//实现真正的提交网络操作,在异步中进行enqueue(params)
private void deliveryResult(final ResultCallback callback, Request request) {

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, final IOException e) {
               //请求失败,回调自定义接口失败的方法。
                sendFailCallback(callback, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                try {
                    String str = response.body().string();
                    if (callback.mType == String.class) {
                        sendSuccessCallBack(callback, str);
                    } else {
                        Object object = JsonUtils.deserialize(str, callback.mType);
                        //请求成功,回调自定义接口成功的方法。
                        sendSuccessCallBack(callback, object);
                    }
                } catch (final Exception e) {
                    LogUtils.e(TAG, "convert json failure", e);
                    sendFailCallback(callback, e);
                }

            }
        });
    }
//对应上面两个回调方法,用handler.post实现与主线程数据交互
 private void sendFailCallback(final ResultCallback callback, final Exception e) {
        mDelivery.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onFailure(e);
                }
            }
        });
    }

    private void sendSuccessCallBack(final ResultCallback callback, final Object obj) {
        mDelivery.post(new Runnable() {
            @Override
            public void run() {
                if (callback != null) {
                    callback.onSuccess(obj);
                }
            }
        });
    }

看上面代码中的callback这个参数,是在deliveryResult方法中传递的参数,需要我们调用get或者post时,去生成一个callback,看看这个类:

//抽象类,封装了两个方法,当要拿到这个类的引用时,需要复写这两个方法,看下面调用
 /**
     * http请求回调类,回调方法在UI线程中执行
     * @param <T>
     */
    public static abstract class ResultCallback<T> {

        Type mType;

        public ResultCallback(){
            mType = getSuperclassTypeParameter(getClass());
        }

        //通过反射得到想要的返回类型
        static Type getSuperclassTypeParameter(Class<?> subclass) {
            Type superclass = subclass.getGenericSuperclass();
            if (superclass instanceof Class) {
                throw new RuntimeException("Missing type parameter.");
            }
            ParameterizedType parameterized = (ParameterizedType) superclass;
            return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
        }

        /**
         * 请求成功回调
         * @param response
         */
        public abstract void onSuccess(T response);

        /**
         * 请求失败回调
         * @param e
         */
        public abstract void onFailure(Exception e);
    }
//主线程中调用okhttp工具类进行网络请求
//这里就是上面两个方法的回调,使用handler后变为在主线程中
        OkHttpUtils.ResultCallback<String> loadNewsCallback = new OkHttpUtils.ResultCallback<String>() {
            @Override 
            public void onSuccess(String response) {
                List<NewsBean> newsBeanList = NewsJsonUtils.readJsonNewsBeans(response, getID(type));
                listener.onSuccess(newsBeanList);
            }

            @Override
            public void onFailure(Exception e) {
                listener.onFailure("load news list failure.", e);
            }
        };

Post请求:
和Get请求不同的在于requertBody的不同,Post请求要提交一些数据参数之类,另外Post使用起来更安全。

    /**
     * post请求
     * @param url       请求url
     * @param callback  请求回调
     * @param params    请求参数
     */
    //和Get请求一样,去进一步封装
    public static void post(String url, final ResultCallback callback, List<Param> params) {
        getmInstance().postRequest(url, callback, params);
    }
    //与get不同的是,多了一个List的参数,这里是用来传递拼接的字段。
    private void postRequest(String url, final ResultCallback callback, List<Param> params) {
        //调用buildPostRequest方法,去拼接
        Request request = buildPostRequest(url, params);
        //执行网络操作
        deliveryResult(callback, request);
    }
//遍历list集合,使用FormEncodingBuilder去add键值对
private Request buildPostRequest(String url, List<Param> params) {
        FormEncodingBuilder builder = new FormEncodingBuilder();
        for (Param param : params) {
            builder.add(param.key, param.value);
        }
        RequestBody requestBody = builder.build();
        return new Request.Builder().url(url).post(requestBody).build();
    }
    /**
     * post请求参数类
     */
     //List集合中的参数类
    public static class Param {

        String key;
        String value;

        public Param() {
        }

        public Param(String key, String value) {
            this.key = key;
            this.value = value;
        }

    }

下面和Get操作一样了,执行网络操作,区别就在于上面的requestbody的不同。同样的调用方法:deliveryResult()

    private void deliveryResult(final ResultCallback callback, Request request) {

        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, final IOException e) {
                sendFailCallback(callback, e);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                try {
                    String str = response.body().string();
                    if (callback.mType == String.class) {
                        sendSuccessCallBack(callback, str);
                    } else {
                        Object object = JsonUtils.deserialize(str, callback.mType);
                        sendSuccessCallBack(callback, object);
                    }
                } catch (final Exception e) {
                    LogUtils.e(TAG, "convert json failure", e);
                    sendFailCallback(callback, e);
                }

            }
        });
    }

实现文件下载和上传:
依赖一个jar包工具类:详细信息点击看这里:https://github.com/jeasonlzy/okhttp-OkGo

compile 'com.lzy.net:okgo:+'        //版本号使用 + 可以自动引用最新版
compile 'com.lzy.widget:imagepicker:0.3.2'  //这个是一个图片选择器工具类
compile 'com.github.bumptech.glide:glide:3.6.1'

仿微信图片选择imagepicker:https://github.com/jeasonlzy/ImagePicker

文件下载:

/**
     * 文件下载
     *
     * @param view
     */

    public void download(View view) {
        OkGo.get(Urls.URL_DOWNLOAD)
                .tag(this)
                .headers("header1", "headerValue1")
                .params("paramq", "paramValue1")
                .execute(new FileCallback() {

                    @Override
                    public void onBefore(BaseRequest request) {
                        super.onBefore(request);
                        bt_download.setText("下载中....");
                    }

                    @Override
                    public void onSuccess(File file, Call call, Response response) {
                        bt_download.setText("下载成功");
                        tv_download_speed.setText("--");
                    }


                    @Override
                    public void onError(Call call, Response response, Exception e) {
                        super.onError(call, response, e);
                        bt_download.setText("下载失败");
                        tv_download_speed.setText("--");
                    }

                    @Override
                    public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
                        super.downloadProgress(currentSize, totalSize, progress, networkSpeed);
                        String downloadLength = Formatter.formatFileSize(getApplicationContext(), currentSize);
                        String totalLength = Formatter.formatFileSize(getApplicationContext(), totalSize);
                        tv_current_size.setText(downloadLength);
                        tv_total_size.setText(totalLength);
                        tv_current_progress.setText((int) (progress * 100) + "%");
                        String netSpeed = Formatter.formatFileSize(getApplicationContext(), networkSpeed);
                        tv_download_speed.setText(netSpeed + "/S");
                        load_progressbar.setProgress((int) (progress * 100));

                    }


                });

    }

可以设置多个头部header参数和params参数信息,这里是任意设置的,可以自定义callback,实现依赖jar包中的AbsCallback。AbsCallback里有几个回调方法:代码如下:

public abstract class AbsCallback<T> implements Converter<T> {

    /** 请求网络开始前,UI线程 */
    public void onBefore(BaseRequest request) {
    }

    /** 对返回数据进行操作的回调, UI线程 */
    public abstract void onSuccess(T t, Call call, Response response);

    /** 缓存成功的回调,UI线程 */
    public void onCacheSuccess(T t, Call call) {
    }

    /** 请求失败,响应错误,数据解析错误等,都会回调该方法, UI线程 */
    public void onError(Call call, Response response, Exception e) {
    }

    /** 缓存失败的回调,UI线程 */
    public void onCacheError(Call call, Exception e) {
    }

    /** 网络失败结束之前的回调 */
    public void parseError(Call call, Exception e) {
    }

    /** 请求网络结束后,UI线程 */
    public void onAfter(T t, Exception e) {
        if (e != null) e.printStackTrace();
    }

    /**
     * Post执行上传过程中的进度回调,get请求不回调,UI线程
     *
     * @param currentSize  当前上传的字节数
     * @param totalSize    总共需要上传的字节数
     * @param progress     当前上传的进度
     * @param networkSpeed 当前上传的速度 字节/秒
     */
    public void upProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
    }

    /**
     * 执行下载过程中的进度回调,UI线程
     *
     * @param currentSize  当前下载的字节数
     * @param totalSize    总共需要下载的字节数
     * @param progress     当前下载的进度
     * @param networkSpeed 当前下载的速度 字节/秒
     */
    public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
    }
}

这里FileCallback的代码如下:

public abstract class FileCallback extends AbsCallback<File> {

    private FileConvert convert;    //文件转换类

    public FileCallback() {
        this(null);
    }

    public FileCallback(String destFileName) {
        this(null, destFileName);
    }

    public FileCallback(String destFileDir, String destFileName) {
        convert = new FileConvert(destFileDir, destFileName);
        convert.setCallback(this);
    }

    @Override
    public File convertSuccess(Response response) throws Exception {
        File file = convert.convertSuccess(response);
        response.close();
        return file;
    }
}

上传代码如下:

/**
     * 文件上传
     *
     * @param view
     */

    public void upload(View view) {
        List<File> files = new ArrayList<>();
        if (imageItems != null && imageItems.size() > 0) {
            for (ImageItem imageItem : imageItems) {
                files.add(new File(imageItem.path));
            }
        }

        try {
            OkGo.post(Urls.URL_FORM_UPLOAD)
                    .headers("header1", "heardvalue1")
                    .headers("header2", "heardvalue2")
                    .headers("header3", "heardvalue3") //看服务器那边要求去拼接了。
                    .params("params1", "value1")
                    .params("params2", "value2")
                    .params("params3", "value3")
                    .addFileParams("file", files) //传个集合,一个key,对应一个集合文件上传。
                    //                    .params("file1",new File("imagePath1"))
                    //                    .params("file2",new File("imagePath2"))
                    //                    .params("file3",new File("imagePath3"))
                    .execute(new StringCallback() {
                        @Override
                        public void onBefore(BaseRequest request) {
                            super.onBefore(request);
                            bt_upload.setText("正在上传中....");
                        }

                        @Override
                        public void onSuccess(String s, Call call, Response response) {
                            bt_upload.setText("上传成功");
                            upload_speed.setText("--");
                        }


                        @Override
                        public void onError(Call call, Response response, Exception e) {
                            super.onError(call, response, e);
                            bt_upload.setText("上传失败");
                            upload_speed.setText("--");
                        }

                        @Override
                        public void upProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
                            super.upProgress(currentSize, totalSize, progress, networkSpeed);
                            String uploadSize=Formatter.formatFileSize(MainActivity.this,currentSize);
                            String totalSizeNum=Formatter.formatFileSize(MainActivity.this,totalSize);
                            upload_current_progress.setText(uploadSize+"/"+totalSizeNum);
                            upload_percentage.setText((int) (progress * 100) + "%");
                            String netSpeed = Formatter.formatFileSize(getApplicationContext(), networkSpeed);
                            upload_speed.setText(netSpeed);
                            upload_progressbar.setProgress((int) (progress * 100));

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


    }

这块和下载逻辑差不错,headersrparams都可以有多对,在上传时,服务端都会要求拼接许多header和一些params键值对,代码那里有标注,可以直接传一个list集合,里面放图片路径就可以了。图片的选择是跳到相册页,这里就用到了刚刚依赖的imagePicker

跳转到相册代码:

/**
     * 去相册选择图片
     *
     * @param view
     */

    public void choose(View view) {
        Intent intent = new Intent(this, ImageGridActivity.class);
        startActivityForResult(intent, IMAGE_PICKER);
    }

回调代码:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == IMAGE_PICKER && resultCode == ImagePicker.RESULT_CODE_ITEMS) {
            if (data != null) {
                StringBuilder sb = new StringBuilder();
                imageItems = (ArrayList<ImageItem>) data.getSerializableExtra(ImagePicker.EXTRA_RESULT_ITEMS);
                for (int i = 0; i < imageItems.size(); i++) {
                    if (i == imageItems.size() - 1) {
                        sb.append("图片" +(i+1) + ":" + imageItems.get(i).path);
                    } else {
                        sb.append("图片" + (i+1) + ":" + imageItems.get(i).path + ",");
                    }
                }

                tv_image_path.setText(sb.toString());
            }

        }
    }

这里需要注意的是,依赖的imagepicker需要配置初始化,不清楚点击看这里:
还可以按照我这个demo里的配置来,当然我也是按照它这里配置的:

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        ImagePicker imagePicker = ImagePicker.getInstance();
        imagePicker.setImageLoader(new GlideImageLoad());   //设置图片加载器
        imagePicker.setShowCamera(true);  //显示拍照按钮
        imagePicker.setCrop(true);        //允许裁剪(单选才有效)
        imagePicker.setSaveRectangle(true); //是否按矩形区域保存
        imagePicker.setSelectLimit(9);    //选中数量限制
        imagePicker.setStyle(CropImageView.Style.RECTANGLE);  //裁剪框的形状
        imagePicker.setFocusWidth(800);   //裁剪框的宽度。单位像素(圆形自动取宽高最小值)
        imagePicker.setFocusHeight(800);  //裁剪框的高度。单位像素(圆形自动取宽高最小值)
        imagePicker.setOutPutX(1000);//保存文件的宽度。单位像素
        imagePicker.setOutPutY(1000);//保存文件的高度。单位像素
    }
}

GlideImageLoad代码:

 @Override
    public void displayImage(Activity activity, String path, ImageView imageView, int width, int height) {
      //Glide的配置去加载图片
      Glide.with(activity.getApplicationContext()).load(path)
                .placeholder(R.mipmap.default_image)
                .error(R.mipmap.default_image)
                .override(width,height)
                .into(imageView);

    }

    @Override
    public void clearMemoryCache() {
     //这里去清除缓存
    }

这样配置就完成了,最后是xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="2dp"
    tools:context="user.example.com.okgosample.acitivity.MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp"
        >
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_weight="1"

            >
            <TextView
                android:id="@+id/tv_current_size"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="-"

                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="/"
                />

            <TextView
                android:id="@+id/tv_total_size"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="-M"

                />
        </LinearLayout>

        <TextView
            android:id="@+id/tv_current_progress"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="--%"
            android:layout_weight="1"
            />

        <TextView
            android:id="@+id/tv_download_speed"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:text="kb/s"
            android:layout_weight="1"
            />

    </LinearLayout>



    <ProgressBar
        android:id="@+id/load_progressbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:progress="0"
        android:visibility="visible"
        />
    <Button
        android:id="@+id/bt_download"
        android:onClick="download"
        android:text="down_load"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />



    <ProgressBar
        android:id="@+id/upload_progressbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:progress="0"
        android:visibility="visible"
        />

     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         android:padding="10dp">
         <TextView
             android:id="@+id/upload_current_progress"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="-/M"
             />
         <TextView
             android:id="@+id/upload_percentage"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="-%"
             />
         <TextView
             android:id="@+id/upload_speed"
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:text="kb/s"
             />
     </LinearLayout>

    <Button
        android:id="@+id/bt_choose_images"
        android:onClick="choose"
        android:text="选择图片"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/bt_upload"
        android:onClick="upload"
        android:text="up_down"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_image_path"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

最后再来张效果图吧,效果还不错: (这里是实现3张图片上传,和下载一个文件)


这里写图片描述

结论: okGo封装类中提供了许多强大的很实用的功能,具体需要哪块就去实现那块吧,demo就不上传了,可以把okGo源码clone下来,里面还是很详细。有问题就拍砖,多多指教~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值