Android MVP 实战——环境云 API + Android 实现天气预报查询

2,375 阅读8分钟
原文链接: blog.csdn.net

前言

最近在学习Android高级开发,除了加强UI、四大组件等的使用技巧外,更多的是学习android项目结构的设计,例如常见的MVCMVP以及最近才了解到的MVVM等。本次项目就是在采用MVP结构,同时结合环境云实现查询全国主要城市的天气预报信息,也是非常简单、实用的一个小Demo。
这里写图片描述

环境云简介:环境云环境大数据服务平台通过获取权威数据源(中国气象网、中央气象台、国家环保部数据中心、美国全球地震信息中心等等)所发布的各类环境数据,以及云创自主布建的各类全国性环境监控传感器网络(包括PM2.5,各类空气质量指标,土壤环境质量指标检测网络)所采集的数据,并结合相关数据预测模型生成的预报数据,依托数据托管服务平台万物云(www.wanwuyun.com)所提供的基础存储服务,推出一系列功能丰富的、便捷易用的基于RESTful架构的综合环境数据调用接口。配合代码示例和详尽的接口使用说明,向各种应用的开发者免费提供可靠丰富的气象、环境、灾害以及地理数据服务。。

MVP简介

相信大家对MVC都是比较熟悉了:M-Model-模型、V-View-视图、C-Controller-控制器,MVP作为MVC的演化版本,也是作为用户界面(用户层)的实现模式,那么类似的MVP所对应的意义:M-Model-模型、V-View-视图、P-Presenter-表示器。从MVC和MVP两者结合来看,Controlller/Presenter在MVC/MVP中都起着逻辑控制处理的角色,起着控制各业务流程的作用。而MVP与MVC最不同的一点是M与V是不直接关联的也是就Model与View不存在直接关系,这两者之间间隔着的是Presenter层,其负责调控View与Model之间的间接交互,MVP的结构图如下所示,对于这个图理解即可而不必限于其中的条条框框,毕竟在不同的场景下多少会有些出入的。在Android中很重要的一点就是对UI的操作基本上需要异步进行也就是在MainThread中才能操作UI,所以对View与Model的切断分离是合理的。此外Presenter与View、Model的交互使用接口定义交互操作可以进一步达到松耦合也可以通过接口更加方便地进行单元测试。
这里写图片描述
以上内容引自Android中的MVP

MVP的优点:

  1. 在MVP中,Presenter可以用于多个视图,但在MVC中的Activity就不行
  2. 模型与视图完全分离,我们可以修改视图而不影响模型
  3. 可以更高效的使用模型,因为所有的交互都发生在一个地方——Presenter
  4. 如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑单元

准备工作

一、注册环境云

1、打开浏览器输入www.envicloud.cn/pages/regis… ,进入环境云登录页面;
填写基本信息
2、注册完成后,将会在您注册时使用的邮箱中收到激活邮件,请按提示点击激活链接;
这里写图片描述
3、登录环境云,点击安全管理的安全凭证标签页,获取您的用户私钥。
这里写图片描述

二、搭建项目环境

1、新建Android 工程:打开Android Studio建立一个android项目,其中详细细节我就不再讲述了,不会的大家可以百度一下,你就知道。我的项目名为WeatherDemo
2、组织包结构:详细结构如下图所示
这里写图片描述
3、导入所需jar包:

三、设计业务逻辑

本项目非常的简单,只有一个实体类,那就是Weather类,所以所有的业务逻辑都是围绕Weather类展开的。用一张图就可以非常完美的展示我们项目的流程:

Created with Raphaël 2.1.0WeatherViewWeatherViewWeatherPresenterWeatherPresenterWeatherModelWeatherModel调用getWeather(String cityName);调用loadWeather(String cityName);回调onWeatherListener更新数据;

那么我们安装MVP的模式从下往上的顺序逐步分析我们的项目:

  • 首先是Model层:基本概念如下:

    Model 是用户界面需要显示数据的抽象,也可以理解为从业务数据(结果)那里到用户界面的抽象(Business rule, data access, model classes)。

    而在本示例的Model中,我们就是简单的通过网络调用环境云API,获取要查询城市的天气信息。所以我们在刚才的model包下建立一个接口,叫做WeatherModel:

package com.leven.weatherdemo.model;

import com.leven.weatherdemo.presenter.OnWeatherLitener;

/**
 *查询天气的接口
 * Created by Administrator on 2016/8/16.
 */
public interface WeatherModel {

    /**
     * 访问环境云API,获取数据
     *
     * @param cityName 要查询城市名称
     * @param litener  得到天气数据后回调接口方法
     */
    void loadWeather(String cityName, OnWeatherLitener litener);

}

Presenter这一层处理着程序各种逻辑的分发,收到View层UI上的反馈命令、定时命令、系统命令等指令后分发处理逻辑交由业务层做具体的业务操作,然后将得到的 Model 给 View 显示。

我们的Presenter中呢,也非常的简单。
-第一、负责从前台拿到用户输入的城市名称,然后调用WeatherModel的loadWeather()方法去获取数据。
-第二、数据获取成功后,去通知View更新数据

详细步骤如下:
1、在presenter包下建立ModelPresenter接口

package com.leven.weatherdemo.presenter;

/**
 * Created by Administrator on 2016/8/16.
 */
public interface WeatherPresenter {

    /**
     * 前台调用该方法去间接调用Model的loadWeather()方法
     * @param cityName  要查询的城市名称
     */
    void getWeather(String cityName);
}

2、建立数据更新完成后的回调接口OnWeatherLitener

package com.leven.weatherdemo.presenter;

