前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >P1-Android基于MVP实现号码归属地查询

P1-Android基于MVP实现号码归属地查询

作者头像
张风捷特烈
发布2018-09-29 11:16:01
6480
发布2018-09-29 11:16:01
举报
文章被收录于专栏:Android知识点总结
零、前言

1.最近在总结了安卓的知识点,现在来穿插一个小项目,算是对前面的一个小小总结 2.本案例是以前在慕课网的学习的一个免费课程,代码看不懂的可以去看看:号码归属地查询实用工具 3.相比与视频课程,本案例按照我的理解,对项目结构进行了优化。 4.案例网络模块是自己对okhttp3的封装,当然你也完全可以使用其他的上层网络框架 5.相比课程,加入缓存机制,使用前面写过的缓存封装:详见:缓存文件封装 6.使用前面的listview封装类:详见:ListView的封装 7.Json的解析详见:Json在Android中的使用--Gson

项目结构及类图

号码归属地查询MVP类图.png

号码归属地查询.gif

手机归属地查询缓存.png


一、M:模型层:负责数据的请求、解析、过滤等数据层操作

1.将网络加载数据逻辑放在模型层,提供数据给控制层。 2.模型层中设置加载监听用来传递数据,及状态(利于控制层对视图层控制)

1.模型层接口--定义模型的方法接口
代码语言:javascript
复制
/**
 * 作者:张风捷特烈<br/>
 * 时间:2018/8/31 0031:13:32<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:模型层接口
 */
public interface ICallModel {
    /**
     * 获取数据
     *
     * @param ctx      上下文
     * @param phoneNum 电话号码
     */
    void getData(Context ctx, String phoneNum);
}
2.模型层接口实现类:负责数据的获取
代码语言:javascript
复制
/**
 * 作者:张风捷特烈<br/>
 * 时间:2018/8/31 0031:13:34<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:模型层,提供数据
 */
public class CallModelImpl implements ICallModel {
    private String mUrl = "https://tcc.taobao.com/cc/json/mobile_tel_segment.htm";

    /**
     * 获取Json
     *
     * @param phoneNum 电话
     */
    public void getData(Context ctx, String phoneNum) {
        //获取网络数据}
        HashMap<String, String> param = new HashMap<>();
        param.put("tel", phoneNum);
        new GetDataWithCache(mUrl, param, new InnerFileStrategy(ctx)) {
            @Override
            public void success(String response) {
                String[] split = response.split("=");
                Phone phone = new Gson().fromJson(split[1].trim(), Phone.class);
                if (mOnLoadListener != null) {
                    mOnLoadListener.success(phone);
                }
            }

            @Override
            public void error(String error) {
                if (mOnLoadListener != null) {
                    mOnLoadListener.error(error);
                }
            }
        };
    }

    ///////////////设置加载监听,传递数据,及状态(利于控制层对视图层控制)////////////////
    public interface OnLoadListener {
        /**
         * 成功监听
         *
         * @param phone 解析后得到的实体
         */
        void success(Phone phone);

        /**
         * 失败监听
         *
         * @param str 失败信息
         */
        void error(String str);
    }

    private OnLoadListener mOnLoadListener;

    public void setOnLoadListener(OnLoadListener onLoadListener) {
        mOnLoadListener = onLoadListener;
    }
}
3.实体类:Phone
代码语言:javascript
复制
public class Phone {

    /**
     * mts : 1871507
     * province : 安徽
     * catName : 中国移动
     * telString : 18715079389
     * areaVid : 30509
     * ispVid : 3236139
     * carrier : 安徽移动
     */
     
    private String mts;
    private String province;
    private String catName;
    private String telString;
    private String areaVid;
    private String ispVid;
    private String carrier;
    
