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

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 零、前言[1].最近在总结了安卓的知识点,现在来穿插一个小项目,算是对前面的一个小小总结[2].本案例是以前在慕课网的学习的一个免费课程,代码看不懂的可以去看看:号码归属地查询实用工具 [3].相比与视频课程,本案例按照我的理解,对项目结构进行了优化。

零、前言

[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模式,如有不恰,欢迎指正


后记、

1.声明:

[1]本文由张风捷特烈原创,转载请注明
[2]欢迎广大编程爱好者共同交流
[3]个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
[4]你的喜欢与支持将是我最大的动力

2.连接传送门:

更多安卓技术欢迎访问:安卓技术栈
我的github地址:欢迎star
简书首发,腾讯云+社区同步更新
张风捷特烈个人网站,编程笔记请访问:http://www.toly1994.com

3.联系我

QQ:1981462002
邮箱:1981462002@qq.com
微信:zdl1994328

4.欢迎关注我的微信公众号,最新精彩文章,及时送达:
公众号.jpg
相关文章
|
6月前
|
XML 前端开发 测试技术
Android基础知识:解释Android的MVC和MVP模式。
Android基础知识:解释Android的MVC和MVP模式。
66 0
|
6天前
|
前端开发 Java 测试技术
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
10 0
|
1月前
|
前端开发 Java 测试技术
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
android MVP契约类架构模式与MVVM架构模式,哪种架构模式更好?
19 2
|
6月前
|
设计模式 前端开发 Android开发
Android应用开发中的MVP架构模式解析
【5月更文挑战第25天】本文深入探讨了在Android应用开发中广泛采用的一种设计模式——Model-View-Presenter (MVP)。文章首先概述了MVP架构的基本概念和组件,接着分析了它与传统MVC模式的区别,并详细阐述了如何在实际开发中实现MVP架构。最后,通过一个具体案例,展示了MVP架构如何提高代码的可维护性和可测试性,以及它给开发者带来的其他潜在好处。
|
6月前
|
存储 前端开发 Java
Android应用开发中的MVP架构模式实践
【5月更文挑战第5天】随着移动应用开发的复杂性增加,传统的MVC(Model-View-Controller)架构在应对大型项目时显得笨重且不灵活。本文将探讨一种更适应现代Android应用开发的架构模式——MVP(Model-View-Presenter),并展示如何在Android项目中实现该模式以提升代码的可维护性和可测试性。通过对比分析MVP与传统MVC的差异,以及提供一个实际案例,读者将能深入了解MVP的优势和实施步骤。
|
6月前
|
前端开发 测试技术 数据处理
安卓开发中的MVP架构模式深度解析
【4月更文挑战第30天】在移动应用开发领域,模型-视图-呈现器(Model-View-Presenter, MVP)是一种广泛采用的架构模式。它旨在通过解耦组件间的直接交互来提高代码的可维护性和可测试性。本文将深入探讨MVP在安卓开发中的应用,揭示其如何促进代码的模块化,提升用户界面的响应性,并简化单元测试过程。我们将从理论概念出发,逐步过渡到实践案例,为读者提供一套行之有效的MVP实施策略。
|
6月前
|
XML 前端开发 测试技术
安卓架构模式:MVC、MVP、MVVM及更多
【4月更文挑战第13天】本文探讨了安卓应用开发中的常见架构模式,包括MVC、MVP和MVVM,以及VIPER和Clean Architecture。MVC分离关注点,易于理解,但安卓不直接支持。MVP通过呈现器实现更清晰的分层和便于单元测试。MVVM利用数据绑定简化UI逻辑,适合声明式编程。开发者应根据项目需求、团队技能和维护周期选择合适架构,随着工具和框架的进步,未来将提供更多模块化、可测试性和敏捷性的解决方案。
281 7
|
6月前
|
C# 索引
C# | 【完全开源】手机号码归属地查询,一秒内百万次查询
这个开源项目是一个.NET库,可以通过手机号码获取号码归属地信息,包括运营商、国家、省份、城市、邮政编码、区号等信息。 该库加载了一个包含46万条数据的“中国手机号归属地信息”数据集,并实现了高速查询。在我的7年老笔记本上执行一百万次查询耗时不足一秒。
295 0
|
XML 前端开发 JavaScript
【Android】MVC,MVP,MVVM的优缺点
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构.
386 0
【Android】MVC,MVP,MVVM的优缺点
|
存储 SQL 前端开发
借一个项目谈Android应用软件架构,你还在套用MVP 或MVVM吗
借一个项目谈Android应用软件架构,你还在套用MVP 或MVVM吗