使用Service+BroadcastReceiver实现定时更新天气

来源:互联网 发布:mysql多表外连接查询 编辑:程序博客网 时间:2024/04/29 05:43

一、前言

最近两个月在做毕业设计:天气app,需要用到定时更新天气数据,用的是Service+BroadcastReceiver方法(之前对服务和广播也不是很了解,所以恶补了一下)。关于这个方法我借鉴了另一篇网络文章点击打开链接

二、情景描述

    2.1 HomeActivity(app启动页,城市天气显示界面)

CityActivity(城市列表界面,跟墨迹天气,天气通一样,就是你选择的城市)


                图2.1 侧滑菜单               图2.2 HomeActivity(天气显示)             图2.3 CityActivity 

       

2.2 我们需要实现的是:(1)定时更新城市列表CityActivity中的数据(这些城市天气详细数据都存储在本地SQLite数据库中)。

      (2)定时更新主界面HomeActivity中的显示数据(每次打开HomeActivity界面时,显示的是城市列表CityActivity的置顶城市,其实也是从本地数据库中加载显示的)

(3)当APP退出时,定时更新功能仍然有效,可以在后台执行。(注意:有些情况会使得这一功能无效,在文章末尾我会给大家详述)

由于(1)(2)都需要从数据库加载城市天气去显示,所以我们定时更新的时候就直接从数据库一一取出你选择的城市,然后网络请求该城市天气,再存储到本地数据库中,这样就实现了更新本地数据库中城市天气。然后我们再发送一个广播,让CityActivity和HomeActivity接收,继而实现刷新界面显示数据的功能。

三、实现方法

3.1 定时

3.1.1 在app启动页HomeActivity的onCreate方法中设置定时,代码如下:

