Android通用网络请求解析框架.1(需求,思想)

19 篇文章 0 订阅
14 篇文章 0 订阅
笔者将通过11篇博客对个人开源框架进行讲解,本篇为第1篇,讲解需求,思想。


如果有兴趣一起讨论本框架的内容,请加QQ群:271335749



从代码说起吧。对于初学者,我们写一个网络请求,通常的,会是下面这样
package com.chenjian.testandroid;

import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        request();
    }

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0:
                    Object object = msg.obj;
                    // 拿着object进行操作,可能要更新ui
                    break;
            }
        }
    };

    private void request() {
        new Thread() {
            @Override
            public void run() {
                String url = "";
                Object object = null;
//                Object object = Http.get(url);
                Message message = Message.obtain();
                message.what = 0;
                message.obj = object;
                mHandler.sendMessage(message);
            }
        }.start();
    }
}

先定义一个Handler,因为多数情况下要进行ui操作。
接着是request方法,里面首先是拿到url,url可能经过各种拼接。
再用url进行http请求,这里省略了http请求的细节部分,但对整体逻辑的理解并没影响。
再接着,我们把请求的结果,通过handler,返回给ui线程。
这样,ui线程就可以拿着数据进行更新ui了。


问题出现了:
1.每个类里面都要写一个Handler。试想一下如果整个项目就写一个全局静态的,那在handleMessage里面的处理会多麻烦。
2.每次请求都要开一个线程,或者说是一个类里面至少要开一个。开线程并不算什么,关键是,你的代码,重复了。
3.线程执行方法里,url的拼接,多数项目是有公共参数的,也就是每一个http请求都要带的参数。且url拼接,直接用普通字符串进行相加,不够符合面向对象思想,也不灵活。
4.发起http请求并将结果包装成Message返回给ui线程,同样存在代码重复的问题。


当然一些开发者会采用另一种方式,可以少写些代码,也就是AsyncTask
package com.chenjian.testandroid;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;

public class OtherActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        request();
    }

    private void request() {
        new AsyncTask<String, Integer, Object>() {
            @Override
            protected Object doInBackground(String... params) {
                String url = "";
                Object object = null;
//                Object object = Http.get(url);
                return object;
            }

            @Override
            protected void onPostExecute(Object o) {
                Object object = o;
                // 拿着object进行操作,可能要更新ui
            }
        }.execute();
    }
}

但是2和3两点,还是不可避免,并且在代码行数上,并没有减少太多。


以上的代码分析,还漏掉了至关重要的两点:
1.解析,通常是将json字符串解析为我们代码中的实体
2.错误处理,包括请求的过程中,解析的过程中,ui线程拿到实体后还需要判断是否为null等

1.解析
解析是繁琐的,对于json,android的sdk里面就有相应的类和方法,也有一些第三方解析框架,比如gson,fastjson。
笔者的开源框架,完全兼容第三解析框架,且第三方解析的内容,只是本框架的一小部分内容。

现在来说本篇中至关重要的一个思想。
笔者到现在也接触过不少项目了,发现了一个共性,也就是服务端返回的数据,多数情况下会是以一定格式的,比如下面这样:

{code, message, data : { 这里才是具体数据 } }

可能你在外层会增加一些数据,比如time之类的,但是总归是类似于上面那样的。

因此,我们在解析的时候,可以分为两部分,一部分是外层的解析,一部分是内层具体数据的解析。

对于外层的解析,可以抽成公共部分,这样代码就省了很多。
内层的解析,需要具体分析,因为内层可能返回的是单个Bean(实体),也可能是List<Bean>,更有可能是多种不同的Bean和不同的List<Bean>组合而成。当然,也可能是空字符串。

在编程上,我们可以在一个类里面解析外层数据,此类定义一个抽象方法,再由他的子类实现抽象方法去解析内层数据。来看看下面这段代码,应该就很清晰了
abstract public class ParseJson {

