结合MVP设计模式和解析Json数据,制作一款“手机号码归属地查询的App小程序(Android)”

说明:实现的原理很简单,有多种设计方式和代码编写风格。本文主要是认识、理解MVP设计模式和Json数据的常见解析框架的使用。

源码:请点击链接访问我的GitHub进行查看
准备工作:

  1. AndroidStudio 开发工具(谷爹的亲儿子)
  2. 浏览器(进行测试淘宝开放平台返回给我们的Json数据并进行解析)
  3. 一部真机或者模拟机(能上网的手机)

分析步骤:

1.实现界面
2.获取数据:
(1).本地数据(数据不是最新的)
(2).网络数据(数据是最新的)。【数据提供方:淘宝的开放接口。淘宝API的Web接口】
接口地址:https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=要查询的手机号码
3.解析数据
http框架:Okhttp框架
Json数据处理:Gson,FastJson,自带JsonObject
本例,笔者调用的是自己的手机号。通过解析淘宝开放平台提供的数据接口返回Json数据进行解析
https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13795950539
4.展现数据
MVP通知界面-》展示数据

Json解析框架介绍:

JSONObject:安卓自带的Json解析框架,采用对象和数组的2种方式解析.不能直接把JSon转化为java对象
Gson:Java对象和Json可以相互直接转换
FastJson:阿里提供的库,用法和Gson差不多,小巧,解析快

关于MVP和MVC的简要说明:

层次分明的代码:
低耦合:业务层-数据层-展示层。(要分离)
1.MVC模式
2.衍生MVP模式(业务和界面进行分离)
Activity充当了 MVP中的View 的“角色”,业务逻辑在Presenter进行实现
View与Presenter进行解耦:采用接口交互。(即笔者在此把View的主要操作抽象为接口,Presenter通过接口与View进行交互,所以Presenter就不必依赖于具体的View对象)

MVC:

MVP:

MVP的好处:

MVP的使用:

MVC和MVP的结构对比:

注:实战中代码的具体说明已经注释到相应代码段中,如有需要,请自行在GitHub中搜索我的库进行查看理解

编写程序:AndroidStudio 3.3

  1. 资源文件:

strings.xml:

<resources><string name="app_name">手机号码归属地查询</string><string name="query_phone">手机号码查询:</string><string name="input_phone">请输入手机号码查询</string><string name="query">查询</string><string name="result_query">查询结果:</string><string name="phone">手机号:未知</string><string name="province">省份:未知</string><string name="type">运营商:未知</string><string name="belongtype">归属运营商:未知</string>
</resources>

colors.xml:
颜色不重要,自定义即可

<?xml version="1.0" encoding="utf-8"?>
<resources><color name="colorPrimary">#008577</color><color name="colorPrimaryDark">#00574B</color><color name="colorAccent">#D81B60</color><!--自定义颜色--><!--标题颜色--><color name="title_text_color">#F44336</color><!--输入框提示文字颜色--><color name="hint_color">#F44336</color><!--按钮背景--><color name="button_backgrond_color">#54673AB7</color><!--查询结果标题文字颜色--><color name="search_title_color">#3F51B5</color><!--透明--><color name="BarColor">#00FFFFFF</color>
</resources>

styles.xml:
主要是在默认的styles文件中,去掉了ActionBar,并且底部栏改成了白色