private void startTimingUpdateService() {// 初次启动服务后,我紧接着设置了一个“闹钟”,一小时后让另一个独立的广播接收器(TimerBroadcastReceiver.java)// 去启动更新天气的服务,从而不必依赖当前的Activity了// 通过闹钟机制30秒后启动服务//静态注册的广播,无论该程序是否启动,都会当广播到来时接收,并处理。Intent intent = new Intent(this, TimerBroadcastReceiver.class);PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1, intent, 0);AlarmManager almManager = (AlarmManager) getSystemService(ALARM_SERVICE);almManager.set(AlarmManager.RTC, // RTC 在指定的时刻,发送广播,但不唤醒设备System.currentTimeMillis() + (1000 * 30), pendingIntent);// 30秒}
在清单文件AndroidManifest.xml中,静态注册TimerBroadcastReceiver,代码如下:

<receiver android:name="com.eh.erhua_weather.utils.TimerBroadcastReceiver" >        </receiver>
3.1.2 新建java文件TimerBroadcastReceiver.java,代码如下:

package com.eh.erhua_weather.utils;import android.app.AlarmManager;import android.app.PendingIntent;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;/*** @author Administrator* 在这个广播接收器里又设置了一个“闹钟”,30秒后启动自己,然后开启服务,又设置一个周期为一小时的“闹钟”……如此循环,就达到了,周期定时的目的了**/public class TimerBroadcastReceiver extends BroadcastReceiver {private static final String tag = "TimerBroadcastReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Log.d(tag, "广播");intent.setClass(context, UpdateCityWeatherService.class);context.startService(intent);// 通过闹钟机制3秒后启动本广播接收器,实现以后每隔3秒启动本接收器,开启服务Intent intent2 = new Intent(context, TimerBroadcastReceiver.class);PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent2, 0);AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);alarmManager.set(AlarmManager.RTC, // RTC 在指定的时刻,发送广播,但不唤醒设备System.currentTimeMillis() + (1000 * 30), pendingIntent);}}

完成这两步后,就实现了定时,每隔30秒就会循环一次。我们看到在TimerBroadcastReceiver中开启了一个服务UpdateCityWeatherService,我们要在它里面实现具体的天气更新操作。你可能会问:“为什么不直接在广播中执行具体的天气更新操作呢,而是开启服务,去服务中执行”,因为广播中不能执行耗时操作,你可以去百度了解一下相关资料,这里我就不解释了,而且我也是个小白,估计不能给你解释清楚。

3.2 具体的天气更新操作

3.2.1 上面说开启了服务UpdateCityWeatherService,接着我们去清单文件AndroidManifest.xml注册这个服务,代码如下:

<service android:name="com.eh.erhua_weather.utils.UpdateCityWeatherService" >        </service>

3.2.2 然后我们新建java文件UpdateCityWeatherService(继承Service),在onCreate方法中开启线程执行具体的天气更新操作,代码如下:
@Overridepublic void onCreate() {super.onCreate();// Log.d(TAG, "onCreate() executed");// 应该在Service中开启线程去执行耗时任务,这样就可以有效地避免ANR(Application Not Responding)的出现。// 先检查网络是否连接,如果有网再执行否则关闭本次更新,等待下一个闹钟if (isNetWorkAvailable()) {// Log.d(TAG, "测试");new Thread(mRunnable).start();}stopSelf();// 发送广播后,停止自己,优化资源}private Runnable mRunnable = new Runnable() {@Overridepublic void run() {// 在此执行具体更新天气操作// 取出本地数据库城市天气中的城市,一个一个地查询保存// 完毕后,通知城市管理界面和主界面,自动刷新// 数据库查询数据--城市天气集合// 创建dao对象cityWeatherDao = new CityWeatherDao(getApplicationContext());CityWeatherList = cityWeatherDao.query();// 判断城市列表是否为空,如果是取消本次更新for (int i = 0; i < CityWeatherList.size(); i++) {// 取出第i个城市,获取最新天气String city = CityWeatherList.get(i).getCity();Log.d(TAG, "开始更新第"+i+"个城市: "+city);try {city = URLEncoder.encode(city, "utf-8");String url = "http://apicloud.mob.com/v1/weather/query?key=1c050b9935858&city=" + city;URL httpUrl;httpUrl = new URL(url);HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();conn.setRequestMethod("GET");conn.setReadTimeout(5000);BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));String str1;StringBuffer sb = new StringBuffer();while ((str1 = reader.readLine()) != null) {sb.append(str1);}Gson gson = new Gson();WeatherDataBean winfo = gson.fromJson(sb.toString(), WeatherDataBean.class);initCityListData(winfo);} catch (Exception e) {}}// 完毕后,通知城市管理界面和主界面,自动刷新Log.d(TAG, "所有城市天气更新完毕");Intent intent = new Intent("android.intent.action.MY_BROADCAST");intent.putExtra("msg", "1");sendBroadcast(intent);}};


我们可以看到在子线程中,当本地数据库城市天气更新完毕后,我发送了一个广播,它的作用就是去通知CityActivity和HomeActivity,这样它们就可以更新界面数据了。
3.2.3 我们先看CityActivity(城市列表界面),我们要在它里面写一个广播接收器,这样才能收到UpdateCityWeatherService中的广播。
首先我们要在onCreate方法里动态注册广播接收器,代码如下:
// 在发送之前,确定在代码的某个位置已经动态注册MyReceiver receiver = new MyReceiver();IntentFilter filter = new IntentFilter();filter.addAction("android.intent.action.MY_BROADCAST");registerReceiver(receiver, filter);

动态注册的程序只有在程序运行时才会收到广播消息,程序不运行了,它就收不到了。因为只有在app运行时,只有在当前一直停留在CityActivity页面时,我们才需要主动刷新
城市管理页面数据,至于你从其他页面切换到CityActivity时,程序会自己从本地数据库加载已经更新好的数据去显示,不需要我们去干预。

3.2.4 然后我们在CityActivity中写一个内部类MyReceiver,代码如下:

public class MyReceiver extends BroadcastReceiver      {          @Override          public void onReceive(Context context, Intent intent)          {              if (intent.getAction().equals("android.intent.action.MY_BROADCAST"))              {              String msg = intent.getStringExtra("msg");             //Log.i(tag, "收到广播测试"+msg);            if(msg.equals("1")){            //收到广播指令后,重新从数据库加载页面数据            //Log.i(tag, "收到广播测试"+msg);            initCityListData();            }            }          }      }  

它收到从UpdateCityWeatherService传来的广播,匹配后,就执行initCityListData()来更新CityActivity页面数据。
        3.2.5同理,HomeActivity也一样,这里我就不写了,文章末尾会给大家提供我的源码。

四、注意


我在上面有段标红的文字说“当app退出时,定时更新在某些情况下会失效”,下面我就详细给大家说说:
     4.1 情景一(测试机:三星   型号:GT-S7898  Android版本4.1.2):
手机上安装“净化大师”的时候,“净化大师”会默认将你的app纳入它的净化范围(它的
“净化设置”->“系统净化”会自动把你app设置为“防止自启和相互唤醒”,“智能回收后台进程”)。
此时我们运行APP:

图4.1  APP处于运行状态(定时更新运行正常)

图4.2 APP退出,被关闭(发现从23:22:01到23:25及其以后的时间,没有执行定时更新)


当我们在“净化大师”去掉对该APP的设置后,允许其自启,再次运行APP并退出程序,见图4.3


图4.3 打开APP再退出(因为你要运行启动页Activity才能启动定时更新)


会发现,此时当退出APP时,定时更新仍然有效。
     4.2 情景二(测试机:贝尔丰Bifer   型号BF_T13)
该测试机有个自带的“安全中心”,在里面有“手机加速”->“受保护应用”“自启动管理”将你
当前APP勾选住,运行启动页后再退出APP,发现定时更新仍然可用。反之,在“安全中心”取消刚才对APP的设置,你会
发现在退出APP后,定时更新不可用。
     总结,如果你手机出现上述该类情况,使得定时更新不可用,就看看是哪个应用限制了APP的后台运行。

五、源码



5.1 为了方便大家能更好的理解,我将我正在做的毕业设计源代码提供给大家,仅供学习参考。源码地址:点击打开链接(即
http://download.csdn.net/download/qq_24933841/9828926)
     5.2 我也是个小白,可能文章讲的不是那么好,欢迎大家批评指正。


0 0
原创粉丝点击