How to really update the widget every minute

I'm at a dead end. I know that this question has already been answered a hundred times, but nothing I tried works.

My question is: I made an Android widget that needs to be updated exactly every minute, like all clock widgets. (This widget tells me how many minutes are left before my train, one minute makes it useless).

Here are my attempts far and the corresponding results:

  • I put android:updatePeriodMillis="60000"in mine appwidget_info.xml. However, as stated in the API Docs , "Updates requested with help updatePeriodMilliswill be sent no more than once every 30 minutes," and it is true that how often my widget is updated.
  • I tried to use AlarmManager. In my WidgetProvider.onEnabled:

    AlarmManager am = (AlarmManager)context.getSystemService
            (Context.ALARM_SERVICE);
    Calendar calendar = Calendar.getInstance();
    long now = System.currentTimeMillis();
    // start at the next minute
    calendar.setTimeInMillis(now + 60000 - (now % 60000));
    am.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), 60000,
            createUpdateIntent(context));
    

    , API docs, " API 19 ", .

  • , targetSdkVersion 18 ( ).
  • setRepeating, -, setExact. . :

    Calendar calendar = Calendar.getInstance();
    long now = System.currentTimeMillis();
    long delta = 60000 - (now % 60000);
    
    Log.d(LOG_TAG, "Scheduling another update in "+ (delta/1000) +" seconds");
    calendar.setTimeInMillis(now + delta);
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    alarmManager.setExact(AlarmManager.RTC, calendar.getTimeInMillis(), //UPDATE_PERIOD_SECONDS * 1000,
            createUpdateIntent(context));
    

    , ( ). , :

    • 21: 44: 17,962
    • 21: 52: 37,232
    • 21: 59: 13,872
    • 22: 00: 00.012 ← ??
    • 22: 01: 47,352
    • 22: 02: 25,132
    • 22: 06: 56,202
  • Handler. a Service, , , :

    int delay = (int)(60000 - (System.currentTimeMillis() % 60000));
    Log.d(LOG_TAG, "Scheduling another update in " + delay/1000 + " seconds");
    new Handler().postDelayed(new Runnable() {
        public void run() {
            Log.d(LOG_TAG, "Scheduled update running");
            updateAppWidget();
        }
    }, delay);
    

    , " HUGE delay". , - .

, : (, , . , ). - , .

Timers BroadcastReceiver, , , " ", , . , , , Handler s.

, AlarmManager , ( , , ), , , , ...

Handler , , AlarmManager , , , AlarmManager , Handler . - , Android , , - . , .

EDIT: , Samsung Galaxy Note 4 (2016-06-01) Android 6.0.1.

+4
3

, , . , , , , , , .

.

public static final String ACTION_TICK = "CLOCK_TICK";
public static final String SETTINGS_CHANGED = "SETTINGS_CHANGED";
public static final String JOB_TICK = "JOB_CLOCK_TICK";

 @Override
    public void onReceive(Context context, Intent intent){
        super.onReceive(context, intent);

        preferences =  PreferenceManager.getDefaultSharedPreferences(context);

        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        ComponentName thisAppWidget = new ComponentName(context.getPackageName(), WidgetProvider.class.getName());
        int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);

        if (intent.getAction().equals(SETTINGS_CHANGED)) {
            onUpdate(context, appWidgetManager, appWidgetIds);
            if (appWidgetIds.length > 0) {
                restartAll(context);
            }
        }

        if (intent.getAction().equals(JOB_TICK) || intent.getAction().equals(ACTION_TICK) ||
                intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
                || intent.getAction().equals(Intent.ACTION_DATE_CHANGED)
                || intent.getAction().equals(Intent.ACTION_TIME_CHANGED)
                || intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
            restartAll(context);
            onUpdate(context, appWidgetManager, appWidgetIds);
        }
    }

private void restartAll(Context context){
        Intent serviceBG = new Intent(context.getApplicationContext(), WidgetBackgroundService.class);
        context.getApplicationContext().startService(serviceBG);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            scheduleJob(context);
        } else {
            AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
            appWidgetAlarm.startAlarm();
        }
    }



 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void scheduleJob(Context context) {
        ComponentName serviceComponent = new ComponentName(context.getPackageName(), RepeatingJob.class.getName());
        JobInfo.Builder builder = new JobInfo.Builder(0, serviceComponent);
        builder.setPersisted(true);
        builder.setPeriodic(600000);
        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
        int jobResult = jobScheduler.schedule(builder.build());
        if (jobResult == JobScheduler.RESULT_SUCCESS){
        }
    }


    @Override
    public void onEnabled(Context context){

        restartAll(context);
    }


    @Override
    public void onDisabled(Context context){

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
            jobScheduler.cancelAll();
        } else {
            // stop alarm
            AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
            appWidgetAlarm.stopAlarm();
        }

        Intent serviceBG = new Intent(context.getApplicationContext(), WidgetBackgroundService.class);
        serviceBG.putExtra("SHUTDOWN", true);
        context.getApplicationContext().startService(serviceBG);
        context.getApplicationContext().stopService(serviceBG);
    }