<resources xmlns:tools="http://schemas.android.com/tools"><!-- Base application theme. --><style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"><!-- Customize your theme here. --><item name="colorPrimary">@color/colorPrimary</item><item name="colorPrimaryDark">@color/colorPrimaryDark</item><item name="colorAccent">@color/colorAccent</item><item name="android:navigationBarColor" tools:targetApi="lollipop">@color/BarColor</item></style></resources>
  1. 界面布局:
    activity_main.xml:(文末有界面图片展示,有需要的话请自行复制)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_height="match_parent"android:layout_width="match_parent"android:orientation="vertical"android:background="@drawable/background"tools:context=".MainActivity"><android.support.v7.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="?attr/actionBarSize"android:background="#673AB7"android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"app:popupTheme="@style/ThemeOverlay.AppCompat.Light"></android.support.v7.widget.Toolbar><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:textColor="@color/title_text_color"android:text="@string/query_phone"android:textSize="20sp"/><EditTextandroid:id="@+id/input_phone"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:layout_marginLeft="50dp"android:layout_marginRight="50dp"android:singleLine="true"android:maxLength="11"android:inputType="phone"android:textColor="#FF5722"android:textColorHint="@color/hint_color"android:hint="@string/input_phone"android:textSize="20sp"/><Buttonandroid:id="@+id/btn_search"android:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20sp"android:layout_marginTop="10dp"android:layout_gravity="center_horizontal"android:background="@color/button_backgrond_color"android:textColor="#ffff"android:text="@string/query"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="30dp"android:layout_gravity="center_horizontal"android:textColor="@color/hint_color"android:text="@string/result_query"android:textSize="18sp"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:layout_marginTop="10dp"android:layout_marginRight="30dp"android:background="@color/button_backgrond_color"android:orientation="vertical"><TextViewandroid:id="@+id/result_phone"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/button_backgrond_color"android:paddingLeft="40dp"android:paddingTop="15dp"android:paddingBottom="15dp"android:text="@string/phone"android:textColor="#FF5722"android:textSize="20sp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#ffff" /><TextViewandroid:id="@+id/result_province"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/button_backgrond_color"android:paddingLeft="40dp"android:paddingTop="15dp"android:paddingBottom="15dp"android:text="@string/province"android:textColor="#FF5722"android:textSize="20sp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#ffff" /><TextViewandroid:id="@+id/result_type"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/button_backgrond_color"android:paddingLeft="40dp"android:paddingTop="15dp"android:paddingBottom="15dp"android:text="@string/type"android:textColor="#FF5722"android:textSize="20sp" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:background="#ffff" /><TextViewandroid:id="@+id/result_carrier"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/button_backgrond_color"android:paddingLeft="40dp"android:paddingTop="15dp"android:paddingBottom="15dp"android:text="@string/belongtype"android:textColor="#FF5722"android:textSize="20sp" /><ProgressBarandroid:id="@+id/progress"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout>
</LinearLayout>
  1. MVP目录结构大致理解:

    1.先看Model层次:
    在笔者自己的理解中,Model无非就是一个模型,我们可以理解为一个“模特(穿没穿衣服自己想象)”。然后就是这个Class的get()、set()方法,有了这样的条件,就组成了“人”的特点。所以说,Model也就是类的实体层,用于保存实例状态(保存“模特穿衣服的状态”)。
    2.再看View层次:
    其实说白了,View层就是我们的Activity.对于MVC设计模式,Activity充当着View和一部分Controller的角色。导致Activity的代码很臃肿,且不易维护,看起来蛋疼,没有真正做到一目了然的效果。所以,MVP就是把View和Controller分离开,便于项目的维护和升级。
    3.大哥大:Presenter(中文意思:委托者)也称为业务逻辑处理实现。【说白了,复杂的东西交给它来做】
    Presenter是Model和View之间的桥梁。
    看下图:

    Model和View没有直接联系,那么他们是怎么进行交互的呢?
    答案当然是MVP的核心思想:把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成Presenter接口,Model类还是原来的Model
    这样,Activity的工作就简单了,只用来响应生命周期。
  2. MVP设计模式主要代码:
    (1).Model->Phone(实体):