    //get set 方法省略......

二、控制层:View和Model沟通的桥梁
1.基类BasePresenter:为了方便让控制层和Activity生命周期同步
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/4/25:9:40
 * 邮箱:1981462002@qq.com
 * 说明:Presenter基类--为了方便让控制层和Activity生命周期同步
 */
public class BasePresenter {
    private Context mCtx;

    public void attach(Context ctx) {
        mCtx = ctx;

    }

    public void onPause() {

    }

    public void onResume() {

    }

    public void onDestroy() {
        mCtx = null;
    }

    public Context getCtx() {
        return mCtx;
    }
}
2.控制层实现类:CallPresenterImpl
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/4/25:9:38
 * 邮箱:1981462002@qq.com
 * 说明:控制层实现类
 */
public class CallPresenterImpl extends BasePresenter {


    public ICallView mICallView;
    private Phone mPhone;
    private final CallModelImpl mCallModel;
    Handler handler = new Handler();

    public CallPresenterImpl(ICallView iCallView) {
        mICallView = iCallView;
        mCallModel = new CallModelImpl();
    }

    /**
     * 从网络获取数据
     *
     * @param phoneNum 手机号
     */
    public void getDataFromInternet(final String phoneNum) {
        if (!checkPhoneNum(phoneNum)) {
            //号码错误让视图层处理
            mICallView.numError();
            return;
        }
        //校验正确--视图层:显示进度条

        mICallView.showLoading();

        //耗时操作新开线程
        new Thread() {
            @Override
            public void run() {
                handler.post(
                        new Thread(new Runnable() {
                            @Override
                            public void run() {
                                getDataFromModel(phoneNum);
                            }
                        }));
            }
        }.start();
    }

    /**
     * 从模型层获取数据,并根据监听设置视图层
     * @param phoneNum 号码
     */
    private void getDataFromModel(String phoneNum) {
        mCallModel.getData(getCtx(), phoneNum);
        mCallModel.setOnLoadListener(new CallModelImpl.OnLoadListener() {
            @Override
            public void success(Phone phone) {
                //接收模型层数据
                mPhone = phone;
                //网络加载、解析数据完成--视图层:隐藏进度条
                mICallView.hideLoading();
                //视图层--更新视图
                mICallView.updateView();
            }

            @Override
            public void error(String str) {
                //网络加载失败--视图层:隐藏进度条
                mICallView.hideLoading();
                mICallView.loadDataError();
            }
        });
    }

    public Phone getPhone() {
        return mPhone;
    }

    /**
     * 检查手机号是否合法
     *
     * @param phone 手机号
     * @return 是否合法
     */
    public boolean checkPhoneNum(String phone) {
        Pattern pattern = Pattern.compile("^[1][3,4,5,8][0-9]{9}$"); // 验证手机号
        Matcher matcher = pattern.matcher(phone);
        return matcher.matches();
    }

}

三、视图层 V:负责视图部分展示、视图事件处理
1.显示和消失对话框接口--由于非常常见所以单独抽出
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/4/25:9:37
 * 邮箱:1981462002@qq.com
 * 说明:显示和消失对话框接口--由于非常常见所以单独抽出
 */

public interface ILoadingView {
    /**
     * 显示进度条
     */
    void showLoading();

    /**
     * 隐藏进度条
     */
    void hideLoading();

}
2.视图层接口
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/4/25:9:31
 * 邮箱:1981462002@qq.com
 * 说明:视图层接口
 */
public interface ICallView extends ILoadingView {
    /**
     * 更新视图
     */
    void updateView();

    /**
     * 号码错误视图操作
     */
    void numError();

    /**
     * 网络获取数据异常视图操作
     */
    void loadDataError();

}
3.视图层基类--已封装进度条显隐逻辑
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/4/25:12:52
 * 邮箱:1981462002@qq.com
 * 说明:视图层基类--已封装进度条显隐逻辑
 */
public abstract class BaseICallActivity extends AppCompatActivity implements ICallView {
    private ProgressDialog mProgressDialog;

    /**
     * 更新视图
     */
    public abstract void updateView();

