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项目源码
- Android用NoHttp+MVP构建项目框架
- Android-NoHttp网络框架
- android项目框架MVP
- Android 项目框架构建
- Android Router+RxAnroid+Retrofit2.0+OkHttp+MVP构建项目所需框架
- NoHttp框架
- Android网络请求框架NoHttp简介
- Android网络请求框架NoHttp简介
- Android网络请求框架NoHttp简介
- Android 项目框架 使用MVP开发
- Android MVP+RXJava+Retrofit框架的初步构建
- RxJava+Retrofit+OkHttp3+Dagger2+MVP构建Android项目简单例子
- MVP架构 + EventBus 构建项目
- Http标准协议Android网络框架——NoHttp
- Android网络框架综述(一)OkHttp、NoHttp、Volley
- Http标准协议Android网络框架——NoHttp
- Http标准协议Android网络框架——NoHttp
- Http标准协议Android网络框架——NoHttp
- 微信运营注意什么问题?公众平台运营问题解答。
- hdu 1824 Let's go home (2-sate)
- 购物车的修改查找排序批量删除
- Netty源码分析(二)—客户端初始化
- 数据结构实验之图论六:村村通公路
- Android用NoHttp+MVP构建项目框架
- 2017_11_22 学习Json数据与Java对象互转的两种方式(一)
- Spring—InitializingBean
- Ubuntu系统使用Doxygen生成文档
- postgres 并发控制
- docker 学习有感
- java导出简单的word模板
- SpringBoot使用JavaConfig配置ActiveMQ
- 仿探探卡片图片展示