我写的是一个名叫DogWeather的天气查询APP,当然这只是我的一种自娱自乐。这个APP有手动更新、后台启动更新和指定城市查询天气的功能。不过页面很丑,确实不擅长做页面。废话不多说直接开始。
我觉得做一个APP的最主要的事情就是要先布好局,要不然就会事倍功半。首先我将程序分为这几个模块,Activity模块、网络信息获取和解析模块、信息存储模块、后台更新模块。
Activity模块
在这个模块中我设计了这么几个ActivityBaseActivity是用来作为我的其他的Activity的父类来使用的,他不实现具体的功能,但是他和ActivityCollector结合起来使用就可以来管理我的Activity(这个技巧是我在《第一行代码》上看到的,还有后面的MyLog。这确实是一本很好的书,学到很多)。
他的具体功能是通过一个List来管理Activity,并且提供一个随时退出程序的静态方法。
package com.peter.dogweather.activity;
import android.app.Activity;
import android.os.Bundle;
public class BaseActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Log.d(tag, msg)
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
package com.peter.dogweather.activity;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
/**
* 用于管理应用程序的退出
*
* @author Administrator
*
*/
public class ActivityCollector {
public static List<Activity> activities = new ArrayList<Activity>();
public static void addActivity(Activity activity) {
activities.add(activity);
}
public static void removeActivity(Activity activity) {
activities.remove(activity);
}
public static void finishAll() {
for (Activity activity : activities) {
if (!activity.isFinishing()) {
activity.finish();
}
}
}
}
ChooseActivity是选择城市的活动,优先从SQLite数据库中读取数据,如果没有则从网络上获取
private void queryProvinces() {
provinceList = dogWeatherDB.getProvinceInfo();
if (provinceList.size() > 0) {
dataList.clear();// 清除dataList的数据
for (Province province : provinceList) {
// MyLog.d("ChooseActivity",province.getProvinceName());
dataList.add(province.getProvinceName());
}
// 通知适配器数据已改变
adapter.notifyDataSetChanged();
lv_choose_area.setSelection(0);
tv_title_select.setText("中国");
LEVEL_CURRENT = LEVEL_PROVINCE;
} else {
queryFromServer(null, "Province");
}
}
private void queryFromServer(final String code, final String level) {
String httpUrl;
if (TextUtils.isEmpty(code)) {
httpUrl = "http://www.weather.com.cn/data/list3/city.xml";
} else {
httpUrl = "http://www.weather.com.cn/data/list3/city" + code
+ ".xml";
}
Utility.sendRequest4AreaInfo(httpUrl, new HttpResponseListener() {
@Override
public void onFinish(String response) {
boolean result = false;
if ("Province".equals(level)) {
result = Utility.handleProvinceInfo(dogWeatherDB, response);
MyLog.d("ChooseActivity", "解析省份信息完毕");
// queryProvinces();
} else if ("City".equals(level)) {
result = Utility.handleCityInfo(dogWeatherDB, response,
selectProvince.getId());
MyLog.d("ChooseActivity", "解析市级信息完毕");
} else if ("County".equals(level)) {
result = Utility.handleCountyInfo(dogWeatherDB, response,
selectCity.getId());
MyLog.d("ChooseActivity", "解析县级信息完毕");
}
if (result) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if ("Province".equals(level)) {
queryProvinces();
} else if ("City".equals(level)) {
queryCitys();
} else if ("County".equals(level)) {
queryCountys();
}
}
});
}
}
@Override
public void onError(Exception e) {
Toast.makeText(ChooseActivity.this, "网络请求失败,请检查你的网络!",
Toast.LENGTH_SHORT).show();
}
});
}
这里在网络请求模块开启线程而不是让调用者去开启,为了读取线程中返回的结果信息,这里使用了Java的回调机制。
WeatherActivity是用将信息显示到页面上的,具体你需要什么信息你自己去设置。哦,对了,我使用的接口是和风天气,这个可以在API store中找到。
private void showWeather() {
MyLog.d("WeatherActivity", "开始显示数据");
if (weatherData != null) {
MyLog.d("WeatherActivity", "开始解析天气字符串数据");
heWeatherData = Utility.handleWeatherInfo(weatherData);
if(heWeatherData == null){
return;
}
if (heWeatherData == null) {
MyLog.d("WeatherActivity", "传递对象失败");
}
} else {
MyLog.d("WeatherActivity", "查询天气数据");
queryWeather(selectName);
return;// 避免堆栈
}
}
weatherData中我存储的是从网络获取的天气信息,每当我要使用天气信息的时候我就会调用Utility中的解析JSON数据的方法来解析这个字符串。我是通过GSON方式来解析的数据,使用GSON数据你要先往你的项目中加入gson的jar包,然后你需要构建好Bean对象,使用GSON的方法就可以很简便的方式来解析他,当然你也可以使用JSONObject来解析他。
private void queryWeather(final String selectName) {
String httpUrl = "http://apis.baidu.com/heweather/weather/free?city=";
try {
MyLog.d("WeatherActivity", "" + selectName);
String temp = URLEncoder.encode(selectName, "utf-8");
Utility.sendRequest4WeatherInfo(httpUrl + temp,
new HttpResponseListener() {
@Override
public void onFinish(String response) {
if(!response.contains("ok")){
weatherData = null;
Toast.makeText(WeatherActivity.this, "请切换城市", Toast.LENGTH_SHORT).show();
return;
}
weatherData = response;
SharedPreferences.Editor editor = getSharedPreferences(
"data", MODE_PRIVATE).edit();
editor.putString("weatherData", response);
editor.putBoolean("isSelected", true);
editor.commit();
runOnUiThread(new Runnable() {
@Override
public void run() {
showWeather();
}
});
}
@Override
public void onError(Exception e) {
Toast.makeText(WeatherActivity.this,
"网络请求失败,请检查你的网络!", Toast.LENGTH_SHORT)
.show();
}
});
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
WelcomeActivity就是一个欢迎页,不赘述。
Model ,这里存放了我的Bean,天气对象和省市区对象。Bean对象其实也就是GETSET方法。
后台启动更新,通过服务和广播结合的形式来完成。这里需要注意的是在service中接收Activity发送的数据
public int onStartCommand(Intent intent, int flags, int startId) {
selectName = intent.getExtras().getString("selectName");
MyLog.d("AutoRefreshService", selectName + "-->自动更新");
new Thread() {
public void run() {
MyLog.d("AutoRefreshService","后台更新天气");
updateWeather();
};
}.start();
// 后台自动更新天气,三个小时更新一次
Intent autoRefreshIntent = new Intent(this, AutoRefreshReceiver.class);
autoRefreshIntent.putExtra("selectName", selectName);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,
autoRefreshIntent, 0);
long triggerAtMillis = SystemClock.elapsedRealtime() + 3 * 60 * 60 * 1000;
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis,
pendingIntent);
return super.onStartCommand(intent, flags, startId);
}
在广播中重新启动service
@Override
public void onReceive(Context context, Intent intent) {
// System.out.println("广播"+intent.getStringExtra("selectName"));
Intent autoRefreshIntent = new Intent(context,AutoRefreshService.class);
autoRefreshIntent.putExtra("selectName", intent.getStringExtra("selectName"));
context.startService(autoRefreshIntent);
}
当然你需要在Activity中启动service
// 启动后台更新服务
Intent intent = new Intent(WeatherActivity.this,
AutoRefreshService.class);
intent.putExtra("selectName", tv_select_area.getText().toString()
.trim());
startService(intent);
信息解析模块
,最要的一个文件是Utility,这里面包含获取天气信息,获取城市信息,解析天气信息,解析城市信息的方法。代码太长我就不贴出来,而且这些方法对不同的数据不同的解析方法。
我会把我这个项目方法Github上点击打开链接,博客写的不好,主要还是为了巩固自己知识。