Android用NoHttp+MVP构建项目框架

来源:互联网 发布:大数据岗位 知乎 编辑:程序博客网 时间:2024/05/22 15:37

最近使用了NoHttp+MVP写了一个项目,NoHttp是一个网络框架,个人觉得是我用过最好用的网络请求框架,没有之一,嗯,对!(NoHttp开源地址) 严大神之作。MVP,大家都再熟悉不过了,优点就是高度解耦和能有效避免内存泄漏。

一、NoHttp网络框架:要详细的可以到GitHub里面看,首先把NoHttp封装成单例模式:

/** * @author: Administrator * @description:    网络请求 * @date: 2017-11-22   09:23 */public class CallServer {    private static CallServer instance;    /**     * 请求队列。     */    private RequestQueue requestQueue;    /**     * 下载队列     */    private DownloadQueue downloadQueue;    private CallServer() {        requestQueue = NoHttp.newRequestQueue(5);        downloadQueue = NoHttp.newDownloadQueue();    }    /**     * 请求队列。     */    public static CallServer getInstance() {        if (instance == null)            synchronized (CallServer.class) {                if (instance == null)                    instance = new CallServer();            }        return instance;    }    /**     * 添加一个请求到请求队列。     *     * @param what      用来标志请求, 当多个请求使用同一个Listener时, 在回调方法中会返回这个what。     * @param request   请求对象。     * @param listener  结果回调对象。     */    public <T> void add(int what, Request<T> request, OnResponseListener listener) {        requestQueue.add(what, request, listener);    }    /**     * 文件下载     * @param what 用来标志请求, 当多个请求使用同一个Listener时, 在回调方法中会返回这个what。     * @param request 请求对象。     * @param listener 结果回调对象。     */    public  void loadAdd(int what, DownloadRequest request, DownloadListener listener){        downloadQueue.add(what,request,listener);    }    public void loadStop(){        downloadQueue.stop();    }    /**     * 取消这个sign标记的所有请求。     * @param sign 请求的取消标志。     */    public void cancelBySign(Object sign) {        requestQueue.cancelBySign(sign);    }    /**     * 取消队列中所有请求。     */    public void cancelAll() {        requestQueue.cancelAll();    }}

然后就是网络请求异常处理:

