AndroidのAppWidgetでupdatePeriodMillisを使わずに回す
ほどよくまとまった情報がなかったので自分用メモ。
参考ページ
Build an App Widget | Android Developers
Developer.com: Your Home for Java and Open Source Development Knowledge - Developer.com
AndroidのappWidget作成時に行っておくべき消費電力対策についてのまとめ(その1) - 闘争より逃走したい日記
(2番目の内容が、Androidのホーム画面に常駐するアプリを作るには (1/3):Androidで動く携帯Javaアプリ作成入門(10) - @ITとかぶってる気がするんだけど、翻訳? パクリ?)
updatePeriodMillisを使うと、
- スリープ時でもスリープしてくれない
- ユーザが更新間隔を変更することができない
などのデメリットがあるのですが、これをAlarmManagerでやろうとしたらちょっと複雑になったのでメモ。(Serviceでやらない理由は3番目の記事に)
メモと言うかスニペットですが。
(追記)
以下の方法だと、スリープしていてもAlarmManagerが止まってくれないっぽい…。調べてみても分からなかったので、ACTION_SCREEN_ON/OFFを捕まえることに
(再追記)
あれ…ACTION_SCREEN_ON/OFFは捕まえられないのか…。30分未満で更新するWidgetは止めとけってことなのかどうなのか。2.1以降ならPowerManager#isScreenOnが使えるということですが…
デフォルトのAnalogClockのソースをざっと見てみたけど、1分毎にViewをちょこっと更新するだけならぶん回しても問題ない、ということかな。眠い
public class FooWidgetProvider extends AppWidgetProvider { public static final String URI_SCHEME = "foowidget"; private Intent buildAlarmIntent( Context context , int appWidgetId ) { Intent intent = new Intent( context , CurrencyWidgetProvider.class ); intent.setAction( AppWidgetManager.ACTION_APPWIDGET_UPDATE ); intent.putExtra( AppWidgetManager.EXTRA_APPWIDGET_ID , appWidgetId ); intent.setData( Uri.parse( URI_SCHEME + "://update/" + appWidgetId ) ); return intent; } private void deleteAlarm( Context context , Intent intent ) { int appWidgetId = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID , AppWidgetManager.INVALID_APPWIDGET_ID ); if ( appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID ) { AlarmManager alarmManager = ( AlarmManager ) context.getSystemService( Context.ALARM_SERVICE ); alarmManager.cancel( PendingIntent.getBroadcast( context , 0 , buildAlarmIntent( context , appWidgetId ) , PendingIntent.FLAG_UPDATE_CURRENT ) ); /* 設定の削除など */ } } private void doProc( Context context , Intent intent ) { int appWidgetId = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID , -1 ); /* メインの処理など */ } @Override public void onReceive( Context context , Intent intent ) { if ( AppWidgetManager.ACTION_APPWIDGET_DELETED.equals( intent.getAction() ) ) { deleteAlarm( context , intent ); } else if ( AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals( intent.getAction() ) ) { if ( !URI_SCHEME.equals( intent.getScheme() ) ) { setAlarm( context , intent ); } else { doProc( context , intent ); } } super.onReceive( context , intent ); } private void setAlarm( Context context , Intent intent ) { for ( int appWidgetId : intent.getExtras().getIntArray( AppWidgetManager.EXTRA_APPWIDGET_IDS ) ) { /* 設定の読み込みなど */ long interval = anyValueYouWant; if ( interval != -1 ) { AlarmManager alarmManager = ( AlarmManager ) context.getSystemService( Context.ALARM_SERVICE ); alarmManager.setRepeating( AlarmManager.RTC , System.currentTimeMillis() , interval * 1000 , PendingIntent.getBroadcast( context , 0 , buildAlarmIntent( context , appWidgetId ) , PendingIntent.FLAG_UPDATE_CURRENT ) ); } } } }
とこんな感じになりました。無理にonUpdateとかonDeleteとか使わない方がすっきりするんじゃないかなー。
初回はconfigureなActivityから呼び出すのですが、そこは
Intent updateIntent = new Intent( FooWidgetConfigActivity.this , FooWidgetProvider.class ); updateIntent.setAction( AppWidgetManager.ACTION_APPWIDGET_UPDATE ); updateIntent.putExtra( AppWidgetManager.EXTRA_APPWIDGET_IDS , new int[] { _appWidgetId } ); sendBroadcast( updateIntent );
こんな感じ。