    @Override
    public void showLoading() {

        if (mProgressDialog == null) {
            mProgressDialog = ProgressDialog.show(this, "",
                    "Loading...", true, false);
        } else if (mProgressDialog.isShowing()) {
            mProgressDialog.setTitle("");
            mProgressDialog.setMessage("Loading...");
        }
        mProgressDialog.show();
    }


    @Override
    public void hideLoading() {

        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
    }

    @Override
    public void onBackPressed() {
        if (mProgressDialog != null && mProgressDialog.isShowing()) {
            mProgressDialog.dismiss();
        }
        super.onBackPressed();
    }
}
4.视图层核心:CallPhoneActivity
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/4/25:12:52
 * 邮箱:1981462002@qq.com
 * 说明:视图层核心
 */
public class CallPhoneActivity extends BaseICallActivity {

    @BindView(R.id.et_phone)
    EditText mEtPhone;
    @BindView(R.id.btn_search)
    Button mBtnSearch;
    @BindView(R.id.textView)
    TextView mTextView;
    @BindView(R.id.lv_info)
    ListView mLvInfo;

    private CallPresenterImpl mCallPresenterImpl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_call);
        ButterKnife.bind(this);
        //视图层持有控制层对象
        mCallPresenterImpl = new CallPresenterImpl(this);
        mCallPresenterImpl.attach(this);
    }

    @OnClick(R.id.btn_search)
    public void onViewClicked() {
        //控制层对象执行逻辑,将视图层数据传入控制层
        mCallPresenterImpl.getDataFromInternet(mEtPhone.getText().toString());
    }

    @Override
    public void updateView() {

        Phone phone = mCallPresenterImpl.getPhone();

        ArrayList<String> infos = new ArrayList<>();
        infos.add("手机号:" + phone.getTelString());
        infos.add("运营商:" + phone.getCatName());
        infos.add("省份:" + phone.getProvince());
        infos.add("详情信息:" + phone.getCarrier());

        mLvInfo.setAdapter(new MyLVAdapter<String>(this, infos, R.layout.simple_item_tv1) {
            @Override
            public void setData(MyLVHolder holder, String data, int position) {
                holder.setText(R.id.tv_item1, data);
            }
        });

    }

    @Override
    public void numError() {
        mTextView.setError("");
        ToastUtil.show(this, "输入正确的手机号");
    }

    @Override
    public void loadDataError() {
        ToastUtil.show(this, "无网络,无缓存");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mCallPresenterImpl.onDestroy();
    }
}

网络封装
代码语言:javascript
复制
public class ZHttp {
    private String mUrl;
    private Map<String, String> mParam;

    private HttpResponse mHttpResponse;
    private OkHttpClient mHttpClient = new OkHttpClient();

    private Handler mHandler = new Handler(Looper.getMainLooper());

    public interface HttpResponse {
        void onSuccess(String result);

        void onFail(String error);
    }

    public ZHttp(HttpResponse httpResponse) {
        mHttpResponse = httpResponse;
    }

    public void sendPostHttp(String url, Map<String, String> param) {
        sendHttp(url, param, true);
    }


    public void sendGetHttp(String url, Map<String, String> param) {
        sendHttp(url, param, false);

    }

    private void sendHttp(String url, Map<String, String> param, boolean isPost) {
        mUrl = url;
        mParam = param;
        //获取网络数据
        getData(isPost);
    }