/** * @author: Administrator * @description: 请求异常 * @date: 2017-08-24   16:00 */public class HttpException {    public static String doException(Throwable exception) {        if (exception instanceof NetworkError) {// 网络不好            return MyApplication.getMyApplication().getString(R.string.error_please_check_network);        } else if (exception instanceof TimeoutError) {// 请求超时            return MyApplication.getMyApplication().getString(R.string.error_timeout);        } else if (exception instanceof UnKnownHostError) {// 找不到服务器            return MyApplication.getMyApplication().getString(R.string.error_not_found_server);        } else if (exception instanceof URLError) {// URL是错的            return MyApplication.getMyApplication().getString(R.string.error_url_error);        } else if (exception instanceof NotFoundCacheError) {            // 这个异常只会在仅仅查找缓存时没有找到缓存时返回            return MyApplication.getMyApplication().getString(R.string.error_not_found_cache);        } else {            return MyApplication.getMyApplication().getString(R.string.error_unknow);        }    }}

这些东西都可以在开源项目上找到。

二、MVP架构封装:
1、M层(model),model层是用来处理数据来源的,请求网络数据或者加载本地数据库数据等,这项目中,抽取了一个model层的基类,因为model层是用来网络请求的,所以在model层抽取了网络请求的添加到请求队列和取消请求的方法,这里取消请求这个方法比较重要,比如,在网络不好的情况下,你在Activity发出请求,但是后台10s后再给你做出了响应,此时,你结束了这个Activity,跳到下一个Activity,这个情况下有可能会造成空指针异常的请情况,或者,在下一个Activity中会弹出上一个Activity的Toast内容,这个就非常不好了,所以需要一个取消请求的方法,与Activity的什么周期绑定,在onDestroy中去取消网络请求,代码如下:

public class BaseModel {    //用给每个网络请求添加一个标志,用于取消请求    private Object cancelObject;    public BaseModel() {        this.cancelObject = new Object();    }    protected <T> void doRequest(int what, Request<T> request, OnResponseListener<T> listener) {        // 这里设置一个sign给这个请求。        request.setCancelSign(cancelObject);        CallServer.getInstance().add(what, request, listener);    }    public void cancelRequest() {        //取消请求        CallServer.getInstance().cancelBySign(cancelObject);    }}

2、V层(View),view层是一个接口形式,将所有有关于view的方法写成接口,在接口里面处理相关逻辑
当然view层也可以抽出基类,比如,网络加载时的加载框的显示与隐藏,当然还以个是toast,在这个例子中没有用到这个基类:

public interface BaseView {    void showLoading();    void dismissLoading();}

3、P层(Presenter) P就是连接model层与View层的中间桥梁,这样就达到了View与model层的解耦的方式,为了防止内存泄漏,p层也需要抽取出基类,将View绑定到Presenter上,同时,当view销毁时,解除绑定,同时View使用了弱引用:

public abstract class BasePresenter<T> {    public WeakReference<T> refView;   // public T mView;    public void attach(T mView){        //this.mView = mView;        refView = new WeakReference<T>(mView);    }    public void detach(){        //mView = null;        if (refView != null) {            refView.clear();            refView = null;        }    }    public boolean isViewAttached() {        return refView != null && refView.get() != null;    }    //当对象被销毁时,可以用次方法获取    protected T getRefView() {        return refView.get();    }    //用于取消网络请求    public abstract void destroy();}

4、再看Activity的基类:Activity需要将View和model绑定在一起,然后在子类Activity中实现相关view的接口,在Activity中引入了Persenter的泛型作为基类,同时听歌一个抽象方法去实例化该对象,然后在onResume中绑定View,在onDestroy中解除绑定:

public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity {    public T presenter;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        presenter = initPresent();    }    /**     初始化view抽象方法     */    protected abstract void initView();    @Override    protected void onResume() {        super.onResume();        presenter.attach((V) this);    }    @Override    protected void onDestroy() {        presenter.detach();        super.onDestroy();    }    public abstract T initPresent();}

三,用一个登录的例子来使用这个框架:
登录

1、网络请求:LoginModel,写网络请求,然后将网络请求的结果用接口回调出去:

public class LoginModel extends BaseModel{    public void loginRequest(final String userName, final String psw, final OnLoginListener loginListener) {        Request<JSONObject> request = NoHttp.createJsonObjectRequest(Constants.URL + "/applogin", RequestMethod.POST);        request.add("mobile", userName);        request.add("userPwd", Base64.encode(psw.getBytes()));        request.add("app", "android");        doRequest(0, request, new OnResponseListener<JSONObject>() {            @Override            public void onStart(int what) {                loginListener.onStart();            }            @Override            public void onSucceed(int what, Response<JSONObject> response) {                if (response.responseCode() == 200) {                 Log.i("tag","登录:" + response.get());                    JSONObject object = response.get();                    if (object.optInt("code") == 1) {                        String data = object.optString("data");                        if (data != null) {                            LoginInfo info = JSON.parseObject(data, LoginInfo.class);                            loginListener.onSuccess(info);                        }                    } else {                        loginListener.onFailed(object.optString("msg"));                    }                }            }            @Override            public void onFailed(int what, Response<JSONObject> response) {                loginListener.onFailed(HttpException.doException(response.getException()));            }            @Override            public void onFinish(int what) {                loginListener.onFinish();            }        });    }//取消网络请求    @Override    public void cancelRequest() {        super.cancelRequest();    }//接口回调    public interface OnLoginListener {        void onStart();        void onSuccess(LoginInfo info);        void onFailed(String msg);        void onFinish();    }}

看网络请求回调,有四个方法,onStart,onSucceed,onFailed,onFinish,一般在onStart,onFinish找个来处理网络请求的加载框的显示和隐藏,onFinish方法不管请求成功或者失败,都会回调这个方法,在每个方法中都会有what这个参数,这个是用来区分请求队里中的请求,比如你在这个界面中有三个请求,同时将这个三个请求添加到请求队列中,然后用同一个回调(OnResponseListener)来回调数据,此时这个what就是用来区分是哪个请求的。

2、ILoginView 的接口,结合上面的界面和网络请求的参数,我们可以知道,在view中,我们需要获取,账号,密码,更新view,显示Toast,显示加载框,隐藏加载框这些方法:

public interface ILoginView {    String getLoginName();    String getLoginPsw();    void updateView(LoginInfo info);    void showError(String msg);    void showLoading();    void hiddenLoading();}

3、LoginPresenter:处理LoginModel和ILoginView之间的关系,也就是数据和view之间的处理,有些写法也会将这里面的方法用接口的形式来处理,但是我个人觉得这里面直接自己写方法方便一点:

public class LoginPresenter extends BasePresenter<ILoginView>{    private ILoginView iLoginView;    private LoginModel loginModel;    public LoginPresenter(ILoginView iLoginView) {        this.iLoginView = iLoginView;        loginModel = new LoginModel();    }    public void login(){        loginModel.loginRequest(iLoginView.getLoginName(), iLoginView.getLoginPsw(), new LoginModel.OnLoginListener() {            @Override            public void onStart() {                iLoginView.showLoading();            }            @Override            public void onSuccess(LoginInfo info) {                iLoginView.updateView(info);            }            @Override            public void onFailed(String msg) {                iLoginView.showError(msg);            }            @Override            public void onFinish() {                iLoginView.hiddenLoading();            }        });    }    @Override    public void destroy() {        loginModel.cancelRequest();    }}

4、LoginActivity:登录界面实现ILoginView接口用来处理相关逻辑,同时这里要重要说明的就是onDestroy方法,这个方法调用presenter中的destroy方法来取消网络请求:

public class MainActivity extends BaseActivity<ILoginView, LoginPresenter> implements ILoginView {    @InjectView(R.id.et_account)    EditText etAccount;    @InjectView(R.id.et_psw)    EditText etPsw;    @InjectView(R.id.commit)    Button commit;    @InjectView(R.id.tv_result)    TextView tvResult;    @InjectView(R.id.loading)    ProgressBar loading;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.inject(this);        initView();    }    @Override    protected void initView() {    }    @Override    public LoginPresenter initPresent() {        return new LoginPresenter(this);    }    @Override    public String getLoginName() {        return etAccount.getText().toString();    }    @Override    public String getLoginPsw() {        return etPsw.getText().toString();    }    @Override    public void updateView(LoginInfo info) {        tvResult.setText(info.toString());    }    @Override    public void showError(String msg) {        tvResult.setText(msg);        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();    }    @Override    public void showLoading() {        loading.setVisibility(View.VISIBLE);    }    @Override    public void hiddenLoading() {        loading.setVisibility(View.GONE);    }    @OnClick(R.id.commit)    public void onViewClicked(View view) {        switch (view.getId()) {            case R.id.commit:                presenter.login();                break;        }    }    @Override    protected void onDestroy() {        super.onDestroy();        presenter.destroy();    }}

到此,这个架构的例子已经全部完成,相信直接看代码就可以很明了了,同时如果有什么错误的地方还请多多指正!

源码:NoHttp+MVp项目源码

原创粉丝点击