demo中遇到的问题直接跳转见这里:问题总结


学习目标:实现归属地查询,通过OkHttp网络框架请求手机号数据,并能掌握流行的MVP设计模式以及如何使用目前主流的Json和Gson解析框架。

难度点:在gson解析时,遇到了key会动态变化的json数据,如何处理花费了很长时间。

利用淘宝接口只能显示到省份,不能具体到城市,json数据简单:

淘宝接口:

https://tcc.taobao.com/cc/json/mobile_tel_segment.htm

利用淘宝接口的demo:https://github.com/liuchenyang0515/SearchPhone

利用百度接口可以显示到具体的城市,就是json数据嵌套多

百度接口:

http://mobsec-dianhua.baidu.com/dianhua_api/open/location

利用百度接口的demo:https://github.com/liuchenyang0515/SearchPhone1

有人经过多方接口对比,据说百度接口更加准确,我们就采用百度接口。

在以上URL后面加上手机号即可,比如?tel=159xxxxxxxx

自己新建一个project,关掉android studio,再把app目录替换到自己的app目录,再打开即可。

mvp设计思路:

关于mvc和mvp更加详细的讲解:http://kaedea.com/2015/10/11/android-mvp-pattern/

接下来以百度接口讲解:

返回的json数据如下:

大致的demo目录如下:

activity充当view,presenter处理业务逻辑,p和v采用接口交互,大致上算一个mvp框架模式。

运行结果如图:

代码如下:(或者更直观的去看demo)

MainActivity.java

package com.example.searchphone;import android.app.ProgressDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;import com.example.searchphone.model.GsonParsePhone;
import com.example.searchphone.model.JsonparsePhone;
import com.example.searchphone.mvp.MvpMainView;
import com.example.searchphone.mvp.impl.MainPresenter;public class MainActivity extends AppCompatActivity implements MvpMainView {EditText input_phone;Button btn_search;TextView result_phone;TextView result_province;TextView result_type;TextView result_carrier;MainPresenter mainPresenter;ProgressDialog progressDialog;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);input_phone = findViewById(R.id.input_phone);btn_search = findViewById(R.id.btn_search);result_phone = findViewById(R.id.result_phone);result_province = findViewById(R.id.result_province);result_type = findViewById(R.id.result_type);result_carrier = findViewById(R.id.result_carrier);btn_search.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mainPresenter.searchPhoneInfo(input_phone.getText().toString());}});mainPresenter = new MainPresenter(this);//mainPresenter.attach(this);}// mvpMainView接口的方法@Overridepublic void showToast(String msg) {Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();}/*    // 用JSONObject解析时用这个@Overridepublic void updateView() {JsonparsePhone phone = mainPresenter.getPhoneInfo();result_phone.setText("手机号:" + phone.getTelString());result_province.setText("省份:" + phone.getProvince());result_type.setText("城市:" + phone.getCity());result_carrier.setText("运营商:" + phone.getOperator());}*/// 用Gson解析时用这个@Overridepublic void updateView() {GsonParsePhone phone = mainPresenter.getPhoneInfo1();result_phone.setText("手机号:" + input_phone.getText().toString()); // 手机号码直接在EditView获取就行result_province.setText("省份:" + phone.getResponse().getPhoneNumber().getDetail().getProvince());result_type.setText("城市:" + phone.getResponse().getPhoneNumber().getDetail().getArea().get(0).getCity());result_carrier.setText("运营商:" + phone.getResponse().getPhoneNumber().getDetail().getOperator());}@Overridepublic void showLoading() {if (progressDialog == null) {progressDialog = ProgressDialog.show(this, "", "正在加载...", true, false);} else if (progressDialog.isShowing()) {progressDialog.setTitle("");progressDialog.setMessage("正在加载...");}progressDialog.show();}@Overridepublic void hidenLoading() {if (progressDialog != null && progressDialog.isShowing()){progressDialog.dismiss();}}
}

MvpMainView.java

package com.example.searchphone.mvp;public interface MvpMainView extends MvpLoadingView{void showToast(String msg);void updateView();
}

MvpLoadingView.java

package com.example.searchphone.mvp;public interface MvpLoadingView {void showLoading();void hidenLoading();
}