import com.leven.weatherdemo.model.entity.Weather;

/**
 * Created by Administrator on 2016/8/16.
 */
public interface OnWeatherLitener {
    /**
     * 成功时回调该方法
     * @param weather
     */
    void onSuccess(Weather weather);

    /**
     * 失败时调用
     */
    void onFiled();

}

视图这一层体现的很轻薄,负责显示数据、提供友好界面跟用户交互就行。MVP下Activity和Fragment体现在了这一层,Activity一般也就做加载UI视图、设置监听再交由Presenter处理的一些工作,所以也就需要持有相应Presenter的引用。例如,Activity上滚动列表时隐藏或者显示Acionbar(Toolbar),这样的UI逻辑时也应该在这一层。另外在View上输入的数据做一些判断时,例如,EditText的输入数据,假如是简单的非空判断则可以作为View层的逻辑,而当需要对EditText的数据进行更复杂的比较时,如从数据库获取本地数据进行判断时明显需要经过Model层才能返回了,所以这些细节需要自己掂量。

详细步骤如下:
view包下建立WeatherView接口,主要功能包括

package com.leven.weatherdemo.view;

import com.leven.weatherdemo.model.entity.Weather;

/**
 * Created by Administrator on 2016/8/16.
 */
public interface WeatherView {
    /**
     * 显示进度条
     */
    void showLoading();

    /**
     *隐藏进度条
     */
    void dismissLoading();

    /**
     * 显示更新失败
     */
    void showError();

    /**
     * 更新UI
     * @param weather
     */
    void setWeatherInfo(Weather weather);
}

四、json2Bean

大家都知道java是面向对象的,所以我们要将环境云返回给我们的json数据封装为一个bean。这里推荐一个Android Studio的插件,名字叫Json2Class
这里写图片描述
安装这里就再不详细讲述了。
然后在entity包下,右键—>new—->class from json—>粘贴Json字符串
这里写图片描述
把json字符串粘贴进去,起一个类名(这里叫Weather)就可以自动生成对应的Bean,非常实用而且方便的小插件。

环境云返回给我们的json数据如下:

{
    "resultCode": "0",
    "resultDesc": "Success",
    "info": {
        "citycode": "101120201",
        "publishTime": "2016-08-17",
        "forecast": [
            {
                "hightemp": "30℃",
                "fengxiang": "北风",
                "lowtemp": "26℃",
                "fengli": "3-4级",
                "date": "2016-08-17",
                "type": "多云",
                "week": "星期三"
            }  
      ],
        "currentTemp": "29.3",
        "cityname": "青岛"
    }
}

该json字符串一共会自动生成三个类,大家可以参照一下:

这里写图片描述

Weather

package com.leven.weatherdemo.model.entity;

import android.os.Parcel;
import android.os.Parcelable;

import java.util.List;

public class Weather {

    private String resultCode;
    private String resultDesc;
    private WeatherInfo info;

    public String getResultCode() {
        return this.resultCode;
    }

    public void setResultCode(String resultCode) {
        this.resultCode = resultCode;
    }

    public String getResultDesc() {
        return this.resultDesc;
    }

    public void setResultDesc(String resultDesc) {
        this.resultDesc = resultDesc;
    }

    public WeatherInfo getInfo() {
        return info;
    }

    public void setInfo(WeatherInfo info) {
        this.info = info;
    }
}

WeatherInfo

package com.leven.weatherdemo.model.entity;

import java.util.List;

public class WeatherInfo {

    private String publishTime;
    private String citycode;
    private String cityname;
    private List forecast;
    private String currentTemp;

    public String getPublishTime() {
        return this.publishTime;
    }

    public void setPublishTime(String publishTime) {
        this.publishTime = publishTime;
    }

    public String getCitycode() {
        return this.citycode;
    }

    public void setCitycode(String citycode) {
        this.citycode = citycode;
    }

    public String getCityname() {
        return this.cityname;
    }

    public void setCityname(String cityname) {
        this.cityname = cityname;
    }


    public String getCurrentTemp() {
        return this.currentTemp;
    }

    public void setCurrentTemp(String currentTemp) {
        this.currentTemp = currentTemp;
    }

    public List getForecast() {
        return forecast;
    }

    public void setForecast(List forecast) {
        this.forecast = forecast;
    }
}

WeatherInfoForecast

package com.leven.weatherdemo.model.entity;

import android.os.Parcel;
import android.os.Parcelable;

public class WeatherInfoForecast {

    private String date;
    private String week;
    private String fengli;
    private String fengxiang;
    private String lowtemp;
    private String type;
    private String hightemp;

    public String getDate() {
        return this.date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public String getWeek() {
        return this.week;
    }

    public void setWeek(String week) {
        this.week = week;
    }

    public String getFengli() {
        return this.fengli;
    }

    public void setFengli(String fengli) {
        this.fengli = fengli;
    }

    public String getFengxiang() {
        return this.fengxiang;
    }

    public void setFengxiang(String fengxiang) {
        this.fengxiang = fengxiang;
    }

    public String getLowtemp() {
        return this.lowtemp;
    }

    public void setLowtemp(String lowtemp) {
        this.lowtemp = lowtemp;
    }

    public String getType() {
        return this.type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getHightemp() {
        return this.hightemp;
    }

    public void setHightemp(String hightemp) {
        this.hightemp = hightemp;
    }

}

五、小结

在本小节中,我们完成了一些基本准备工作,同时简单的分析了该项目大致流程以及如何组建MVP的结构。下一节中,我们将具体实现以上定义的业务接口,完成我们的天气查询功能!