Регулярное обновление значения TextView в виджете Андроида, раз в несколько секунд

Подскажите, пожалуйста, как корректно реализовать обновление (присвоение нового значения) TextView для виджета в Android с интервалом раз в несколько секунд? Встроенный параметр updatePeriodMillis не подходит из-за ограничения обновления виджетов раз в 30 минут. Необходимо чтобы текст в виджете менялся каждые несколько секунд.

Как осуществить эту задачу корректно и оптимально: чтобы минимально расходовать батарею, чтобы обновление по таймеру работало только при видимом виджете на активном и включенном экране, а в остальных случаях (работающее приложение, выключенный или заблокированный экран, открытые настройки и т.п.) - обновления виджета отключались?

Имеется простой NewAppWidget виджет, созданный Android Studio по-умолчанию:

package com.dev.app;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.widget.RemoteViews;

/**
 * Implementation of App Widget functionality.
 */
public class NewAppWidget extends AppWidgetProvider {

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId) {

        CharSequence widgetText = WidgedDataGenerator.getWidgetString();
        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.new_app_widget);
        views.setTextViewText(R.id.appwidget_text, widgetText);

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    @Override
    public void onEnabled(Context context) {
        // Enter relevant functionality for when the first widget is created
    }

    @Override
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled
    }
}

Новые значения генерируется через WidgedDataGenerator.getWidgetString(). Необходимо, чтобы обновление текста в TextView происходило каждые 2 или 20 секунд (в зависимости от значения в настройках приложения), при этом потребляя минимум ресурсов.


Ответы (2 шт):

Автор решения: Deniska SosiSka

Вы можете рассмотреть вариант с использованием UsageStatsManager, через него можно получать статистику использования телефона и в зависимости от неё, включать/выключать обновление виджета.


Но в целом проще использовать (больше информации в сети) вариант с BroadcastReceiver для перехвата событий включения и выключения экрана, Handler и Runnable для обновления виджета.

Следующий код собран из открытых примеров и служит только примерной демонстрацией, возможно в нём содержаться ошибки:

public class ScreenReceiver extends BroadcastReceiver {

    private static boolean isScreenOn = false;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            isScreenOn = true;
            startWidgetUpdater(context);
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            isScreenOn = false;
        }
    }

    public static boolean isScreenOn() {
        return isScreenOn;
    }

    private void startWidgetUpdater(Context context) {
        Intent intent = new Intent(context, WidgetUpdaterService.class);
        context.startService(intent);
    }
}
public class WidgetUpdaterService extends Service {

    private static final int UPDATE_INTERVAL = 2000; // 2 секунды

    private Handler mHandler = new Handler();
    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            if (ScreenReceiver.isScreenOn()) {
                AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getApplicationContext());
                int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(getApplicationContext(), NewAppWidget.class));
                for (int appWidgetId : appWidgetIds) {
                    NewAppWidget.updateAppWidget(getApplicationContext(), appWidgetManager, appWidgetId);
                }
                mHandler.postDelayed(this, UPDATE_INTERVAL);
            }
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mHandler.post(mRunnable);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacks(mRunnable);
    }
}
  • Регистрируем ресивер и сервис:
<receiver android:name=".ScreenReceiver">
    <intent-filter>
        <action android:name="android.intent.action.SCREEN_ON"/>
        <action android:name="android.intent.action.SCREEN_OFF"/>
    </intent-filter>
</receiver>

<service android:name=".WidgetUpdaterService"/>
  • Добавим запуск сервиса при создании виджета:
public class NewAppWidget extends AppWidgetProvider {

    @Override
    public void onEnabled(Context context) {
        super.onEnabled(context);
        context.startService(new Intent(context, WidgetUpdaterService.class));
    }

    // ...
}
→ Ссылка
Автор решения: Falchio

Есть видео на YouTube с вполне рабочим приложением. К сожалению, автор не очень многословен. Но есть ссылка на исходники widget update.

Мне не удалось заставить обновляться виджет каждую секунду, но раз в две секунды он обновляется или около того.

Для обновления виджета используется AlarmManager.

→ Ссылка