    /**
     * 获取网络数据
     *
     * @param isPost
     */
    private void getData(boolean isPost) {
        //创建request请求
        final Request request = createRequest(isPost);

        mHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (mHttpResponse != null) {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            mHttpResponse.onFail("请求错误");
                        }
                    });
                }
            }

            @Override
            public void onResponse(Call call, final Response response) throws IOException {

                if (mHttpResponse == null) {
                    return;
                }
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if (!response.isSuccessful()) {
                            mHttpResponse.onFail("请求失败:code" + response);
                        } else {
                            try {
                                mHttpResponse.onSuccess(response.body().string());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                });
            }
        });

    }

    /**
     * @param isPost
     * @return
     */
    private Request createRequest(boolean isPost) {
        Request request;

        if (isPost) {
            MultipartBody.Builder builder = new MultipartBody.Builder();
            builder.setType(MultipartBody.FORM);
            //遍历map请求参数
            Iterator<Map.Entry<String, String>> iterator = mParam.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                builder.addFormDataPart(entry.getKey(), entry.getValue());
            }
            request = new Request.Builder().url(mUrl).post(builder.build()).build();

        } else {
            String urlStr = mUrl + "?" + MapParamToString(mParam);
            request = new Request.Builder().url(urlStr).build();
        }
        return request;
    }

    /**
     * @param param
     * @return
     */
    private String MapParamToString(Map<String, String> param) {
        StringBuilder sb = new StringBuilder();
        Iterator<Map.Entry<String, String>> iterator = mParam.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            sb.append(entry.getKey() + "=" + entry.getValue() + "&");
        }

        String str = sb.toString().substring(0, sb.length() - 1);
        return str;
    }
}
网络封装,连接网络获取数据,并写缓存
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/3/29:10:21
 * 邮箱:1981462002@qq.com
 * 说明:网络封装,连接网络获取数据,并写缓存
 */
public abstract class GetDataWithCache {

    private CacheStrategy mCacheStrategy;
    private String mUrl;
    private Map<String, String> mParam;

    public GetDataWithCache(String url, Map<String, String> param, CacheStrategy cacheStrategy) {

        mCacheStrategy = cacheStrategy;
        mUrl = url;
        mParam = param;
        String cache = new CacheWorker(mCacheStrategy).getCache(mUrl);//读缓存

        if (!TextUtils.isEmpty(cache)) {//如果有缓存
            success(cache);//解析数据抽象方法
        }

        getDataFromService();//连接网络获取数据,并写缓存
    }

    /**
     * 从网络获取数据
     */
    private void getDataFromService() {

        new ZHttp(new ZHttp.HttpResponse() {
            @Override
            public void onSuccess(String response) {
                if (response != null) {
                    success(response);
                }
                mCacheStrategy.setCache(mUrl, response, 10);//写缓存
            }

            @Override
            public void onFail(String error) {
                if (mCacheStrategy.getCache(mUrl) != null) {
                    success(mCacheStrategy.getCache(mUrl));
                } else {
                    error(error);
                }
            }
        }).sendGetHttp(mUrl, mParam);
    }


    public abstract void success(String response);

    public abstract void error(String error);


}

这是我第一次接触MVP模式,如有不恰,欢迎指正


本文由张风捷特烈原创,转载请注明 更多安卓技术欢迎访问:https://www.jianshu.com/c/004f3fe34c94 张风捷特烈个人网站,编程笔记请访问:http://www.toly1994.com 你的喜欢与支持将是我最大的动力

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018.08.31 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 零、前言
    • 项目结构及类图
    • 一、M:模型层:负责数据的请求、解析、过滤等数据层操作
      • 1.模型层接口--定义模型的方法接口
        • 2.模型层接口实现类:负责数据的获取
          • 3.实体类:Phone
          • 二、控制层:View和Model沟通的桥梁
            • 1.基类BasePresenter:为了方便让控制层和Activity生命周期同步
              • 2.控制层实现类:CallPresenterImpl
              • 三、视图层 V:负责视图部分展示、视图事件处理
                • 1.显示和消失对话框接口--由于非常常见所以单独抽出
                  • 2.视图层接口
                    • 3.视图层基类--已封装进度条显隐逻辑
                      • 4.视图层核心:CallPhoneActivity
                        • 网络封装
                          • 网络封装,连接网络获取数据,并写缓存
                          领券
                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档