Android用NoHttp+MVP构建项目框架

最近使用了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项目源码

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值