Workaround for AppWidgetProvider onEnabled & onDisabled not called

Since I use the AlarmManagerwidget to periodically update, I need to make sure that onEnabledthey onDisabledwill work reliably.

However, I understand that they sometimes do not work. I am not the only one facing this problem.

Android appWidgetProvider onEnabled is never called on a tablet

  • Is there an official bug sent to the Google Android team?
  • Is there a workaround, especially onDisabled? Since I don’t want the AlarmManager to still run repeatedly after removing the last widget.

AndroidManifest.xml

    <receiver android:name="org.yccheok.MyAppWidgetProvider"
        android:exported="true" >
        <intent-filter >
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            <action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
            <action android:name="android.appwidget.action.APPWIDGET_DELETED" />
            <action android:name="android.appwidget.action.APPWIDGET_DISABLED" />
        </intent-filter>

        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/widget_info" />
    </receiver>

public class MyAppWidgetProvider extends AppWidgetProvider {

    private static PendingIntent createAlarmUpdatePendingIntent(Context context) {
        Intent intent = new Intent(context, JStockAppWidgetProvider.class);
        intent.setAction(JStockAppWidgetProvider.ALARM_UPDATE_ACTION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        return pendingIntent;
    }

    @Override
    public void onEnabled(Context context)
    {        
        super.onEnabled(context);

        PendingIntent pendingIntent = createAlarmUpdatePendingIntent(context);
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        int scanSpeed = JStockApplication.instance().getJStockOptions().getScanSpeed();
        alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis() + scanSpeed, scanSpeed, pendingIntent);
    }

    @Override
    public void onDisabled(Context context)
    {
        super.onDisabled(context);

        PendingIntent pendingIntent = createAlarmUpdatePendingIntent(context);
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        alarmManager.cancel(pendingIntent);
    }
+4
source share
4

, .

private boolean hasInstances(Context context) {
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
            new ComponentName(context, this.getClass()));
    return (appWidgetIds.length > 0);
}
+1

, AppWidgetProvider.

onDeleted(Context context, int[] appWidgetIds)
    Called in response to the ACTION_APPWIDGET_DELETED broadcast **when one or more** AppWidget instances have been deleted. Override this method to implement your own AppWidget functionality.

onDisabled(Context context)
    Called in response to the ACTION_APPWIDGET_DISABLED broadcast, which is sent when the last AppWidget instance for this provider is deleted. Override this method to implement your own AppWidget functionality.

, AppWidget, , onDeleted() . AppWidget, , onDesabled() onDeleted(), .


, onDesabled() onDeleted(), .!

, onEnabled() , .

+1

, , , , . , , . BroadcastReceiver.

onUpdate, onEnable ( ). , .

AppWidgetProvider:

public class MyAppWidgetProvider extends AppWidgetProvider {
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
              int[] appWidgetIds)
    {
        ComponentName thisWidget = new ComponentName(context, MyAppWidgetProvider.class);
        int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

        Intent intent = new Intent(context.getApplicationContext(), JStockAppWidgetService.class);
        intent.setAction(JStockAppWidgetService.ALARM_UPDATE_ACTION);

        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, allWidgetIds);

        context.startService(intent);
    }

    @Override
    public void onDisabled(Context context)
    {
        super.onDisabled(context);

        Intent intent = new Intent(context.getApplicationContext(), JStockAppWidgetService.class);
        intent.setAction(JStockAppWidgetService.ALARM_STOP_ACTION);
        // I kept this just in case you wanna keep running your alarm without widget. You can just stopService here too.
        context.startService(intent);
    }
}

:

public class JStockAppWidgetService extends Service {

    public static final String ALARM_UPDATE_ACTION = "ALARM_UPDATE_ACTION";
    public static final String ALARM_STOP_ACTION = "ALARM_STOP_ACTION";

    //delay to refresh your widget
    private int delay = 10000; //10 secs

    private Thread myThread;

    @Override
    public void onStart(Intent intent, int startId) {
        final int[] allWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);

        final Handler handler = new Handler();
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                try {
                    //running your timeout while is not interrupted
                    while(!Thread.currentThread().isInterrupted()) {
                        //we need to back to GUI thread
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                runInMyGuiThread(allWidgetIds);
                            }
                        });
                        //everybody needs to sleep sometime =p
                        Thread.sleep(delay);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                }
            }
        };

        String action = intent.getAction();
        if(action == ALARM_UPDATE_ACTION) {
            if(myThread != null)
                myThread.interrupt();
            myThread = new Thread(runnable);
            myThread.start();
        } else if(action == ALARM_STOP_ACTION && myThread != null) {
            myThread.interrupt();
        }       
    }

    //here you are in GUI thread with all your widgets id
    public void runInMyGuiThread(int[] allWidgetIds) {
        for(int widgetId : allWidgetIds){
            //do what you want to update each widget
        }
    }

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

}

, , .

+1

.

, and then test by adding and removing the widget to the main screen You will never see onEnabled / onDisabled called. The reason is that a widget has been added so far - a screen lock widget.

0
source

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


All Articles