关于桌面widget的动态刷新有这样一个配置,
在res/xml下<appwiget-provider>下有一个属性
android:updatePeriodMillis="86400000"
官方的注释是:
The updatePeriodMillis attribute defines how often the App Widget framework should request an update from the AppWidgetProvider by calling the onUpdate() callback method. The actual update is not guaranteed to occur exactly on time with this value and we suggest updating as infrequently as possible—perhaps no more than once an hour to conserve the battery. You might also allow the user to adjust the frequency in a configuration—some people might want a stock ticker to update every 15 minutes, or maybe only four times a day.
中文翻译:
updatePeriodMillis这个属性规定了AppWiget框架应该多久发送一次请求通过调用AppwidgetProvider的onUpdate()方法来更新,实际的更新并不能保证根据这个值准时的更新,我们建议尽可能频率少的进行更新操作,最好不要超过1个小时更新一次,为了保存电池的电量。你页可以给出配置允许用户来调整更新的频率,就像一些人希望股票行情追踪器每15分钟更新一次,或者仅仅一天更新4次
Note: If the device is asleep when it is time for an update (as defined by updatePeriodMillis), then the device will wake up in order to perform the update. If you don't update more than once per hour, this probably won't cause significant problems for the battery life. If, however, you need to update more frequently and/or you do not need to update while the device is asleep, then you can instead perform updates based on an alarm that will not wake the device. To do so, set an alarm with an Intent that your AppWidgetProvider receives, using the AlarmManager. Set the alarm type to either ELAPSED_REALTIME or RTC, which will only deliver the alarm when the device is awake. Then set updatePeriodMillis to zero ("0").中文翻译:
如果屏幕是睡眠的,用updatePeriodMillis更新widget时,会唤醒屏幕。如果你不是每一次小时更新一次,那么你就有可能为电池的待机时间带来严重的影响。然而,如果你需要频繁的进行更新操作,但是或许你并不需要更新时唤醒屏幕,那么你可以在alarm的基础上进行更新操作而不用唤醒设备。像这样做,用AlarmManager设置一个携带intent的闹钟让你的AppwidgetProvider接受更新操作。将alarm的类型设置为ELAPSED_REALTIME或者RTC,这样就会仅仅在设备处于唤醒状态下才会传递alarm的intent。然后设置updatePeriodMillis=0.
在实际的操作中会出现一些问题,
1、SDK在1.6以上时,当updatePeriodMillis的设置的值小于半个小时时,就会失效。
2、使用service更新,或者alarm更新时也会出现一些问题
下面说一下我个人的理解,有不对的地方,欢迎提出。
先说appwidget创建的过程。
1、长按桌面会出现一个列表,这个列表应该就是在manifest注册了AppWidgetProvider的应用列表
<receiver android:name="com.test.TestAppWidgetProvider" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/test_appwidget_info" /> </receiver>
2、点击图标时候会触发android.appwidget.action.APPWIDGET_UPDATE这个intent,
AppWidgetProvider的onReceive()方法执行。
然后onUpdate()方法执行,这时会为该appWidget分配id。
然后会调用AppWidgetConfigure这个Activity并且让你配置widget的大小。
<activity android:name=".test.TestAppWidgetConfigure"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> </intent-filter></activity>
然后就是刷新的方法了,我先后试过几个方法都说一说。
1、我最先用的是是在AppWidgetProvider的onUpate方法中用handler刷新,开一个线程10秒一次发消息
这个方法有几个问题:1>应用升级时会发现问题。2>线程会挂掉
2、用service,将appWidgetManager, appWidgetIds传给service然后在service内刷新,结果不行。不知道为什么。
3、通过发android.appwidget.action.APPWIDGET_UPDATE这个广播刷新。用service发或者alarm发,相比较alarm更好,也是官方推荐的。下面就说用alarm做的过程
这个方法刷新时最开始也出现了问题,我一直想的是在onUpdate方法里刷新,但事实是错误的。1>在onUpdate里面创建一个alarm然后每10秒发一次intent,那么每次onUpate刷新时又都会创建一个alarm发intent这样就是出问题了
2>刷新时我都会将这个appwidget的id获取到然后appWidgetManager.updateAppWidget(appWidgetIds[0], views);刷新,结果出现了一个变态的情况我自己的widget和系统的桌面渲染的一个widget来回切换。没搞清楚什么情况。
最后说下我现在在用的方法:
在onUpdate中设置alarm
AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); int requestCode = 0; PendingIntent pendIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); // 5秒后发送广播,然后每个10秒重复发广播。广播都是直接发到AlarmReceiver的 long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000; int interval = 10 * 1000; alarmMgr.setRepeating(AlarmManager.RTC, triggerAtTime, interval, pendIntent);
然后在onReceive中刷新AppWidgetId是从AppWidgetConfigure里面拿的,把mAppWidgetId设为静态的了。
Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) { mAppWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); }
AppWidgetManager.getInstance(context).updateAppWidget(TestAppWidgetConfigure.mAppWidgetId, views);
关键:
看一下TestAppWidgetProvider父类中的onReceive方法,在onUpdate中设置alarm时intent不用带参数,这样每次alarm刷新时只会调用onReceive方法也不会调用onUpate方法
/** * Implements [email protected] BroadcastReceiver#onReceive} to dispatch calls to the various * other methods on AppWidgetProvider. * * @param context The Context in which the receiver is running. * @param intent The Intent being received. */ // BEGIN_INCLUDE(onReceive) public void onReceive(Context context, Intent intent) { // Protect against rogue update broadcasts (not really a security issue, // just filter bad broacasts out so subclasses are less likely to crash). String action = intent.getAction(); if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) { Bundle extras = intent.getExtras(); if (extras != null) { int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS); if (appWidgetIds != null && appWidgetIds.length > 0) { this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds); } } } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) { Bundle extras = intent.getExtras(); if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) { final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID); this.onDeleted(context, new int[] { appWidgetId }); } } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) { this.onEnabled(context); } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) { this.onDisabled(context); } }