MainPresenter.java

package com.example.searchphone.mvp.impl;import android.util.Log;import com.example.searchphone.business.HttpUtils;
import com.example.searchphone.model.JsonparsePhone;
import com.example.searchphone.model.GsonParsePhone;
import com.example.searchphone.mvp.MvpMainView;
import com.google.gson.Gson;import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;import java.util.HashMap;
import java.util.Map;public class MainPresenter extends BasePresenter {//String mUrl = "https://tcc.taobao.com/cc/json/mobile_tel_segment.htm";String mUrl = "http://mobsec-dianhua.baidu.com/dianhua_api/open/location";MvpMainView mvpMainView;JsonparsePhone mPhone;GsonParsePhone mPhone1;String phoneNum;private static final String TAG = "MainPresenter";public JsonparsePhone getPhoneInfo() {return mPhone;}public GsonParsePhone getPhoneInfo1() {return mPhone1;}public MainPresenter(MvpMainView mainView) {mvpMainView = mainView;}public void searchPhoneInfo(String phone) {if (phone.length() != 11) {mvpMainView.showToast("请输入正确的手机号");return;}phoneNum = phone;mvpMainView.showLoading();// 写上http请求处理逻辑sendHttp(phone);}private void sendHttp(String phone) {Map<String, String> map = new HashMap<String, String>();map.put("tel", phone);HttpUtils httpUtils = new HttpUtils(new HttpUtils.HttpResponse() {@Overridepublic void onSuccess(Object object) {String json = object.toString();// 使用Gson时需要下面2句,使用JSONObject时注释下面2句StringBuilder str = new StringBuilder(json);json = str.substring(0, 14) + "phoneNumber" + str.substring(25);// 使用JSONObject//mPhone = parseModelWithOrgJson(json);// GsonmPhone1 = parseModelWithGson(json);// FastJson// mPhone = parseModelWithFastJson(json);mvpMainView.hidenLoading();mvpMainView.updateView();}@Overridepublic void onFail(String error) {mvpMainView.showToast(error);mvpMainView.hidenLoading();}});httpUtils.sendGetHttp(mUrl, map);}// 手动练习json解析,根据自己选择需要来修改,这里写的是未修改json的手机号// 还是动态手机号解析private JsonparsePhone parseModelWithOrgJson(String json) {JsonparsePhone phone = new JsonparsePhone();Log.d(TAG, "=====================" + json);try {JSONObject jsonObject = new JSONObject(json);JSONObject response = jsonObject.getJSONObject("response");// Log.d(TAG, "response=====================" + response);JSONObject num = response.getJSONObject(phoneNum);// 外面传进来的手机号,json可以一步步处理动态keyphone.setTelString(phoneNum);// Log.d(TAG, "num======================" + num);JSONObject detail = num.getJSONObject("detail");String province = detail.getString("province");phone.setProvince(province);String operator = detail.getString("operator");phone.setOperator(operator);JSONArray areaArr = detail.getJSONArray("area");JSONObject obj = areaArr.getJSONObject(0);String city = obj.getString("city");phone.setCity(city);} catch (JSONException e) {e.printStackTrace();}return phone;}private GsonParsePhone parseModelWithGson(String json) {Gson gson = new Gson();GsonParsePhone phone = gson.fromJson(json, GsonParsePhone.class);return phone;}private JsonparsePhone parseModelWithFastJson(String json) {JsonparsePhone phone = com.alibaba.fastjson.JSONObject.parseObject(json, JsonparsePhone.class);return phone;}
}

BasePresenter.java

package com.example.searchphone.mvp.impl;import android.content.Context;public class BasePresenter {Context mContext;public void attach(Context context) {mContext = context;}public void onPause() {}public void onResume() {}public void Destroy() {mContext = null;}
}

JsonparsePhone.java

package com.example.searchphone.model;public class JsonparsePhone {String telString;String province;String city;String operator;public String getTelString() {return telString;}public void setTelString(String telString) {this.telString = telString;}public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getCity() {return city;}public void setCity(String city) {this.city = city;}public String getOperator() {return operator;}public void setOperator(String operator) {this.operator = operator;}
}

GsonparsePhone.java

