美文网首页安卓网络
iOS转android之网络封装

iOS转android之网络封装

作者: 移动端_小刚哥 | 来源:发表于2019-01-07 16:39 被阅读6次

在移动端开发中和基础架构同样重要的是网络封装,包括对JSON数据的处理。Android中有很多好用的网络请求工具,这里我选择的是OkHttp

一、网络配置文件

新建单例类NetWorkManager,这个类的功能是创建一个全局唯一的OkHttpClient,每次网络请求都使用这个OkHttpClient类来创建一个Call类,每个Call对象只能进行一次网络请求。如果每次网络请求都创建OkHttpClient会造成资源的极大浪费。在这个单例类中可以对OkHttpClient单例对象配置超时时间、请求头、编码格式等基础配置,还可以实现动态修改超时时间,取消某个网络请求等功能。

/**
 * 一、网络请求相关的配置,包括超时时间、请求头等
 * 二、生成OkHttpClient单例对象,并且以次单例对象生成Call对象进行网络请求
 */
public class NetWorkManager {

    private static int mConnectTimeout = 10000; //默认连接超时时间10秒=10000毫秒
    private static int mReadTimeout = 10000; //默认读取超时时间10秒=10000毫秒
    private static int mWriteTimeout = 10000; //默认写入超时时间10秒=10000毫秒

    /**
     * 存放当前正在网络请求的call们,以备后续进行取消等操作
     */
    private static ArrayList<Call> callList = new ArrayList<Call>();


    /**
     * 声明OkHttp的客户端类
     */
    private static volatile OkHttpClient mOkHttpClient = null;


    /**
     * 私有化构造方法防止外部创建
     */
    private NetWorkManager(){}


    /**
     * 定义类型变量(不初始化,不使用final关键字,使用volatile保证了多线程访问时mNetWorkManager变量的可见性,
     * 避免了mNetWorkManager初始化时其他变量属性还没有赋值完时被另外线程调用)
     */
    private static volatile NetWorkManager mNetWorkManager = null;




