美文网首页Android开发Android开发Java 杂谈
P1-Android基于MVP实现号码归属地查询

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

作者: e4e52c116681 | 来源:发表于2018-08-31 16:10 被阅读9次

    零、前言

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

    项目结构及类图
    号码归属地查询MVP类图.png 号码归属地查询.gif 手机归属地查询缓存.png

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

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

    1.模型层接口--定义模型的方法接口
    /**
     * 作者:张风捷特烈<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.模型层接口实现类:负责数据的获取
    /**
     * 作者:张风捷特烈<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
    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生命周期同步
    /**
     * 作者:张风捷特烈
     * 时间: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
    /**
     * 作者:张风捷特烈
     * 时间: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.显示和消失对话框接口--由于非常常见所以单独抽出
    /**
     * 作者:张风捷特烈
     * 时间:2018/4/25:9:37
     * 邮箱:1981462002@qq.com
     * 说明:显示和消失对话框接口--由于非常常见所以单独抽出
     */
    
    public interface ILoadingView {
        /**
         * 显示进度条
         */
        void showLoading();
    
        /**
         * 隐藏进度条
         */
        void hideLoading();
    
    }
    
    2.视图层接口
    /**
     * 作者:张风捷特烈
     * 时间:2018/4/25:9:31
     * 邮箱:1981462002@qq.com
     * 说明:视图层接口
     */
    public interface ICallView extends ILoadingView {
        /**
         * 更新视图
         */
        void updateView();
    
        /**
         * 号码错误视图操作
         */
        void numError();
    
        /**
         * 网络获取数据异常视图操作
         */
        void loadDataError();
    
    }
    
    3.视图层基类--已封装进度条显隐逻辑
    /**
     * 作者:张风捷特烈
     * 时间: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
    /**
     * 作者:张风捷特烈
     * 时间: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();
        }
    }
    

    网络封装
    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;
        }
    }
    
    网络封装,连接网络获取数据,并写缓存
    /**
     * 作者:张风捷特烈
     * 时间: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
    你的喜欢与支持将是我最大的动力

    相关文章

      网友评论

        本文标题:P1-Android基于MVP实现号码归属地查询

        本文链接:https://www.haomeiwen.com/subject/satewftx.html