package com.example.searchphone.model;import java.util.List;public class GsonParsePhone {/*** response : {"phoneNumber":{"detail":{"area":[{"city":"武汉"}],"province":"湖北","type":"domestic","operator":"移动"},"location":"湖北武汉移动"}}* responseHeader : {"status":200,"time":1533717512727,"version":"1.1.0"}*/private ResponseBean response;private ResponseHeaderBean responseHeader;public ResponseBean getResponse() {return response;}public void setResponse(ResponseBean response) {this.response = response;}public ResponseHeaderBean getResponseHeader() {return responseHeader;}public void setResponseHeader(ResponseHeaderBean responseHeader) {this.responseHeader = responseHeader;}public static class ResponseBean {/*** phoneNumber : {"detail":{"area":[{"city":"武汉"}],"province":"湖北","type":"domestic","operator":"移动"},"location":"湖北武汉移动"}*/private PhoneNumberBean phoneNumber;public PhoneNumberBean getPhoneNumber() {return phoneNumber;}public void setPhoneNumber(PhoneNumberBean phoneNumber) {this.phoneNumber = phoneNumber;}public static class PhoneNumberBean {/*** detail : {"area":[{"city":"武汉"}],"province":"湖北","type":"domestic","operator":"移动"}* location : 湖北武汉移动*/private DetailBean detail;private String location;public DetailBean getDetail() {return detail;}public void setDetail(DetailBean detail) {this.detail = detail;}public String getLocation() {return location;}public void setLocation(String location) {this.location = location;}public static class DetailBean {/*** area : [{"city":"武汉"}]* province : 湖北* type : domestic* operator : 移动*/private String province;private String type;private String operator;private List<AreaBean> area;public String getProvince() {return province;}public void setProvince(String province) {this.province = province;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getOperator() {return operator;}public void setOperator(String operator) {this.operator = operator;}public List<AreaBean> getArea() {return area;}public void setArea(List<AreaBean> area) {this.area = area;}public static class AreaBean {/*** city : 武汉*/private String city;public String getCity() {return city;}public void setCity(String city) {this.city = city;}}}}}public static class ResponseHeaderBean {/*** status : 200* time : 1533717512727* version : 1.1.0*/private int status;private long time;private String version;public int getStatus() {return status;}public void setStatus(int status) {this.status = status;}public long getTime() {return time;}public void setTime(long time) {this.time = time;}public String getVersion() {return version;}public void setVersion(String version) {this.version = version;}}
}

HttpUtils.java

package com.example.searchphone.business;import android.os.Handler;
import android.os.Looper;
import android.util.Log;import java.io.IOException;
import java.util.Iterator;
import java.util.Map;import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;public class HttpUtils {private static final String TAG = "HttpUtils";String mUrl;Map<String, String> mParam;HttpResponse mHttpResponse;Handler myhandler = new Handler(Looper.getMainLooper());private final OkHttpClient client = new OkHttpClient();public interface HttpResponse {void onSuccess(Object object);void onFail(String error);}public HttpUtils(HttpResponse response) {mHttpResponse = response;}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;// 编写http请求逻辑run(isPost);}private void run(boolean isPost) {// request请求创建final Request request = createRequest(isPost);client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {if (mHttpResponse != null) {myhandler.post(new Runnable() {@Overridepublic void run() {mHttpResponse.onFail("请求错误");}});}}@Overridepublic void onResponse(Call call, final Response response) {if (mHttpResponse == null) return;myhandler.post(new Runnable() {@Overridepublic void run() {if (!response.isSuccessful()) {mHttpResponse.onFail("请求失败:code" + response);} else {try {String s = response.body().string(); // response.body().string();使用一次流就关闭了mHttpResponse.onSuccess(s);} catch (IOException e) {e.printStackTrace();mHttpResponse.onFail("结果转换失败");}}}});}});}private Request createRequest(boolean isPost) {Request request;if (isPost) {MultipartBody.Builder requestBodyBuilder = new MultipartBody.Builder();requestBodyBuilder.setType(MultipartBody.FORM);Iterator<Map.Entry<String, String>> iterator = mParam.entrySet().iterator();// 遍历map请求参数while (iterator.hasNext()) {Map.Entry<String, String> entry = iterator.next();requestBodyBuilder.addFormDataPart(entry.getKey(), entry.getValue());}request = new okhttp3.Request.Builder().url(mUrl).post(requestBodyBuilder.build()).build();} else {String urlStr = mUrl + "?" + MapParamToString(mParam);request = new Request.Builder().url(urlStr).build();}return request;}private String MapParamToString(Map<String, String> param) {StringBuilder stringBuilder = new StringBuilder();Iterator<Map.Entry<String, String>> iterator = param.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<String, String> entry = iterator.next();stringBuilder.append(entry.getKey() + "=" + entry.getValue() + "&");}String str = stringBuilder.toString();return str;}
}

问题总结:

写的过程中遇到了一些问题,比如想让gson直接转换这种多层嵌套并且key会动态变化的json数据成java bean,开始想利用gsonformat插件生成java bean,但是还是有问题,像用gson解析key(手机号)是动态变化的value,试了半天做不到,想利用反射修改static final String a = "phoneNum"...@SerializedName(a) 注释的值,因为这个注释可以强制修改字段的值,能将每次输入的手机号转换成相同的key去解析,可惜失败了,虽然强制修改了static final修饰的a的值,再把这个值的引用传给@SerializedName(a) 做参数,很遗憾,编译器已经把这里的参数优化成了常量,比如static final a = "qqq";那么编译器已经优化成了@SerializedName("qqq"),哪怕强制修改了a为其他值,这个这个注释了值还是没变,达不到想要的效果,但是阻止编译器优化之后,又会提醒这个属性值必须是常量。所以这种方法办不到。以下是我参考的网址,虽然失败了,但还是学到了新东西,因为我把这些知识都试了一次,知识点熟悉了一遍,但是这些知识在这个问题上行不通。

所以真的没办法了吗?其实是有的,在传回json数据的时候,也没规定我们不能修改啊,只要显示给别人看的时候是正确的就行了,自己修改自己解析是没有问题的。

修改如下:

// 使用Gson时需要下面2句,使用JSONObject时注释下面2句

StringBuilder str = new StringBuilder(json);

json = str.substring(0, 14) + "phoneNumber" + str.substring(25);

因为json数据是这样的:

{"response":{"12345678910":{"detail":{"area":[{"city":"武汉"}],"province":"湖北","type":"domestic","operator":"移动"},"location":"湖北武汉移动"}},"responseHeader":{"status":200,"time":1533717512727,"version":"1.1.0"}}

修改后是这样的:

{"response":{"phoneNumber":{"detail":{"area":[{"city":"武汉"}],"province":"湖北","type":"domestic","operator":"移动"},"location":"湖北武汉移动"}},"responseHeader":{"status":200,"time":1533717512727,"version":"1.1.0"}}

只要将手机号码这个部分替换成其他字符串即可,就不用去硬生生的解决key会动态变化的json数据。然后利用gsonformat生成java bean,最后直接利用gson解析即可。

如何下载gsonformat:

最后创建一个jave bean类,右键generate->GsonFormat,勾上就行

操作的参考网址:https://www.cnblogs.com/bdsdkrb/p/7204912.html

在遇到的问题中,有过这样的学习经历,如下网址:

如何阻止编译器优化static final修饰的值为常量,内容参见:https://www.cnblogs.com/damonhuang/p/5421563.html

Java反射-修改字段值, 反射修改static final修饰的字段:http://www.cnblogs.com/noKing/p/9038234.html

论 f.getModifiers() & Modifier.FINAL &Modifier.STATIC 如何成立:https://blog.csdn.net/vi__iv/article/details/47294243

Java反射之如何判断类或变量、方法的修饰符(Modifier解析):https://blog.csdn.net/xiao__gui/article/details/8141216

===============================Talk is cheap, show me the code=============================

android学习笔记----手机号码查询归属地相关推荐

  1. Android学习笔记36:使用SQLite方式存储数据

    在Android中一共提供了5种数据存储方式,分别为: (1)Files:通过FileInputStream和FileOutputStream对文件进行操作.具体使用方法可以参阅博文<Andro ...

  2. Pro Android学习笔记(三三):Menu(4):Alternative菜单

    什么是Alternative menu(替代菜单) 举个例子,Activity显示一个文本文件.如果用户想对文本文件进行编辑,Activity不提供编辑能力,但可由其他activity或者其他应用提供 ...

  3. Android学习笔记:Android基础知识点(不断更新中)

    1.Android学习笔记:OkHttp 2.Android学习笔记:更新UI的方法(UI线程和非UI线程) 3.Android学习笔记:Volley 4.Android学习笔记:Handler 5. ...

  4. Android学习笔记 93. Room、LiveData 和 ViewModel

    Android学习笔记 Android 开发者基础知识 (Java) -- Google Developers 培训团队 文章目录 Android学习笔记 Android 开发者基础知识 (Java) ...

  5. 大数据HiveSQL学习笔记三-查询基础语法以及常用函数

    大数据HiveSQL学习笔记三-查询基础语法以及常用函数 一.基础语法 1.SELECT -列名- FROM -表名- WHERE -筛选条件- 如:需要根据城市,性别找出匹配的10个用户 user_ ...

  6. Android学习笔记21:ImageView获取网络图片

    Android平台有3种网络接口可以使用,它们分别是:java.net.*(标准java接口).org.apache(Apache接口)和android.net.*(Android网络接口).本文将使 ...

  7. Android学习笔记(七):多个Activity和Intent

    根据www.mars-droid.com:Andriod开发视频教学,先跳过书本<Beginning Android 2>的几个章,我是这两个资源一起看,需要进行一下同步.先初步了解一下应 ...

  8. Android学习笔记26:图片切换控件ImageSwitcher的使用

    在Windows操作系统中,要查看多张图片,可以通过使用"Windows照片查看器"在"上一张"和"下一张"之间切换,进行多张图片的浏览. ...

  9. Pro Android学习笔记(二九):用户界面和控制(17):include和merge

    xml控件代码重用:include 如果我们定义一个控件,需要在不同的layout中重复使用,或者在同一个layout中重复使用,可以采用include的方式.例如定义my_button.xml如下 ...

  10. Android学习笔记:TabHost 和 FragmentTabHost

    2019独角兽企业重金招聘Python工程师标准>>> Android学习笔记:TabHost 和 FragmentTabHostTabHost命名空间:android.widget ...

最新文章

  1. DCGAN论文笔记+源码解析
  2. 用shell获取mysql主从状态_shell监控MySQL主从状态脚本两则
  3. 关于 SAP Spartacus UI 框架选型问题
  4. Linux内存管理:ARM64体系结构与编程之cache(1)
  5. Java自学方法和路线,我万字推荐你这样学
  6. 清华谭浩强编著的c语言程序设计教程,清华大学谭浩强C语言程序设计教程第3版 (9).doc...
  7. android 大众点评,Android 大众点评的接入
  8. java 图片 文字_java实现 给图片加上文字
  9. 不同尺寸证件照混合打印
  10. 修复鹏城开发者云硬盘扩容报错 fdisk: cannot write disk label: Invalid argument
  11. cesium entity使用
  12. 如何在WPS中打开多个窗口
  13. 最优化理论笔记及期末复习(《数值最优化》——高立)
  14. 缓存、缓存算法和缓存框架简介
  15. 微信小程序如何获取云存储中指定文件夹下所有图片
  16. 20172328--蓝墨云班课实验--哈夫曼树的编码
  17. neo4j desktop下载及安装详解
  18. c语言奇遇之初见函数
  19. 算法训练营 重编码_您可能不需要$ 15K的编码训练营
  20. 2020中国工业软件企业排行2020中国智能制造企业排行

热门文章

  1. Linux 脚本修改ps1,Linux使环境变量PS1的修改永久生效——修改配置文件/etc/profile...
  2. 用防火墙自动拦截攻击IP
  3. sock 中的send和recv
  4. html5小米手机调用摄像头,html5摄像头 如何调用手机摄像头
  5. Pixhawk之启动代码和入口函数(.mk、rcS、__start、hrt)
  6. Flash Magic使用
  7. 灰度图像放大_matlab
  8. 世界黑客编程大赛第一名的作品
  9. 常用的oracle时间计算
  10. Geforce 错误代码 ERROR CODE:0x0003问题方法