    /**
     * 通过OkHttpClient的单例对象创造一个call对象进行本次网络请求
     * @param url 地址
     * @param map 参数map
     * @param method GET/POST
     * @return
     */
    public static Call getInstance( String url, HashMap<String,Object> map, String method){

        /**
         * 对象实例化时调用,不使用同步代码块,mNetWorkManager!=null时直接返回对象,提高运行效率
         */
        if (mNetWorkManager == null){
            synchronized (NetWorkManager.class){
                //未初始化时,初始化mNetWorkManager对象
                if (mNetWorkManager == null){
                    mNetWorkManager = new NetWorkManager();
                    mOkHttpClient = new OkHttpClient
                            .Builder()
                            .connectTimeout(mConnectTimeout,TimeUnit.MILLISECONDS)
                            .readTimeout(mReadTimeout,TimeUnit.MILLISECONDS)
                            .writeTimeout(mWriteTimeout,TimeUnit.MILLISECONDS)
                            .build();
                }
            }
        }


        /**
         * 如果通过setConnectTimeout方法修改了连接超时时间,
         * 那么再次进行网络请求时要修改为默认的连接超时时间
         */
        if (mOkHttpClient.connectTimeoutMillis() != mConnectTimeout){
            try {
                Field connectTimeoutField = mOkHttpClient.getClass().getDeclaredField("connectTimeout");
                connectTimeoutField.setAccessible(true);
                connectTimeoutField.setInt(mOkHttpClient,mConnectTimeout);
            }catch (NoSuchFieldException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }


        /**
         * 如果通过setReadTimeout方法修改了连接超时时间,
         * 那么再次进行网络请求时要修改为默认的读取超时时间
         */
        if (mOkHttpClient.readTimeoutMillis() != mReadTimeout){
            try {
                Field readTimeoutField = mOkHttpClient.getClass().getDeclaredField("readTimeout");
                readTimeoutField.setAccessible(true);
                readTimeoutField.setInt(mOkHttpClient,mReadTimeout);
            }catch (NoSuchFieldException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }


        /**
         * 如果通过setWriteTimeout方法修改了连接超时时间,
         * 那么再次进行网络请求时要修改为默认的写入超时时间
         */
        if (mOkHttpClient.writeTimeoutMillis() != mWriteTimeout){ //通过其他方法修改过写入超时时间
            try {
                Field writeTimeoutField = mOkHttpClient.getClass().getDeclaredField("writeTimeout");
                writeTimeoutField.setAccessible(true);
                writeTimeoutField.setInt(mOkHttpClient,mWriteTimeout);
            }catch (NoSuchFieldException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }


        //将参数字段转化为json字符串
        Gson gson = new Gson();
        String json = gson.toJson(map);

        //设置请求参数的格式为utf-8
        MediaType JSON = MediaType.parse("application/json;charset=UTF-8");
        RequestBody body = RequestBody.create(JSON,json);
        Request request;
        //请求头
        Headers headers = new Headers.Builder()
                    .add("os","ios")
                    .add("appname","crm")
                    .add("version","2.9.1")
                    .add("Content-Type","application/json;charset=UTF-8")
                    .build();


        if (method.equals(PCH.mHttpRequestPost)){ //post请求
            request = new Request.Builder()
                    .url(url)
                    .post(body)
                    .headers(headers)
                    .build();
        }else {//get请求
            request = new Request.Builder()
                    .url(url)
                    .get()
                    .headers(headers)
                    .build();
        }


        Call call = mOkHttpClient.newCall(request);
        callList.add(call);
        return call;
    }


    /**
     * 如果当前网络请求已经成功或者失败,那么要将当前call从callList中删除
     * @param call
     */
    public static void removeCall(Call call){
        callList.remove(call);
    }



    /**
     * 单独修改本次网络请求的连接超时时间
     * @param connectTimeout
     */
    public static void setConnectTimeout(int connectTimeout){
        if (mOkHttpClient != null && mOkHttpClient.connectTimeoutMillis() != connectTimeout){
            try {
                Field connectTimeoutField = mOkHttpClient.getClass().getDeclaredField("connectTimeout");
                connectTimeoutField.setAccessible(true);
                connectTimeoutField.setInt(mOkHttpClient,connectTimeout);
            }catch (NoSuchFieldException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 单独修改本次网络请求的读取超时时间
     * @param readTimeout
     */
    public static void setReadTimeout(int readTimeout){
        if (mOkHttpClient != null && mOkHttpClient.readTimeoutMillis() != readTimeout){
            try {
                Field readTimeoutField = mOkHttpClient.getClass().getDeclaredField("readTimeout");
                readTimeoutField.setAccessible(true);
                readTimeoutField.setInt(mOkHttpClient,readTimeout);
            }catch (NoSuchFieldException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 单独修改本次网络请求的读取超时时间
     * @param writeTimeout
     */
    public static void setWriteTimeout( int writeTimeout ) {
        if (mOkHttpClient != null && mOkHttpClient.readTimeoutMillis() != writeTimeout){
            try {
                Field writeTimeoutField = mOkHttpClient.getClass().getDeclaredField("writeTimeout");
                writeTimeoutField.setAccessible(true);
                writeTimeoutField.setInt(mOkHttpClient,writeTimeout);
            }catch (NoSuchFieldException e){
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

二、新建网络请求工具类NetWorkTool

/**
 * 网络请求方法
 */
public class NetWorkTool {


    /**
     * 网络请求
     * @param url 地址
     * @param paraDic 地址
     * @param method GET/POST
     * @param myCallBack 回调
     */
    public static <T extends BaseDao> void request( final String url, final HashMap<String,Object> paraDic, String method, final MyCallBack<T> myCallBack){


        /**
         * 通过NetWorkManager.getInstance(url,paraDic,method)获取一个call对象进行异步网络请求
         */
        Call call = NetWorkManager.getInstance(url,paraDic,method);
//        if (url == ""){ //可以根据具体url设置不同超时时间😂
//            NetWorkManager.setConnectTimeout(15000); //可以设置本次网络请求的超时时间
//        }
        call.enqueue(new Callback() {

            /**
             * 网络请求失败 没有访问到服务器
             * @param call
             * @param e
             */
            @Override
            public void onFailure( Call call, IOException e ) {

                /**
                 * 打印出本次网络请求的错误信息,开发调试使用,生产环境可以注释掉
                 */
                Log.d(" "," ");Log.d(" "," ");Log.d(" "," ");
                Log.d("网络请求失败url=",url);
                Log.d("requestHeader=", String.valueOf(call.request().headers()));
                Log.d("requestPara=", String.valueOf(paraDic));
                JUtil.i("response==", String.valueOf(e));
                Log.d("","--------------------------------------------------------------------------------------------------------");
                Log.d(" "," ");Log.d(" "," ");Log.d(" "," ");
                /**
                 * 在NetWorkManager的单例对象中保留当前网络请求的所有call对象,
                 * 当本次网络请求结束(成功、失败)以后删除此call对象
                 */
                NetWorkManager.removeCall(call);
                myCallBack.onError(PCH.mHttpConnectError,e);
            }


            /**
             * 网络请求访问到服务器了,分析服务器数据得到想要的结果
             * 如果数据很简单不需要解析为model可以直接传入BaseDao,然后获取
             * myCallBack.onSuccess(t,jsonStr,response)中的jsonStr自己做解析
             *
             * @param call
             * @param response
             * @throws IOException
             */
            @Override
            public void onResponse( Call call, Response response ) throws IOException {

                    /**
                     * 在NetWorkManager的单例对象中保留当前网络请求的所有call对象,
                     * 当本次网络请求结束(成功、失败)以后删除此call对象
                     */
                    NetWorkManager.removeCall(call);
                //response.body().string()只能执行一次
                // 否则会报AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher错误
                String jsonStr = response.body().string();

                /**
                 * 打印出本次网络请求的信息
                 */

                //格式化输出获取的数据,开发调试使用,生产环境可以注释掉
                Gson gson = new GsonBuilder().setPrettyPrinting().create();
                HashMap<String,Object> jsonModel = gson.fromJson(jsonStr,new TypeToken<HashMap<String,Object>>(){}.getType());
                String outputJsonStr = gson.toJson(jsonModel,new TypeToken<HashMap<String,Object>>(){}.getType());
                Log.d(" "," ");Log.e(" "," ");Log.i(" "," ");
                Log.d("网络请求成功url=",url);
                Log.d("requestHeader=", String.valueOf(call.request().headers()));
                Log.d("requestPara=", String.valueOf(paraDic));
                JUtil.i("response==",outputJsonStr);
                Log.d("","--------------------------------------------------------------------------------------------------------");
                Log.d(" "," ");Log.e(" "," ");Log.i(" "," ");



                //设计思想: 由上而下,层层细化: ,传入要解析对象,返回解析实体对象。
                T t = null;
                // 解析对象过程
                //获得泛型集合
                //实体类型
                Class<? extends MyCallBack> callBackClass = myCallBack.getClass();
                String name = callBackClass.getSimpleName();
                Type[] interfaces = callBackClass.getGenericInterfaces(); //获取接口类型
                if (!(interfaces[0] instanceof ParameterizedType)){ //MyCall中的范型T不能为空,如果不需要解析为model可以传BaseDao
                    myCallBack.onError(PCH.mHttpParseError,null);
                    return;
                }
                ParameterizedType parameterizedType = (ParameterizedType) (interfaces[0]);
                Type type = parameterizedType.getActualTypeArguments()[0];
                Class<T> entityClass = (Class<T>) (type);

                t = NetUtil.parse(jsonStr,entityClass);

                if (t != null){
                   
                    if (t.getStatusCode() == 200){ //请求成功了 状态码200表示成功
                        myCallBack.onSuccess(t,jsonStr,response);
                    }else if (t.getStatusCode() == 401){ //用户未登录 602表示用户不是销售

                    }else if (t.getStatusCode() == 600){ //升级

                    }else { //请求失败
                        myCallBack.onError(PCH.mHttpConnectError,null);
                    }
                }else {
                    myCallBack.onError(PCH.mHttpConnectError,null);
                }
            }
        });
    }

}

三、新建Dao的基类BaseDao

第二章节中的网络请求需要传入一个dao对象,然后返回一个数据解析成功之后的dao文件,这个dao不能为空,这个BaseDao包含了所有接口都会返回的最基本的信息

public class BaseDao {

    private String status;
    private int statusCode;
    private String message;


    public String getStatus() {
        return status;
    }

    public void setStatus( String status ) {
        this.status = status;
    }

    public int getStatusCode() {
        return statusCode;
    }

    public void setStatusCode( int statusCode ) {
        this.statusCode = statusCode;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage( String message ) {
        this.message = message;
    }
}

四、使用方法

        /**
         *这个类可以做首页初始化完成之后需要处理的事情,
         * 首页显示的东西在TabbarActivity已经初始化完成,这里可以什么都不做 😂
         */
        HashMap<String,Object> para = new HashMap<>();
        para.put("username","username");
        para.put("password","password");
        NetWorkTool.request(PCH.requestLoginAction, para, PCH.mHttpRequestPost, new MyCallBack<BaseDao>() {

            @Override
            public void onSuccess( BaseDao baseDao, String responseString, Response response ) {


//                Gson gson = new Gson();
//                HashMap<String,String> obj = gson.fromJson(responseString,(new HashMap<String,Object>()).getClass());
//                String string = obj.get("data").toString();

                Log.d("网络请求成功",responseString);
//                SharedPreferences sharedPreferences = getSharedPreferences(PCH.userLoginCookieKey,Context.MODE_PRIVATE);
//                SharedPreferences.Editor editor = sharedPreferences.edit();
//                editor.putString(PCH.userLoginCookieKey, "f26bf1fc7f9640fe8c8fa7926aebb414");
//                editor.apply();
            }

            @Override
            public void onError( String errString, IOException e ) {
                Log.d("网络请求失败",errString);
            }

        });

五、项目实战

相比较iOS我比较喜欢Android的一个用法就是内部类的使用,尤其在Dao中,不需要新建很多的Dao类,在一个类中根据json数据的结构新建内部类就可以了,见以下代码

public class MessageListDao extends BaseDao {


    private DataBean data = new DataBean();
    public DataBean getData() {
        return data;
    }

    /**
     * data中的数据
     */
    public static class DataBean{
        private int pageSize = 0;
        private int pageNo = 0;
        private int count = 0; //共多少条数据
        private int pageCount = 0; //共多少页数据
        private ArrayList<ListData> list = new ArrayList<>();

        public int getPageSize() {
            return pageSize;
        }

        public void setPageSize( int pageSize ) {
            this.pageSize = pageSize;
        }

        public int getPageNo() {
            return pageNo;
        }

        public void setPageNo( int pageNo ) {
            this.pageNo = pageNo;
        }

        public int getCount() {
            return count;
        }

        public void setCount( int count ) {
            this.count = count;
        }

        public int getPageCount() {
            return pageCount;
        }

        public void setPageCount( int pageCount ) {
            this.pageCount = pageCount;
        }

        public ArrayList<ListData> getList() {
            return list;
        }

        /**
         * 上拉加载更多数据插入
         * @param list
         */
        public void loadMoreInsertList( ArrayList<ListData> list ) {
            this.list.addAll(list);
        }

        /**
         * list中的数据
         */
        public static class ListData{
          
            private String notifyId  =  ""; /** 消息体id 用于获取消息详情*/
            private String title  =  ""; /** 标题*/
            private String content  =  ""; /** 内容*/
            private String readFlag  =  ""; /** 用户阅读状态 0未读 1已读*/
            private String sendTime  =  ""; /** 消息发送时间*/
            private String showTime  =  ""; /** 消息发送时间 */
            public String getTitle() {
                return title;
            }

            public String getContent() {
                return content;
            }

            public String getReadFlag() {
                return readFlag;
            }

            public String getSendTime() {
                return sendTime;
            }

            public String getShowTime() {
                return showTime;
            }

            public String getNotifyId() {
                return notifyId;
            }
        }
    }

}

我对网络请求的理解就是会用,对底层理解不深入,所以就不讲解了😄

demo地址
https://github.com/jzglovewjr/crmandroidj

相关文章

网友评论

    本文标题:iOS转android之网络封装

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