WidgetBackgroundService

public class WidgetBackgroundService extends Service {

        private static final String TAG = "WidgetBackground";
        private static BroadcastReceiver mMinuteTickReceiver;

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

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



        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            if(intent != null) {
                if (intent.hasExtra("SHUTDOWN")) {
                    if (intent.getBooleanExtra("SHUTDOWN", false)) {

                        if(mMinuteTickReceiver!=null) {
                            unregisterReceiver(mMinuteTickReceiver);
                            mMinuteTickReceiver = null;
                        }
                        stopSelf();
                        return START_NOT_STICKY;
                    }
                }
            }

            if(mMinuteTickReceiver==null) {
                registerOnTickReceiver();
            }
            // We want this service to continue running until it is explicitly
            // stopped, so return sticky.
            return START_STICKY;
        }

        @Override
        public void onDestroy(){
            if(mMinuteTickReceiver!=null) {
                unregisterReceiver(mMinuteTickReceiver);
                mMinuteTickReceiver = null;
            }

            super.onDestroy();
        }

        private void registerOnTickReceiver() {
            mMinuteTickReceiver = new BroadcastReceiver(){
                @Override
                public void onReceive(Context context, Intent intent){
                    Intent timeTick=new Intent(WidgetProvider.ACTION_TICK);
                    sendBroadcast(timeTick);
                }
            };
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_TIME_TICK);
            filter.addAction(Intent.ACTION_SCREEN_ON);
            registerReceiver(mMinuteTickReceiver, filter);
        }
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class RepeatingJob extends JobService {
    private final static String TAG = "RepeatingJob";

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(TAG, "onStartJob");
        Intent intent=new Intent(WidgetProvider.JOB_TICK);
        sendBroadcast(intent);
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

AppWidgetAlarm

public class AppWidgetAlarm {
    private static final String TAG = "AppWidgetAlarm";

    private final int ALARM_ID = 0;
    private static final int INTERVAL_MILLIS = 240000;
    private Context mContext;


    public AppWidgetAlarm(Context context){
        mContext = context;
    }


    public void startAlarm() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MILLISECOND, INTERVAL_MILLIS);
        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        Intent alarmIntent = new Intent(WidgetProvider.ACTION_TICK);
        PendingIntent removedIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        Log.d(TAG, "StartAlarm");
        alarmManager.cancel(removedIntent);
        // needs RTC_WAKEUP to wake the device
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), INTERVAL_MILLIS, pendingIntent);
    }

    public void stopAlarm()
    {
        Log.d(TAG, "StopAlarm");

        Intent alarmIntent = new Intent(WidgetProvider.ACTION_TICK);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);

        AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(pendingIntent);
    }
}

<receiver android:name=".services.SlowWidgetProvider" >
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <intent-filter>
        <action android:name="CLOCK_TICK" />
    </intent-filter>
    <intent-filter>
        <action android:name="JOB_CLOCK_TICK" />
    </intent-filter>
    <intent-filter>
        <action android:name="SETTINGS_CHANGED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.TIME_SET" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.TIMEZONE_CHANGED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.DATE_CHANGED" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.os.action.DEVICE_IDLE_MODE_CHANGED"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.ACTION_DREAMING_STOPPED" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
        android:resource="@xml/slow_widget_info" />
</receiver>

<service
    android:name=".services.RepeatingJob"
    android:permission="android.permission.BIND_JOB_SERVICE"
    android:exported="true"/>

<service android:name=".services.WidgetBackgroundService" />
+3

runnable. postDelayed.

, runnable .

Edit:

, OP , . :

  • ONE.
  • , , / .
0

Try this code

    Intent intent = new Intent(ACTION_AUTO_UPDATE_WIDGET);
    PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 1);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);

    AlarmManager alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    alarmMgr.setInexactRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), 60 * 1000, alarmIntent);
0
source

Source: https://habr.com/ru/post/1649653/


All Articles