美文网首页
开发问题记录随笔之更换网络请求框架由HttpUrlConnect

开发问题记录随笔之更换网络请求框架由HttpUrlConnect

作者: 打工崽 | 来源:发表于2021-03-27 10:48 被阅读0次

    昨天心血来潮想把自己的项目里的网络请求框架从HttpUrlConnection更换为OkHttp,本来一开始没想着用GSON解析JSON数据的,后来发现GSON解析是真的方便,便在此记录一下

    先来看看原来用HttpUrlConnection是怎么写的吧

    HttpUrlConnection

    new Thread() {
    
                public void run() {
                    // 准备一个要访问的链接地址
    
                    //此处我把自己的网址隐藏了噢,你应该要自己去申请一个网址噢
                    String site = "***************";
                    try {
                        // 转换字符串为URL对象
                        URL url = new URL(site);
                        // 获得网络请求对象
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        // 设置请求模式为GET
                        conn.setRequestMethod("GET");
                        // 获得网络请求状态码
                        int httpCode = conn.getResponseCode();
                        // 如果正常
                        if (httpCode == 200) {
                            // 获得字节输入流
                            InputStream is = conn.getInputStream();
                            // 字节流转字符流
                            InputStreamReader isr = new InputStreamReader(is);
                            // 套一层缓冲流提高效率(字符输入缓冲流)
                            BufferedReader br = new BufferedReader(isr);
    
                            // 用于拼接服务器返回的字符数据的字符串对象
                            String data = new String();
    
                            // 每次循环读取的buffer
                            String buf;
    
                            // 循环读取
                            while ((buf = br.readLine()) != null) {
                                // 拼接结果
                                data += buf;
                            }
    
                            // 关闭流
                            br.close();
    
                            // 输出接收到的服务器的数据
                            Log.d("HomeFragment", data);
    
                            // 解析JSON数据(一层一层解析)
                            // 最外层是一个JSONObject对象
                            JSONObject object = new JSONObject(data);
                            // 通过“result”键取得对应的值:JSONObject对象
                            JSONObject result = object.getJSONObject("result");
                            // 拿到“data”键对应的值:JSONArray(JSONObject数组)
                            JSONArray dataArray = result.getJSONArray("data");
                            // 遍历数组
                            for (int i = 0; i < dataArray.length(); i++) {
                                //每一条新闻的JSONObject对象
                                JSONObject jsonObjectNews = dataArray.getJSONObject(i);
                                //拿到新闻中需要需要的数据
                                String name = jsonObjectNews.getString("title");
                                String url2 = jsonObjectNews.getString("url");
                                String image = jsonObjectNews.getString("thumbnail_pic_s");
                                //添加到数据源中
                                News news = new News(name,image);
                               mNewsList.add(news);
                                mUrlList.add(url2);
                            }
    
                            //切换到主线程刷新UI
                            getActivity().runOnUiThread(new Runnable() {
    
                                @Override
                                public void run() {
                                    //主线程代码
                                    newsAdapter.upDateItem(mNewsList);
                                }
                            });
    
                        } else {
                            Log.d("MainActivity", "http请求失败,状态码:" + httpCode);
                        }
    
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }
    
            }.start();
    

    其实具体每一步做了什么注释都有写,具体思路还是在这里说一下吧

    首先网络请求操作是耗时的,要新开一个线程new Thread,里面的网址我是申请的聚合数据的API进行测试的,如果你也想要就需要自己去注册噢,然后把它给你的网址复制在上面

    try块中就是HttpUrlConnection的操作,实例化URL对象,网络请求对象,并且设置请求模式为GET,判断得到的网络状态码是不是200,因为网络请求几个状态码里只有200是可以正常操作的,往下就是java里熟悉的字符流包裹一层缓冲流加快速度,每一次将字符串拼接,老实说这样是非常耗空间的。记得要关闭流噢

    再往下就是解析JSON数组,你可以看下图,申请的JSON数组还算是比较复杂的,所以基本步骤就是先获取result,再获取data,然后遍历,遇到我们需要的title,url,imageUrl这三个数据就取出来,放到List里,然后切换到主线程去更新界面,最后抛异常结束,子线程最后不要忘了调用start()方法启动


    image.png

    我之前的文章记录了学习OkHttp的相关内容,学习之后思考如何更换网络请求框架,因为还不是很熟悉,所有没有封装成框架,所以我们先看代码,再分析思路吧

    OkHttp

    private void sendRequestWithOkHttp(){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    OkHttpClient okHttpClient = new OkHttpClient();
                    Request.Builder requestBuilder = new Request.Builder().url("http://v.juhe.cn/toutiao/index?type=youxi&page=1&page_size=25&key=2cb4dfb90827deb9158e0a1f4e4f1e45");
                    requestBuilder.method("GET",null);
                    Request request = requestBuilder.build();
                    Call call = okHttpClient.newCall(request);
                    call.enqueue(new Callback() {
                        @Override
                        public void onFailure(Call call, IOException e) {
    
                        }
    
                        //此方法在主线程中回调,因此不能做耗时操作,要么重开子线程,要么改为同步方法直接接收Response
                        @Override
                        public void onResponse(Call call,  Response response) throws IOException {
                            final String responseData = response.body().string();
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    parseJSONWIthGson(responseData);
                                    showResponse(responseData);
                                }
                            }).start();
    
                        }
                    });
                }
            }).start();
        }
    
    
        private void parseJSONWIthGson(String jsonData) {
            Gson gson = new Gson();
    
            News news = gson.fromJson(jsonData,News.class);
            List<News.DataBean> newsDataBean = news.getResult().getData();
    
            for (News.DataBean ndb : newsDataBean) {
                mNewsListTitle.add(ndb);
                mUrlList.add(ndb.getUrl());
            }
        }
    
    private void showResponse(final String response) {
            getActivity().runOnUiThread(new Runnable() {
    
                @Override
                public void run() {
                    //主线程代码
                    newsAdapter.upDateItem(mNewsListTitle);
                }
            });
        }
    

    GSON解析部分我们先不看,我们来分析OkHttp,其实具体写法是和《第一行代码》,《Android进阶之光》差不多的

    先写一个方法sendRequestWithOkHttp(),之前说过了网络请求是耗时操作,所以这里也要new Thread,在run()方法里走OkHttp基本三步骤:实例化OkHttpClient,实例化Request,实例化Call。然后调用call的enqueue异步GET请求方法(同步GET请求调用execute()就好啦),在onResponse()方法里进行GSON解析。

    问题就出在这里了,如果按这个写法的话,在我的app里首次进入刷新新闻时需要大概五六秒的时间,甚至还没有我之前用HttpUrlConnection快,简单思考之后判断应该是还好当前数据量小,不然大的话就要ANR了,经过Debug后发现在onResponse()方法执行时,居然会回到主线程!就像我注释里写的那样,即使在之前开了线程,即使做了异步GET请求,在onResponse()方法里依然会回到主线程,那么我下面的解析GSON的过程必然是在主线程进行的了,所以会造成如上所说的情况

    这里解决方法有两种:

    一种是将上面的GET请求变为同步的,确实新开一个线程又异步GET有点多余

    另一种是在onResponse()方法里再开一个线程,这样的话需要把responseData这个数据拿出来并且final修饰,因为这种操作不加final的话有可能会引起数据在读取过程中出现各种各样奇奇怪怪的问题,final修饰让其不可变就不会有数据问题了

    最后一定记得如果像我一样在异步请求的onResponse()方法里又开一个线程进行网络请求操作的话,最后一定要通过Handler切回主线程,切回主线程有很多方式,我这里使用的是runOnUiThread()方法,需要用getActivity()调用是因为我的首页是在一个Fragment里的,需要获取上下文


    聊完了框架更换,我们看GSON解析JSON数据带来了哪些便利吧

    JSONBean

    public class News implements Serializable {
        private String reason;
        private Result result;
    
        public String getReason() {
            return reason;
        }
    
        public void setReason(String reason) {
            this.reason = reason;
        }
    
        public Result getResult() {
            return result;
        }
    
        public void setResult(Result result) {
            this.result = result;
        }
    
        public static class Result{
            private String stat;
            private List<DataBean> data;
            public String getStat() {
                return stat;
            }
            public void setStat(String stat) {
                this.stat = stat;
            }
            public List<DataBean> getData() {
                return data;
            }
            public void setData(List<DataBean> data) {
                this.data = data;
            }
    
        }
    
        public static class DataBean implements Serializable{
            private String uniquekey;
            private String title;
            private String date;
            private String category;
            private String author_name;
            private String url;
            private String thumbnail_pic_s;
            private String thumbnail_pic_s02;
            private String thumbnail_pic_s03;
            public String getUniquekey() {
                return uniquekey;
            }
            public void setUniquekey(String uniquekey) {
                this.uniquekey = uniquekey;
            }
            public String getTitle() {
                return title;
            }
            public void setTitle(String title) {
                this.title = title;
            }
            public String getDate() {
                return date;
            }
            public void setDate(String date) {
                this.date = date;
            }
            public String getCategory() {
                return category;
            }
            public void setCategory(String category) {
                this.category = category;
            }
            public String getAuthor_name() {
                return author_name;
            }
            public void setAuthor_name(String author_name) {
                this.author_name = author_name;
            }
            public String getUrl() {
                return url;
            }
            public void setUrl(String url) {
                this.url = url;
            }
            public String getThumbnail_pic_s() {
                return thumbnail_pic_s;
            }
            public void setThumbnail_pic_s(String thumbnail_pic_s) {
                this.thumbnail_pic_s = thumbnail_pic_s;
            }
            public String getThumbnail_pic_s02() {
                return thumbnail_pic_s02;
            }
            public void setThumbnail_pic_s02(String thumbnail_pic_s02) {
                this.thumbnail_pic_s02 = thumbnail_pic_s02;
            }
            public String getThumbnail_pic_s03() {
                return thumbnail_pic_s03;
            }
            public void setThumbnail_pic_s03(String thumbnail_pic_s03) {
                this.thumbnail_pic_s03 = thumbnail_pic_s03;
            }
    
        }
    }
    

    我在这里解析的JSON是选择全部解析的,因为以后我还会添加新功能,所以就索性全部解析出来了,这样的话bean代码会有点多,如果你还记得上面那张JSON数据图的话,你就会发现我是一一对应的,只不过创建了两个静态内部类用于存放同级的JSON数据,比如reason,result是同级的。stat,data是同级的,如此。


    image.png

    GSON

    private void parseJSONWIthGson(String jsonData) {
            Gson gson = new Gson();
    
            News news = gson.fromJson(jsonData,News.class);
            List<News.DataBean> newsDataBean = news.getResult().getData();
    
            for (News.DataBean ndb : newsDataBean) {
                mNewsListTitle.add(ndb);
                mUrlList.add(ndb.getUrl());
            }
        }
    
    private void showResponse(final String response) {
            getActivity().runOnUiThread(new Runnable() {
    
                @Override
                public void run() {
                    //主线程代码
                    newsAdapter.upDateItem(mNewsListTitle);
                }
            });
        }
    

    可以明显看出GSON解析比JSON解析代码要少很多,但是对于我个人来说JSON解析还是挺符合我的逻辑思路的,一层层数据剥开最后取自己需要的数据,可是以后还是要渐渐习惯用GSON来解析数据吧,因为确实很方便,代码很简单,gson自带的方法fromJson()将数据先放到news里,之后使用泛型集合将data数据放入,最后一个增强for循环遍历把每一个DataBean类型的数据放到mNewsListTitle里,在ViewHolder里进行TextView和ImageView的视图绑定操作。把String类型的url单独拿出来用于跳转。但这是我个人项目里的逻辑,服务于我个人的项目。你应该去写自己的逻辑噢


    欢迎指正。

    相关文章

      网友评论

          本文标题:开发问题记录随笔之更换网络请求框架由HttpUrlConnect

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