android学习笔记----手机号码查询归属地

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;

    @Override
    protected 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() {
            @Override
            public void onClick(View v) {
                mainPresenter.searchPhoneInfo(input_phone.getText().toString());
            }
        });
        mainPresenter = new MainPresenter(this);
        //mainPresenter.attach(this);
    }

    // mvpMainView接口的方法
    @Override
    public void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }

/*    // 用JSONObject解析时用这个
   @Override
    public 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解析时用这个
    @Override
    public 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());
    }

    @Override
    public void showLoading() {
        if (progressDialog == null) {
            progressDialog = ProgressDialog.show(this, "", "正在加载...", true, false);
        } else if (progressDialog.isShowing()) {
            progressDialog.setTitle("");
            progressDialog.setMessage("正在加载...");
        }
        progressDialog.show();
    }

    @Override
    public 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() {
            @Override
            public 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);
                // Gson
                mPhone1 = parseModelWithGson(json);
                // FastJson
                // mPhone = parseModelWithFastJson(json);
                mvpMainView.hidenLoading();
                mvpMainView.updateView();
            }

            @Override
            public 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可以一步步处理动态key
            phone.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() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (mHttpResponse != null) {
                    myhandler.post(new Runnable() {
                        @Override
                        public void run() {
                            mHttpResponse.onFail("请求错误");
                        }
                    });
                }
            }

            @Override
            public void onResponse(Call call, final Response response) {
                if (mHttpResponse == null) return;
                myhandler.post(new Runnable() {
                    @Override
                    public 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=============================

猜你喜欢

转载自blog.csdn.net/qq_34115899/article/details/81461218