    private void parseJson(String jsonString) throws JSONException {
        JSONObject jsonObject = new JSONObject(jsonString);
        String code = jsonObject.getString("code");
        String message = jsonObject.getString("message");
        Object data = parseData(jsonObject.getString("data"));
    }

    abstract Object parseData(String jsonString) throws JSONException;
}

子类在parseData方法中,可以选择使用Android自带的Json相关类进行解析,也可以使用gson,或者fastjson等第三方解析框架。


2.错误处理
错误处理通常是麻烦在判断,比如ui线程拿到实体后还需要判断是否为null,确实是够蛋疼的但又不得不去做。
还有一个比较让人头疼的地方,当ui线程拿到数据后,如果判断为错误,ui线程很难知道是哪个步骤出错了,必须在之前要做很多的记录才行。



说到最后,那我们到底需要一个怎样的网络请求解析框架呢?本框架又能达到怎样的效果呢?
1.必须是一步到位的,通过一个入口,我传入url等参数,如果请求成功,返回给我时,是已经解析好的Bean
2.所有环节出错,都要归类到一个地方,且在那个地方,可以判断是哪个步骤出错了,以及出错的相关信息
3.请求后,不管成功失败,我得到结果时,必须已经回到主线程

用代码来表达最好了,我希望是这样的
Util.getXXBean("url") {
    onSuccess(XXBean xxBean) {
        // 这里是ui线程
    }

    onError(int errorCode, ErrorBean errorBean) {
        // 这里是ui线程
    }
};

Util.getYYBean("url") {
    onSuccess(YYBean yyBean) {

    }

    onError(int errorCode, ErrorBean errorBean) {

    }
};

通过util工具类,把url传进去。
首先,回调onSuccess时,已经是实体Bean了,且是不为null的,如果为null,就会回调onError。
这里的Bean是已经去掉了外层的数据,用data里面的内层数据所解析出来的。
当然你如果需要在成功的时候也要得到外层数据,可以在onSuccess方法里面加上一些参数,或者一个参数实体来包含那些数据。

其次,回调onError方法时,通过errorCode,就可以判断错误类型,
而且这个方法带了一个ErrorBean参数,里面记录了一些错误的信息,比如json解析出错,http请求的status码不为200,连接超时,以及所有可能的错误信息。
在这里可以把错误信息提示给用户,或者你的代码逻辑需要,总之更加清楚错误来源。

最后,onSuccess和onError都必须是已经回到ui线程。
本框架也提供了同步请求,即直接在当前线程中请求和返回的方法,后面的章节会讲到。

细心的你也许发现了,以上的代码有一个致命的缺点,也就是一种Bean需要在Util类里面写一个方法。
怎么办?你应该想到了:泛型。使用泛型吧,感谢这个伟大的发明。

使用泛型后,代码修改,会简洁成下面这个样子
Util<T>.getBean("url") {
    onSuccess(T t) {

    }

    onError(int errorCode, ErrorBean ErrorBean) {

    }
};

当然,前面也说过了返回的类型不一定是单个Bean的,也可能是List<Bean>
Util<T>.getListBean("url") {
    onSuccess(List<T> ts) {

    }

    onError(int errorCode, ErrorBean ErrorBean) {

    }
};

也许你还不满足,需要自定义
Util<A, B, C>.getCustomBean("url") {
    onSuccess(A a, List<B> bs, C c) {

    }

    onError(int errorCode, ErrorBean ErrorBean) {

    }
};

或者你不想解析成实体,或者你不关心返回结果,都可以写成下面这样
Util.getString("url") {
    onSuccess(String string) {

    }

    onError(int errorCode, ErrorBean ErrorBean) {

    }
};

以上的几种模式,几乎满足了所有网络请求解析需求。

鉴于本篇是讲解思想,以上代码都比较简陋,跟源码中有所差异,url的拼接,这里也没介绍。
本篇也不想细讲代码,总体上就是阐述一个思想,不写重复的代码,让使用框架的开发者方便,把最有用的信息都能采集并归类到一个地方。

下篇将讲解整个框架的构造 


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值