package com.example.yinlei.phoneattributionquery.model;/*** Model。可理解为没有数据的模型,存储我们的手机号码* 代表手机号这个实体。* 有哪些字段需要我们通过预先对数据源接口返回的Json数据进行观察* Example:淘宝返回给我的json数据为* _GetZoneResult_={*     mts:'1379595',*     province:'四川',*     catName:'中国移动',*     telString:'13795950539',*              areaVid:'30508',*              ispVid:'3236139'*              carrier:'四川移动'* }*/
public class Phone {String telString;//手机号String province;//省份String catName;//运营商String carrier;//手机运营商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 getCatName() {return catName;}public void setCatName(String catName) {this.catName = catName;}public String getCarrier() {return carrier;}public void setCarrier(String carrier) {this.carrier = carrier;}}

上面的代码的原理笔者在上面已经阐述过了,就不再啰嗦了,请自行查看理解。

(2).View ->MainActivity:
因为MVP的Presenter和View进行交互是通过接口,所以这里实现了Presenter的impl包下的MvpMainView接口来传递数据【剩下就是接口回调的事了】
在这里简单实例化一下控件,点击监听OnClick()方法请先忽略,等下了解了Presenter后返回来处理。

package com.example.yinlei.phoneattributionquery;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;import com.example.yinlei.phoneattributionquery.model.Phone;
import com.example.yinlei.phoneattributionquery.mvp.MvpMainView;
import com.example.yinlei.phoneattributionquery.mvp.impl.MainPresenter;public class MainActivity extends AppCompatActivity implements View.OnClickListener, MvpMainView {EditText input_phone;Button btn_search;TextView result_phone;TextView result_province;TextView result_type;TextView result_carrier;MainPresenter mainPresenter;ProgressBar progressBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/**设置Toolbar*/Toolbar toolbar=findViewById(R.id.toolbar);//此处注意导入android.support.v7.widget.Toolbar。此包对应于android.support.v7.app.AppCompatActivitysetSupportActionBar(toolbar);initView();//初始化控件btn_search.setOnClickListener(this);mainPresenter=new MainPresenter(this);mainPresenter.attach(this);}/*** 初始化控件*/public void initView(){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);progressBar=findViewById(R.id.progress);}@Overridepublic void onClick(View v) {
//        Toast.makeText(this, "点击了查询按钮", Toast.LENGTH_SHORT).show();mainPresenter.searchPhoneInfo(input_phone.getText().toString());//调用Presenter中的查询手机号码业务}/**以下是MvpMainView接口的方法*/@Overridepublic void showToast(String msg) {Toast.makeText(this, msg, Toast.LENGTH_LONG).show();}@Overridepublic void updateView() {Phone phone= mainPresenter.getPhoneInfo();result_phone.setText("手机号码:"+phone.getTelString());result_province.setText("省份:"+phone.getProvince());result_type.setText("运营商:"+phone.getCatName());result_carrier.setText("归属运营商:"+phone.getCarrier());}@Overridepublic void showLoading() {if (progressBar==null) {progressBar.setVisibility(View.VISIBLE);}}@Overridepublic void hidenLoading() {if (progressBar!=null){progressBar.setVisibility(View.GONE);}}@Overrideprotected void onDestroy() {mainPresenter.onDestroy();super.onDestroy();}
}

(3).Presenter:委托者
首先,把业务逻辑抽象成接口:

这里的MvpLoadingView接口是父类接口,把常用的方法提取到了父类。

MvpLoadingView:

package com.example.yinlei.phoneattributionquery.mvp;/*** MvpMainView接口的父类* 主要是方便MvpMainView对父接口的复用*/
public interface MvpLodingView {void showLoading();void hidenLoading();
}

MvpMainView:

package com.example.yinlei.phoneattributionquery.mvp;/*** Presenter通过View的操作都是通过这个接口来进行处理业务*/
public interface MvpMainView  extends MvpLodingView{void showToast(String msg);void updateView();
}

再创建Presenter:

其中,BasePresenter只是把常用的方法写了去,比如生命周期

package com.example.yinlei.phoneattributionquery.mvp.impl;import android.content.Context;/*** 把Activity中常用的方法写在基类里面*/
public class BasePresenter {Context mContext;public void attach(Context context){mContext=context;}public void onPause(){}public void onResume(){}public void onDestroy(){mContext=null;//释放掉mContext.避免Activity在释放的时候,Presenter却还在使用Context而造成Activity释放不了引起内存泄漏}
}

注:这里的Context与MainActivity进行绑定,建立关联,避免了内存泄漏
然后来看看MainPresenter中主要做了什么事情:

package com.example.yinlei.phoneattributionquery.mvp.impl;import android.widget.Toast;import com.example.yinlei.phoneattributionquery.Until.HttpUntil;
import com.example.yinlei.phoneattributionquery.model.Phone;
import com.example.yinlei.phoneattributionquery.mvp.MvpMainView;
import com.google.gson.Gson;import org.json.JSONException;
import org.json.JSONObject;import java.util.HashMap;
import java.util.Map;/*** 处理MainActivity的逻辑* Presenter靠本类进行接口交互。*/
public class MainPresenter extends BasePresenter{
//    示例Url:https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13795950539String mUrl="https://tcc.taobao.com/cc/json/mobile_tel_segment.htm";MvpMainView mvpMainView;//保存传递过来的MvpMainView接口数据Phone mPhone;//保存下方接口解析后的Phone实例public MainPresenter(MvpMainView mainView){mvpMainView=mainView;}public Phone getPhoneInfo(){//返回解析后并保存后的Phone实例return mPhone;}/*** 查询手机号* @param phone*/public void searchPhoneInfo(String phone){if (phone.length()!=11){mvpMainView.showToast("请输入正确的手机号码!");return;}mvpMainView.showLoading();//Http请求的处理逻辑sendHttp(phone);}/*** Http请求的处理逻辑* @param phone*/private void sendHttp(String phone){//构造Map,然后作为形参传入工具类中final Map<String,String>map=new HashMap<String, String>();map.put("tel",phone);//实例化网络请求工具类HttpUntil httpUntil=new HttpUntil(new HttpUntil.HttpResponse() {//构造方法需要我们传入接口(实例接口变量)@Overridepublic void onSuccess(Object object) {//返回String json=object.toString();//返回给我们的数据:包含json数据int index=json.indexOf("{");json=json.substring(index);//上面的操作是提取数据中的json,去掉多余的数据//mPhone=parseJson(json);//自定义函数:采用安卓自带的JsonObject框架// mPhone=parseJsonWithGson(json);mPhone=parseJsonWithFastJson(json);mvpMainView.hidenLoading();mvpMainView.updateView();}@Overridepublic void onFail(String error) {//通知View显示失败信息,关闭Loading界面mvpMainView.showToast(error);//接口回调:接口变量中的showToast方法有了数据。所以实现mvpMainView接口,就可以接口回调mvpMainView.hidenLoading();}});httpUntil.sendGetHttp(mUrl,map);//调用工具类中定义的通过Get方式请求的方法}/*** 解析淘宝API返回给我们的Json数据* 方法1:通过JSONobjectn框架解析* @param json* @return Phone*/private Phone parseJson(String json){Phone phone=new Phone();try {JSONObject jsonObject=new JSONObject(json);String informationOfJson=jsonObject.getString("telString");phone.setTelString(informationOfJson);informationOfJson=jsonObject.getString("province");phone.setProvince(informationOfJson);informationOfJson=jsonObject.getString("catName");phone.setCatName(informationOfJson);informationOfJson=jsonObject.getString("carrier");phone.setCarrier(informationOfJson);} catch (JSONException e) {e.printStackTrace();}return phone;}/*** 方法2:解析Json通过GSON框架* @param json* @return Phone*/private Phone parseJsonWithGson(String json){Gson gson=new Gson();
//        String myJson=gson.toJson(json);//处理空格Phone phone=gson.fromJson(json,Phone.class);//直接将Json转化为JAVA对象return phone;}/*** 方法3:解析Json通过FastJson框架(阿里)* @param json* @return Phone*/private Phone parseJsonWithFastJson(String json){Phone phone= com.alibaba.fastjson.JSONObject.parseObject(json,Phone.class);return phone;}}

本类主要是对于数据进行处理,比如网络请求,请求后再对返回的JSon数据进行解析。注意:这里的String phone是输入框获取到的用户输入,那么可想而知,我们应该对searchPhoneInfo()方法在MainActivity中的按钮监听事件中进行调用。
对于网络请求:为了便于维护,写在了工具包Until中,在本类直接 HttpUntil httpUntil=new HttpUntil(new HttpUntil.HttpResponse() {}就可以调用网络请求:
工具类下的HttpUntil类发送网络请求:(get/post方式都写上了,实际上本例只需要Post方式请求网络)

package com.example.yinlei.phoneattributionquery.Until;import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;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;/*** 工具类:进行Http请求*/
public class HttpUntil {String mUrl;Map<String,String>mParam;HttpResponse mHttpResponse;//接口变量,保存接收到的接口数据private final OkHttpClient client=new OkHttpClient();Handler mHandler=new Handler(Looper.getMainLooper());//使用主线程(即Ui线程).用Handler处理线程之间的传递问题,即主线程与子线程之间的交互问题解决/**回调接口,拿到请求返回的结果*/public interface HttpResponse{void onSuccess(Object object);void onFail(String error);}public HttpUntil(HttpResponse response) {//构造函数:进行接口回调mHttpResponse=response;}/**Post请求方式提交表单*/public void sendPostHttp(String url, Map<String,String>param){sendHttp(url,param,true);}/**Get请求方式提交表单*/public void sendGetHttp(String url,Map<String,String>param){sendHttp(url,param,false);}/**通过上面的2个方法中任选一个方法,传入定义方法的参数值isPost*/private void sendHttp(String url,Map<String,String>param,boolean isPost){//Post请求方式mUrl=url;mParam=param;//编写Http请求逻辑request(isPost);//自定义请求方法}/*** 自定义请求方法:使用Okhttp网络请求框架**/private void request(boolean isPost){//request请求创建Request request=createRequest(isPost);//构建OkHttp中的Request实例//创建请求队列Callclient.newCall(request).enqueue(new Callback() {//OkHttp中的匿名回调类(即接口回调)@Overridepublic void onFailure(Call call, IOException e) {if (mHttpResponse!=null){//回调是否有赋值,有回调,就把回调结果返回给接口HttpResponse//考虑一个问题:Call接口回调是运行在子线程中,如果直接调用回调方法,在UI线程中使用就不方便。所以在UI线程中去调用回调接口,需要全局的Handler进行处理mHandler.post(new Runnable() {@Overridepublic void run() {mHttpResponse.onFail("请求错误!");}});}}@Overridepublic void onResponse(Call call, final Response response) throws IOException {if (mHttpResponse==null)return;//HttpResponse的接口有被调用mHandler.post(new Runnable() {@Overridepublic void run() {if (!response.isSuccessful()){//请求失败mHttpResponse.onFail("请求失败:"+response);}else {//请求成功try {mHttpResponse.onSuccess(response.body().string());} catch (IOException e) {e.printStackTrace();mHttpResponse.onFail("结果转换失败!");}}}});}});}/***自定义函数:封装了一下Okhttp中,构造一个Request对象实例* @param isPost* @return*/private Request createRequest(boolean isPost){Request request;if (isPost){//是Post请求:需要提交请求参数//构建表单MultipartBody.Builder requestBodyBuilder=new MultipartBody.Builder();requestBodyBuilder.setType(MultipartBody.FORM);//设置类型//遍历Map请求参数:迭代器IteratorIterator<Map.Entry<String,String>> iterator=mParam.entrySet().iterator();while (iterator.hasNext()){Map.Entry<String,String>entry=iterator.next();requestBodyBuilder.addFormDataPart(entry.getKey(),entry.getValue());}request=new Request.Builder().url(mUrl).post(requestBodyBuilder.build()).build();//构造Okhttp3的请求Request实例【Post方式】}else {//是Get请求:地址和参数进行拼接String urlStr=mUrl+"?"+MapParamToString(mParam);request=new Request.Builder().url(urlStr).build();//构造Okhttp3的请求Request实例【Get方式】}return request;//返回经过逻辑处理完成的Request对象}/*** 自定义方法:Map中的值按照url的格式进行拼接并返回它,然后再和mUrl进行完整的拼接* @param param* @return*/private String MapParamToString(Map<String,String>param){StringBuilder stringBuilder=new StringBuilder();//字符串构造器/**遍历Map*/Iterator<Map.Entry<String,String>>iterator=param.entrySet().iterator();while (iterator.hasNext()){Map.Entry<String,String>entry=iterator.next();//把Map参数按http的Get请求方式进行拼接stringBuilder.append(entry.getKey()+"="+entry.getValue()+"&");}//求掉最后一个"&"符号String str=stringBuilder.toString().substring(0,stringBuilder.length()-1);//subString()方法是提取一段String【java的String基础和数据结构中的串的知识点】return str;}}

说明:本类方法还定义了一个接口,该类的内部定义的接口HttpResponse用于保存Okhttp回调时候返回的信息和 返回的Json数据。然后在MainPresenter中进行调用工具类的构造方法的时候就通过匿名实现接口HttpResponse中的方法。此时,实现的方法的参数是有数据的,且是工具类中接口已被赋有数据。即MainPresenter实现HttpResponse接口的方法,它的形参是已经被赋值后的。(理解接口回调)

逻辑就这样了,最后关于MainPresenter主要的业务是对Json数据进行解析,并转化成Phone(Model层)实例来保存解析后的带有数据的Model.(即现在是穿了衣服的模特啦!然后在View里面对界面进行展示数据通过Model的get和set方法)。

结果展示:

手机号码归属地查询App相关推荐

  1. Python实现手机号码归属地查询功能

    文章目录 一.使用场景 二.问题描述 三.解决方案 四.方法补充 方法一: 方法二 一.使用场景 对手机号码进行地域分析,需要查询归属地: 二.问题描述 针对数据集比较大的情况,通过脚本来处理,使用多 ...

  2. 14.4 手机号码归属地查询

    手机号码归属地查询 MobileAddressQuery Android通过调用Webservice实现手机号码归属地查询 注:http://webservice.webxml.com.cn/WebS ...

  3. 手机号码归属地查询 - 一刀工具

    手机号码归属地快速查询移动,联通,电信手机号码归属地,免费手机号码运营商查询,手机归属地查询大全. 代码片段 //获取手机信息static function getMobile($mobile){tr ...

  4. Android 简单几步实现手机号码归属地查询,可监听文本框的变化自动查询

    2019独角兽企业重金招聘Python工程师标准>>> 手机号码归属地查询需要用到一个数据库文件,我们可以用小米公司的数据库文件 第一步:数据库文件存储在 data/data/包名/ ...

  5. 手机号码归属地查询JS跨域

    手机号码归属地查询JS跨域 效果图 代码 css代码 body {font-family: 'Microsoft YaHei'; }.wrap {position: fixed;left: 0;top ...

  6. 调用聚合数据API实现手机号码归属地查询

    调用聚合数据API实现手机号码归属地查询 1.作者介绍 2.相关介绍 2.1 什么是聚合数据? 2.2 API介绍 2.3 手机号码归属地 3.实验过程 3.1如何调用聚合数据API 3.2代码实现 ...

  7. python手机版代码-Python手机号码归属地查询代码

    简单的一个例子,是以前用Dephi写的,前不久刚实现了一个在Python中使用Delphi控件来编写界面程序,于是趁热写一个类似的的查询方案. 本实例是通过www.ip138.com这个网站来查询的, ...

  8. java手机号归属地查询_【原创】Java实现手机号码归属地查询

    网络上已经有很多的手机号码归属地查询的API接口,但是这些接口总是有一些大大小小的缺陷. 总结一下这些缺陷: 1.要直接将它的搜索框链接形式粘到自己的页面,点击查询的时候还要跳转到他们的网站来展示归属 ...

  9. java 手机号码归属地_【原创】Java实现手机号码归属地查询

    网络上已经有很多的手机号码归属地查询的API接口,但是这些接口总是有一些大大小小的缺陷. 总结一下这些缺陷: 1.要直接将它的搜索框链接形式粘到自己的页面,点击查询的时候还要跳转到他们的网站来展示归属 ...

  10. 【原创】Java实现手机号码归属地查询

    网络上已经有很多的手机号码归属地查询的API接口,但是这些接口总是有一些大大小小的缺陷. 总结一下这些缺陷: 1.要直接将它的搜索框链接形式粘到自己的页面,点击查询的时候还要跳转到他们的网站来展示归属 ...

最新文章

  1. 干货|一文看懂美国共享出行3大领域8大头部玩家
  2. 打包本地文件, 并使用Winscp上传脚本
  3. 学习《Hardware-Efficient Bilateral Filtering for Stereo Matching》一文笔记。
  4. python的可变对象和不可变对象
  5. UML 类图几种关系的总结
  6. Spring--Resource
  7. JEECG开源团队招募成员计划
  8. 程序员浮躁,谁之过错?
  9. 悉尼一船只引擎爆炸男子被迫跳水:严重烧伤被送医
  10. java解析xml 字符串_Java解析XML字符串
  11. (Incomplete) Codeforces #395 (Div 1 + Div 2)
  12. python多线程详解_Python多线程详解
  13. 【数据结构基础复习】二叉树的非递归遍历(二)
  14. java 定时任务 cron
  15. CH582 BLE 5.0 使用 LE Coded 广播和连接
  16. 机器学习?有无监督、弱监督、半监督、强化、多示例学习是什么
  17. ES查询-空字段和非空字段结果
  18. Skyscrapers Aren’t Scalable
  19. 虚拟服务器443端口开启,服务器开启443端口
  20. 在华为之后,再有中国芯片赶超美国芯片,美国的图谋将再次失败

热门文章

  1. 做java前端需要学习哪些知识,2022最新
  2. linux系统安装gcc依赖包,linux安装gcc需要的依赖包
  3. C语言谭浩强编程错误归纳
  4. sqldeveloper 工具的使用——连接数据库的介绍
  5. 【工业机器人】全球工业机器人详细产业链梳理!
  6. 凸包算法 Matlab实现
  7. java代码禁止反编译_Java代码防止反编译
  8. python各种库下载地址:
  9. 吴恩达机器学习作业 线性回归
  10. 超级简单C语言进制转换代码