Android AppWidgetProvider的解决方案';s onEnabled&;onDisabled没有被调用

Android AppWidgetProvider的解决方案';s onEnabled&;onDisabled没有被调用,android,android-widget,Android,Android Widget,由于我正在使用AlarmManager执行定期小部件更新,因此我需要确保onEnabled和onDisabled能够可靠工作 然而,我意识到它们有时不会被触发。我不是唯一一个面临这个问题的人 谷歌安卓团队有没有收到任何官方的错误通知单 是否有任何解决方法,尤其是禁用的?因为我不希望在移除最后一个小部件后,AlarmManager仍被重复触发 AndroidManifest.xml 您可以签入当前正在运行的多个实例 private boolean hasInstances(Context con

由于我正在使用
AlarmManager
执行定期小部件更新,因此我需要确保
onEnabled
onDisabled
能够可靠工作

然而,我意识到它们有时不会被触发。我不是唯一一个面临这个问题的人

  • 谷歌安卓团队有没有收到任何官方的错误通知单
  • 是否有任何解决方法,尤其是禁用的
    ?因为我不希望在移除最后一个小部件后,AlarmManager仍被重复触发
  • AndroidManifest.xml
    您可以签入当前正在运行的多个实例

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

    恐怕您误解了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()
    将只为第一个实例调用,而不是为您创建的每个下一个实例调用。

    据我所知,您只需要一个警报,我想所有小部件都是一样的。所以,您的闹钟是在服务上运行的,而不是在小部件上运行的。 您不应该在上运行长时间操作

    我们将使用onUpdate和notonenable(因为这个演示的设计)。然后,当我们得到一个新的小部件时,我们就可以执行服务了

    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;
        }
    
    }
    

    这只是一个小演示,您可以停止服务或在没有小部件的情况下让它运行,我将告诉您。

    我被以下情况弄糊涂了


    如果您已将小部件添加到锁定屏幕,然后通过将小部件添加和删除到主屏幕进行测试,则您永远不会看到调用onEnabled/onDisabled。原因是仍然添加了一个小部件-锁屏小部件。

    这是“按设计”。请参阅,恐怕这不足以可靠地处理该问题。我意识到,有时appWidgetManager的
    getAppWidgetIds
    将返回已从主屏幕中删除的小部件Id。此解决方案已关闭。但是,它不会工作,因为它被这个bug阻止了:但是,因为这是我们能得到的最接近的答案,我将奖励这个答案。我的意图是为所有小部件实例都有一个报警管理器。然后在你上面的代码中,它只被调用一次。onEnabled()仅在第一个实例中调用,onDisabled()也被调用一次。所以你想要的已经被你完成了@切延成诺。问题是,
    onDisabled
    将不被称为可靠性。有时它确实会打电话。有时不会。根据我的说法,
    onDisabled
    会被调用,并且只有当有一个小部件实例并且您试图删除它时才会调用它。根据我在Nexus Android 4.1.2上的测试,我可以确认
    onDisabled
    不会总是被调用,即使我从屏幕上删除了小部件的最后一个实例。
    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;
        }
    
    }