专栏首页Android知识点总结P1-Android基于MVP实现号码归属地查询

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

零、前言

[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 你的喜欢与支持将是我最大的动力

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android点将台:你敢摸我猫 [- IPC -]

    张风捷特烈
  • 锻造正则神兵之Java源码分析器-V0.01

    张风捷特烈
  • Android基于TCP的五子棋双人对战实现

    张风捷特烈
  • Android全能开源项目xUtils3开发教程、简单封装

    用户2235302
  • Android全能开源项目xUtils3开发教程、简单封装

    码农笔录
  • 再忆RxJava---线程切换

    RxJava已经陆陆续续看了将近两年多了,觉得很有必要重新认识一下。而且最新github上都出了3.0了。重新看一下,一来作为回顾,二来也算是学习一下3.0 ...

    提莫队长
  • tomcat源码解读三(1) tomcat的jmx管理

        JMX即Java 管理扩展(Java Management Extensions,JMX)用来管理检测 Java 程序(同时 JMX 也在 J2EE 1...

    cfs
  • Android利用Andserver搭建服务器的详细教程

      在手机上有一个功能,但是需要使用电脑控制什么时候开始这个功能,然后上网搜索,找到了AndServer可以满足要求,我只是实现了简单的功能,如果感兴趣的话,可...

    砸漏
  • FreeMarker_模板引擎_代码自动生成器_源码下载

    你可以到freemarker的官网上去,那里有很详细的介绍:http://freemarker.org/

    Hongten
  • 微信公众号发送模板消息 Java实现。

    本博文是测试公众号调用模板接口测试。请不要完全复制我的代码。里面的测试代码中有本人测试号的微信模板id。麻烦替换成自己的可以吗? 第一步:创建模板信息 ? ? ...

    小帅丶

扫码关注云+社区

领取腾讯云代金券