http://www.webxml.com.cn/WebServices/WeatherWebService.asmx 是一个免费的天气预报服务接口(限访问次数),本文以此为例,实现简单的天气预报功能。
实现效果:
重点如下:
- 天气预报接口的使用
- AsyncTask的使用
- LruCache的使用
源码:
activity_load_weather.xml
显示最近三天的天气。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@mipmap/r_t1"/>
<Button
android:id="@+id/btn_select_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="广东广州"
android:textSize="20sp"
android:textColor="@color/black"
android:layout_marginTop="5dp"
android:background="@color/lightseagreen"/>
<TextView
android:id="@+id/tv_today_pes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="今日天气实况:"
android:textColor="@color/black"
android:textSize="18sp"
android:layout_marginTop="20dp"
android:padding="10dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:gravity="top"
android:layout_marginTop="5dp"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_today_pic1"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/iv_today_pic2"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_today_weather"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/black"
android:textSize="16sp"
android:layout_marginLeft="10dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:padding="5dp"
android:gravity="top"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_tomorrow_pic1"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/iv_tomorrow_pic2"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_tomorrow_weather"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/black"
android:textSize="16sp"
android:layout_marginLeft="10dp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:padding="5dp"
android:layout_gravity="top"
android:layout_weight="1">
<ImageView
android:id="@+id/iv_after_tomorrow_pic1"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/iv_after_tomorrow_pic2"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_after_tomorrow_weather"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/black"
android:textSize="16sp"
android:layout_marginLeft="10dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
LoadWeatherActivity.java
切换城市、显示天气。
public class LoadWeatherActivity extends Activity {
private Button btnSelectCity = null;
private ImageView ivTodayPic1 = null;
private ImageView ivTodayPic2 = null;
private TextView tvTodayWeather = null;
private TextView tvTodayPES = null;
private ImageView ivTomorrowPic1 = null;
private ImageView ivTomorrowPic2 = null;
private TextView tvTomorrowWeather = null;
private ImageView ivAfterTomorrowPic1 = null;
private ImageView ivAfterTomorrowPic2 = null;
private TextView tvAfterTomorrowWeather = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_load_weather);
btnSelectCity = (Button) findViewById(R.id.btn_select_city);
ivTodayPic1 = (ImageView) findViewById(R.id.iv_today_pic1);
ivTodayPic2 = (ImageView) findViewById(R.id.iv_today_pic2);
tvTodayWeather = (TextView) findViewById(R.id.tv_today_weather);
tvTodayPES = (TextView) findViewById(R.id.tv_today_pes);
ivTomorrowPic1 = (ImageView) findViewById(R.id.iv_tomorrow_pic1);
ivTomorrowPic2 = (ImageView) findViewById(R.id.iv_tomorrow_pic2);
tvTomorrowWeather = (TextView) findViewById(R.id.tv_tomorrow_weather);
ivAfterTomorrowPic1 = (ImageView) findViewById(R.id.iv_after_tomorrow_pic1);
ivAfterTomorrowPic2 = (ImageView) findViewById(R.id.iv_after_tomorrow_pic2);
tvAfterTomorrowWeather = (TextView) findViewById(R.id.tv_after_tomorrow_weather);
// 切换城市
btnSelectCity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ProvinceManager.getInstance(LoadWeatherActivity.this).showProvinceDialog();
}
});
// 注册天气改变的事件
WeatherManager.getInstance(LoadWeatherActivity.this).setToldWeatherChangedListener(new WeatherManager.ToldWeatherChangedListener() {
@Override
public void toldWeatherChanged(WeatherBean weather) {
refresh(weather);
}
});
// 初始化天气
WeatherManager.getInstance(LoadWeatherActivity.this).getWeather("广州");
}
/**
* 刷新天气信息
* @param weather
*/
private void refresh(WeatherBean weather) {
if (weather != null) {
btnSelectCity.setText(weather.Province + weather.City);
String today = "今天:" + weather.Today.Day
+ "\n" + "天气:" + weather.Today.Weather
+ "\n" + "气温:" + weather.Today.Temperature
+ "\n" + "风力:" + weather.Today.WindPower;
tvTodayWeather.setText(today);
tvTodayPES.setText(weather.Today.PES);
ivTodayPic1.setImageResource(getImageIdByName("a_" + weather.Today.Picture1));
ivTodayPic2.setImageResource(getImageIdByName("a_" + weather.Today.Picture2));
String tomorrow = "明天:" + weather.Tomorrow.Day
+ "\n" + "天气:" + weather.Tomorrow.Weather
+ "\n" + "气温:" + weather.Tomorrow.Temperature
+ "\n" + "风力:" + weather.Tomorrow.WindPower;
tvTomorrowWeather.setText(tomorrow);
ivTomorrowPic1.setImageResource(getImageIdByName("a_" + weather.Tomorrow.Picture1));
ivTomorrowPic2.setImageResource(getImageIdByName("a_" + weather.Tomorrow.Picture2));
String afterTomorrow = "后天:" + weather.AfterTomorrow.Day
+ "\n" + "天气:" + weather.AfterTomorrow.Weather
+ "\n" + "气温:" + weather.AfterTomorrow.Temperature
+ "\n" + "风力:" + weather.AfterTomorrow.WindPower;
tvAfterTomorrowWeather.setText(afterTomorrow);
ivAfterTomorrowPic1.setImageResource(getImageIdByName("a_" + weather.AfterTomorrow.Picture1));
ivAfterTomorrowPic2.setImageResource(getImageIdByName("a_" + weather.AfterTomorrow.Picture2));
}
}
/**
* 根据图片名称获取R.java中对应的id
*
* @param name
* @return
*/
public static int getImageIdByName (String name) {
int value = 0;
if (null != name) {
if (name.indexOf(".") != -1) {
name = name.substring(0, name.indexOf("."));
}
Class<R.mipmap> cls = R.mipmap.class;
try {
value = cls.getDeclaredField(name).getInt(null);
} catch (Exception e) {
}
}
return value;
}
}
ProvinceManager.java
使用AsyncTask下载城市,保存在内存中,以免重复下载。
选择省份后,弹出城市选择的对话框。
public class ProvinceManager {
private static final String PROVINCE_URL = "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getSupportProvince";
private Context ctx = null;
/**
* 选择省份对话框
*/
private AlertDialog dialog = null;
/**
* 省份列表
*/
private String[] provinces = null;
private static ProvinceManager instance = null;
public static ProvinceManager getInstance(Context context) {
if (instance == null) {
instance = new ProvinceManager(context);
}
return instance;
}
private ProvinceManager(Context context) {
ctx = context;
}
/**
* 弹出省份选择对话框
*/
public void showProvinceDialog() {
if (provinces == null) {
new LoadProvinceAsyncTask().execute();
} else {
showDialog();
}
}
/**
* 弹出省份选择对话框
*/
private void showDialog() {
if (provinces != null) {
if (dialog == null) {
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle("请选择省份:");
builder.setSingleChoiceItems(provinces, 0, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String province = provinces[i];
//选择城市后关闭省份的选择框
CityManager.getInstance(ctx).setToldDimissProvinceSelectDialogListener(new CityManager.ToldDimissProvinceSelectDialogListener() {
@Override
public void toldDimissProvinceSelectDialog() {
dismissDialog();
}
});
//选择城市
CityManager.getInstance(ctx).showCityDialog(province);
}
});
dialog = builder.create();
}
dialog.show();
}
}
/**
* 关闭对话框
*/
private void dismissDialog() {
if (dialog != null) {
dialog.dismiss();
}
}
/**
* 加载省份的异步任务
*/
class LoadProvinceAsyncTask extends AsyncTask<Void, Void, String[]> {
@Override
protected String[] doInBackground(Void... voids) {
return getProvince(PROVINCE_URL);
}
@Override
protected void onPostExecute(String[] strings) {
super.onPostExecute(strings);
provinces = strings;
//显示选择对话框
showDialog();
}
/**
* 读取省份信息
* @param urlStr
* @return
*/
private String[] getProvince(String urlStr) {
List<String> result = new ArrayList<>();
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
try {
inputStream = new URL(urlStr).openStream();
inputStreamReader = new InputStreamReader(inputStream, "utf-8");
XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
xmlPullParser.setInput(inputStreamReader);
int eventType;
while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
String name = xmlPullParser.getName();
if (name.equalsIgnoreCase("string")) {
String value = xmlPullParser.nextText();
result.add(value);
}
break;
default:
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
int size = result.size();
String[] resultArr = new String[size];
for (int i=0;i<size;i++) {
resultArr[i] = result.get(i);
}
return resultArr;
}
}
}
CityManager.java
异步下载城市信息,保存在缓存中,以免重复下载。
选择城市后,下载天气。
public class CityManager {
private static final String CITY_URL = "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getSupportCity?byProvinceName=";
private Context ctx = null;
/**
* 缓存,存储查询过的省份及对应城市
*/
private LruCache<String, String[]> cityLruCache = null;
/**
* 通知关闭省份选择对话框的事件
*/
public interface ToldDimissProvinceSelectDialogListener {
void toldDimissProvinceSelectDialog();
}
private ToldDimissProvinceSelectDialogListener toldDimissProvinceSelectDialogListener = null;
public void setToldDimissProvinceSelectDialogListener(ToldDimissProvinceSelectDialogListener listener) {
toldDimissProvinceSelectDialogListener = listener;
}
private static CityManager instance = null;
public static CityManager getInstance(Context context) {
if (instance == null) {
instance = new CityManager(context);
}
return instance;
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private CityManager(Context context) {
ctx = context;
// 获取最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
// 使用最大可用内存的十分之一
int cacheSize = maxMemory / 10;
// 定义缓存变量,重写sizeOf方法
cityLruCache = new LruCache<String, String[]>(cacheSize){
@Override
protected int sizeOf(String key, String[] value) {
// 在每次存入缓存时调用
if (value != null && value.length > 0) {
int size = 0;
for (String str : value) {
size += str.length();
}
return size;
}
return 0;
}
};
}
/**
* 从缓存中获取指定省份的城市列表
* @param province
* @return
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private String[] getCitysFromCache(String province) {
return cityLruCache.get(province);
}
/**
* 向缓存中添加省份的城市列表
* @param province
* @param citys
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private void addCitysToCache(String province, String[] citys) {
if (getCitysFromCache(province) == null) {
cityLruCache.put(province, citys);
}
}
/**
* 显示城市选择对话框
* @param province
*/
public void showCityDialog(String province) {
String[] citys = getCitysFromCache(province);
if (citys == null) {
new LoadCityAsyncTask().execute(province);
} else {
showDialog(citys);
}
}
private void showDialog(final String[] citys) {
if (citys != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
builder.setTitle("请选择城市:");
builder.setSingleChoiceItems(citys, 0, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String city = citys[i];
// 查询天气
WeatherManager.getInstance(ctx).getWeather(city);
// 关闭城市选择对话框
dialogInterface.dismiss();
// 关闭省份选择对话框
if (toldDimissProvinceSelectDialogListener != null) {
toldDimissProvinceSelectDialogListener.toldDimissProvinceSelectDialog();
}
}
});
AlertDialog dialog = builder.create();
dialog.show();
}
}
class LoadCityAsyncTask extends AsyncTask<String, Void, String[]> {
@Override
protected String[] doInBackground(String... strings) {
try {
//使用URLEncoder.encode解决URL中存在中文导致openStream异常的问题
String[] citys = getCity(CITY_URL + URLEncoder.encode(strings[0], "utf8"));
//添加到缓存
addCitysToCache(strings[0], citys);
return citys;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String[] strings) {
super.onPostExecute(strings);
//显示选择对话框
showDialog(strings);
}
/**
* 通过网络获取指定省份的城市列表
* @param urlAddress
* @return
*/
private String[] getCity(String urlAddress) {
List<String> result = new ArrayList<>();
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
try {
inputStream = new URL(urlAddress).openStream();
inputStreamReader = new InputStreamReader(inputStream, "utf8");
XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
xmlPullParser.setInput(inputStreamReader);
int eventType;
while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
String name = xmlPullParser.getName();
if (name.equalsIgnoreCase("string")) {
String value = xmlPullParser.nextText();
//String code = value.substring(value.length() - 6, value.length() - 1);
String city = value.substring(0, value.indexOf(" "));
result.add(city);
}
break;
default:
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
int size = result.size();
String[] resultArr = new String[size];
for (int i=0;i<size;i++) {
resultArr[i] = result.get(i);
}
return resultArr;
}
}
}
WeatherManager.java
异步下载天气信息,并通知界面进行更新。
public class WeatherManager {
private static final String WEATHER_URL = "http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getWeatherbyCityName?theCityName=";
private Context ctx = null;
private static WeatherManager instance = null;
public static WeatherManager getInstance(Context context) {
if (instance == null) {
instance = new WeatherManager(context);
}
return instance;
}
private WeatherManager(Context context) {
ctx = context;
}
/**
* 通知天气改变的事件
*/
public interface ToldWeatherChangedListener {
void toldWeatherChanged(WeatherBean weather);
}
private ToldWeatherChangedListener toldWeatherChangedListener = null;
public void setToldWeatherChangedListener(ToldWeatherChangedListener listener) {
toldWeatherChangedListener = listener;
}
/**
* 获取指定城市的天气
* @param city
*/
public void getWeather(String city) {
new LoadWeatherAsyncTask().execute(city);
}
class LoadWeatherAsyncTask extends AsyncTask<String, Void, WeatherBean> {
@Override
protected WeatherBean doInBackground(String... strings) {
//使用URLEncoder.encode解决URL中存在中文导致openStream异常的问题
return getWeather(WEATHER_URL + URLEncoder.encode(strings[0]));
}
@Override
protected void onPostExecute(WeatherBean weatherBean) {
super.onPostExecute(weatherBean);
/**
* 通知天气改变
*/
if (toldWeatherChangedListener != null) {
toldWeatherChangedListener.toldWeatherChanged(weatherBean);
}
}
private WeatherBean getWeather(String urlAddress) {
WeatherBean weatherBean = null;
InputStream inputStream = null;
InputStreamReader inputStreamReader = null;
try {
inputStream = new URL(urlAddress).openStream();
inputStreamReader = new InputStreamReader(inputStream, "utf8");
XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
xmlPullParser.setInput(inputStreamReader);
//读取天气信息
List<String> weatherInfo = new ArrayList<>();
int eventType;
while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
if (xmlPullParser.getName().equalsIgnoreCase("string")) {
weatherInfo.add(xmlPullParser.nextText());
}
break;
default:
break;
}
}
if (weatherInfo.size() >= 23) {
weatherBean = new WeatherBean();
weatherBean.Province = weatherInfo.get(0);
weatherBean.City = weatherInfo.get(1);
weatherBean.Today = new WeatherBeanItem();
weatherBean.Today.Temperature = weatherInfo.get(5);
String dateAndWeather = weatherInfo.get(6);
String[] dateAndWeatherArr = dateAndWeather.split(" ");
weatherBean.Today.Day = dateAndWeatherArr[0];
weatherBean.Today.Weather = dateAndWeatherArr[1];
weatherBean.Today.WindPower = weatherInfo.get(7);
weatherBean.Today.Picture1 = weatherInfo.get(8);
weatherBean.Today.Picture2 = weatherInfo.get(9);
weatherBean.Today.PES = weatherInfo.get(10);
weatherBean.Tomorrow = new WeatherBeanItem();
weatherBean.Tomorrow.Temperature = weatherInfo.get(12);
dateAndWeather = weatherInfo.get(13);
dateAndWeatherArr = dateAndWeather.split(" ");
weatherBean.Tomorrow.Day = dateAndWeatherArr[0];
weatherBean.Tomorrow.Weather = dateAndWeatherArr[1];
weatherBean.Tomorrow.WindPower = weatherInfo.get(14);
weatherBean.Tomorrow.Picture1 = weatherInfo.get(15);
weatherBean.Tomorrow.Picture2 = weatherInfo.get(16);
weatherBean.AfterTomorrow = new WeatherBeanItem();
weatherBean.AfterTomorrow.Temperature = weatherInfo.get(17);
dateAndWeather = weatherInfo.get(18);
dateAndWeatherArr = dateAndWeather.split(" ");
weatherBean.AfterTomorrow.Day = dateAndWeatherArr[0];
weatherBean.AfterTomorrow.Weather = dateAndWeatherArr[1];
weatherBean.AfterTomorrow.WindPower = weatherInfo.get(19);
weatherBean.AfterTomorrow.Picture1 = weatherInfo.get(20);
weatherBean.AfterTomorrow.Picture2 = weatherInfo.get(21);
}
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
if (inputStreamReader != null) {
inputStreamReader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return weatherBean;
}
}
}
WeatherBean.java
public class WeatherBean {
/**
* 省份
*/
public String Province;
/**
* 城市
*/
public String City;
/**
* 今日天气
*/
public WeatherBeanItem Today;
/**
* 明日天气
*/
public WeatherBeanItem Tomorrow;
/**
* 后天天气
*/
public WeatherBeanItem AfterTomorrow;
}
WeatherBeanItem.java
public class WeatherBeanItem {
/**
* 天气图片1
*/
public String Picture1;
/**
* 天气图片2
*/
public String Picture2;
/**
* 日期
*/
public String Day;
/**
* 天气
*/
public String Weather;
/**
* 气温
*/
public String Temperature;
/**
* 风力
*/
public String WindPower;
/**
* 天气实况
*/
public String PES;
}
可进一步优化
省份、城市下载后自动保存在本地。
该服务接口的天气每2.5小时才刷新一次,因此一定时间内不需要重新下载天气信息,可添加计时器进行处理。
使用 AsyncHttpResponseHandler 处理网络请求。