本节要完成3个mission。
主要是完成3个工具类,一个是与服务器交互的HttpUtil,另外一个是解析数据的Utility,还有一个是保存天气信息。
mission1:通过联网来获取数据
我们是通过联网来获取省县市的数据的,这就是说我们的程序存在与服务器的交互,那么我们通过一个工具类去与服务器去交互,省去了我们每次都写重复的关于HttpUrlConnection的代码。
我们去创建一个类,叫HttpUtil。
package org.guya.myweather.utils;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpUtil {
public static void sendRequest(final String address,final HttpCallbackListener listener) {
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection conn = null;
try {
URL url = new URL(address);
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
int code = conn.getResponseCode();
if (code == 200) {
InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(
new InputStreamReader(is));
StringBuilder resp = new StringBuilder();
String line = null;
while ((line = br.readLine()) != null) {
resp.append(line);
}
if (resp != null) {
listener.onFinish(resp.toString());
}
}
} catch (Exception e) {
e.printStackTrace();
listener.onError(e);
} finally {
conn.disconnect();
}
}
}).start();
}
}
大致的解释一下上面的代码:联网属于耗时操作,因此要开启子线程去实现联网操作。当响应码为200的时候,说明我们联网成功了,然后我们把数据转化成流的形式,然后去读取数据。返回的数据需要在其他类中使用,但是子线程的数据又不能返回,那么,使用回调的方式去解决。
那么什么时候使用回调呢?举例:某天,我打电话向你请教问题,当然是个难题,你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。
接下来去暴露接口:
package org.guya.myweather.utils;
public interface HttpCallbackListener {
void onFinish(String response);
void onError(Exception e);
}
然后谁去调用了HttpUtil.sendRequest(final String address,final HttpCallbackListener listener),就去具体实现其中的onFinish()方法,这样就可以获得数据并且处理数据了。如果联网失败,则调用onError()方法。
mission2:解析数据。
如何获取全国所有省份的信息呢,我们只要要访问以下网址 http://www.weather.com.cn/data/list3/city.xml,就会返回中国所有省份的名称和代号,如下所示。01|北京,02|上海,03|天津,19|江苏等等,我们可以看到城市与其代号之间通过|号相隔开,省份与省份之间用,号隔开,记住这个结构。之后的之后会用到正则表达式去截取。
如何查看江苏省下的城市的信息呢,其实也非常简单,只需要访问以下网址http://www.weather.com.cn/data/list3/city19.xml,也就是只需要将省级代号添加至city后面就可以了,服务器就会返回数据1901|南京,1902|无锡,1903|镇江,1904|苏州,1905|南通,1906|扬州,1907|盐城,1908|徐州,1909|淮安,1910|连云港,1911|常州,1912|泰州,1913|宿迁。
同样的方法,我们如果想访问扬州以下的县市的信息,只需要city添加1906即可,如下示 http://www.weather.com.cn/data/list3/city1906.xml,服务器就会返回数据190601|扬州,190602|宝应,190603|仪征,190604|高邮,190605|江都,190606|邗江。
以上我们就可以知道如何获得全国省市区的信息了,那么如何得到某具体城市的天气呢?以扬州市邗江区为例,县级代号为
190606,那么访问以下网址http://www.weather.com.cn/data/list3/city
190606.xml就会返回一个很简单的数据190606|101190606,后面的就是扬州市邗江区所对应的天气代号,之后我们在用我们这个得到的代号就可以访问以下网址http://www.weather.com.cn/data/cityinfo/
101190606.html,这样服务器就会把扬州市邗江区的天气信息已json格式的数据返回给我们,如下所示。
{"weatherinfo":{"city":"邗江","cityid":"101190606","temp1":"8℃","temp2":"2℃","weather":"阴转多云","img1":"d2.gif","img2":"n1.gif","ptime":"11:00"}}
经过上面的分析,服务器返回的省市县数据的格式是"代号|城市","代号|城市"这样子的格式。
那么我们就要去用正则表达式分解这样子的数据,来获取省市县。各个
省份与各个省份之间用,号隔开,每个省份和省份代号由|号隔开,城市以及县的数据以此类推。
以省举例子
/**
* 解析和处理服务器返回的省级数据
*
* @param weatherDB
* @param resp
* @return
*/
public static boolean handleProvincesResponse(WeatherDB weatherDB,
String resp) {
if (!TextUtils.isEmpty(resp)) {
String[] provinces = resp.split(",");
if (provinces != null && provinces.length > 0) {
for (String p : provinces) {
String[] array = p.split("\\|");
Province province = new Province();
province.setProvinceCode(array[0]);
province.setProvinceName(array[1]);
weatherDB.saveProvince(province);
}
}
return true;
}
return false;
}
解析天气用的是json解析。
/**
* 解析服务器返回的json数据,并且将数据存储到本地
*
* @param context
* @param response
*/
public static void handleWeatherResponse(Context context, String resp) {
try {
JSONObject jsonObject = new JSONObject(resp);
JSONObject weatherinfo = jsonObject.getJSONObject("weatherinfo");
String cityName = weatherinfo.getString("city");
String weatherCode = weatherinfo.getString("cityid");
String temp1 = weatherinfo.getString("temp1");
String temp2 = weatherinfo.getString("temp2");
String weatherDesp = weatherinfo.getString("weather");
String publishTime = weatherinfo.getString("ptime");
saveWeatherInfo(context, cityName, weatherCode, temp1, temp2,
weatherDesp, publishTime);
} catch (Exception e) {
e.printStackTrace();
}
}
mission3:保存天气。
在mission2中的json解析天气方法里,我们调用了一个saveWeatherinfo()方法,该方法调用了SharedPreferences去保存数据。
private static void saveWeatherInfo(Context context, String cityName,
String weatherCode, String temp1, String temp2, String weatherDesp,
String publishTime) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日", Locale.CHINA);
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(context);
Editor editor = sp.edit();
editor.putBoolean("city_selected", true);
editor.putString("city_name", cityName);
editor.putString("weather_code", weatherCode);
editor.putString("temp1", temp1);
editor.putString("temp2", temp2);
editor.putString("weather_desp", weatherDesp);
editor.putString("publish_time", publishTime);
editor.putString("current_date", sdf.format(new Date()));
editor.commit();
}
这样,我们就可以共享天气数据了。
下面给出全部代码
package org.guya.myweather.utils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.guya.myweather.db.WeatherDB;
import org.guya.myweather.model.City;
import org.guya.myweather.model.County;
import org.guya.myweather.model.Province;
import org.json.JSONObject;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
import android.text.TextUtils;
public class Utility {
/**
* 解析和处理服务器返回的省级数据
*
* @param weatherDB
* @param resp
* @return
*/
public static boolean handleProvincesResponse(WeatherDB weatherDB,
String resp) {
if (!TextUtils.isEmpty(resp)) {
String[] provinces = resp.split(",");
if (provinces != null && provinces.length > 0) {
for (String p : provinces) {
String[] array = p.split("\\|");
Province province = new Province();
province.setProvinceCode(array[0]);
province.setProvinceName(array[1]);
weatherDB.saveProvince(province);
}
}
return true;
}
return false;
}
/**
* 解析和处理服务器返回的城市数据
*
* @param WeatherDB
* @param response
* @param provinceId
* @return
*/
public synchronized static boolean handleCitiesResponse(
WeatherDB WeatherDB, String resp, int provinceId) {
if (!TextUtils.isEmpty(resp)) {
String[] allCities = resp.split(",");
if (allCities.length > 0 && allCities != null) {
for (String c : allCities) {
String[] array = c.split("\\|");
City city = new City();
city.setCityCode(array[0]);
city.setCityName(array[1]);
city.setProvinceId(provinceId);
WeatherDB.saveCity(city);
}
return true;
}
}
return false;
}
/**
* 解析和处理服务器返回的县级数据
*
* @param WeatherDB
* @param resp
* @param cityId
* @return
*/
public synchronized static boolean handleCountiesResponse(
WeatherDB WeatherDB, String resp, int cityId) {
if (!TextUtils.isEmpty(resp)) {
String[] allCounties = resp.split(",");
if (allCounties.length > 0 && allCounties != null) {
for (String c : allCounties) {
String[] array = c.split("\\|");
County county = new County();
county.setCountyCode(array[0]);
county.setCountyName(array[1]);
county.setCityId(cityId);
WeatherDB.saveCounty(county);
}
return true;
}
}
return false;
}
/**
* 解析服务器返回的json数据,并且将数据存储到本地
*
* @param context
* @param response
*/
public static void handleWeatherResponse(Context context, String resp) {
try {
JSONObject jsonObject = new JSONObject(resp);
JSONObject weatherinfo = jsonObject.getJSONObject("weatherinfo");
String cityName = weatherinfo.getString("city");
String weatherCode = weatherinfo.getString("cityid");
String temp1 = weatherinfo.getString("temp1");
String temp2 = weatherinfo.getString("temp2");
String weatherDesp = weatherinfo.getString("weather");
String publishTime = weatherinfo.getString("ptime");
saveWeatherInfo(context, cityName, weatherCode, temp1, temp2,
weatherDesp, publishTime);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void saveWeatherInfo(Context context, String cityName,
String weatherCode, String temp1, String temp2, String weatherDesp,
String publishTime) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年M月d日", Locale.CHINA);
SharedPreferences sp = PreferenceManager
.getDefaultSharedPreferences(context);
Editor editor = sp.edit();
editor.putBoolean("city_selected", true);
editor.putString("city_name", cityName);
editor.putString("weather_code", weatherCode);
editor.putString("temp1", temp1);
editor.putString("temp2", temp2);
editor.putString("weather_desp", weatherDesp);
editor.putString("publish_time", publishTime);
editor.putString("current_date", sdf.format(new Date()));
editor.commit();
}
}