小部件问题:退出主应用程序会重置小部件中的静态变量

Widget issue: Exiting the main app resets the static variables in the widget

我有一个带按钮的小部件。在我的应用程序小部件 (TestWidget.java) 中,我有一个私有静态布尔变量 (buttonClicked) 初始化为 false。

当我点击按钮时,布尔值 buttonClicked 设置为 true。我将 updatePeriodMillis 设置为最小 30 分钟(1800000 毫秒)。

第一个 onUpdate 来了:buttonClicked 值为 true。不出所料。

然后我停止我的主应用程序。以下 onUpdates 将 buttonClicked 值显示为 false。

launch --> 06-10 10:55:56.365 4186-4186/com.narb.testwidget I/TESTWID: update setButtonClicked false
1st onUpdate after button click --> 06-10 10:56:11.685 4186-4186/com.narb.testwidget I/TESTWID: setButtonClicked true
onUpdate after main app exit --> I/TESTWID: update setButtonClicked false

这是为什么?

应用小部件 - TestWidget

public class TestWidget extends AppWidgetProvider {
    private static RemoteViews views;
    private static boolean buttonClicked = false;

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId) {
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Get all ids
        ComponentName thisWidget = new ComponentName(context,
                TestWidget.class);
        int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

        Log.i("TESTWID", "update setButtonClicked "+buttonClicked);

        views = new RemoteViews(context.getPackageName(), R.layout.test_widget);
        views.setOnClickPendingIntent(R.id.wid_btn_tst, setButton(context));

        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    public static void setButtonClicked(boolean b){
        buttonClicked = b;
        Log.i("TESTWID", "setButtonClicked "+buttonClicked);
    }

    public static PendingIntent setButton(Context context) {
        Intent intent = new Intent();
        intent.setAction("TEST");
        return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }

    public static void pushWidgetUpdate(Context context, RemoteViews remoteViews) {
        ComponentName myWidget = new ComponentName(context, TestWidget.class);
        AppWidgetManager manager = AppWidgetManager.getInstance(context);
        manager.updateAppWidget(myWidget, remoteViews);
    }

}

按钮代码 - TestWidgetReceiver

public class TestWidgetReceiver extends BroadcastReceiver{
    private static boolean isButtonON = false;

    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction().equals("TEST")){
            updateWidgetButton(context, 2);
        }
    }

    private void updateWidgetButton(Context context, int index) {
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.test_widget);
        if(index == 2) {
            if(isButtonON) {
                remoteViews.setTextViewText(R.id.wid_btn_tst, "Test Off");
                isButtonON = false;
            }
            else{
                remoteViews.setTextViewText(R.id.wid_btn_tst, "Test On");
                isButtonON = true;
                TestWidget.setButtonClicked(isButtonON);
            }
            //REMEMBER TO ALWAYS REFRESH YOUR BUTTON CLICK LISTENERS!!!
            remoteViews.setOnClickPendingIntent(R.id.wid_btn_tst, TestWidget.setButton(context));
        }

        TestWidget.pushWidgetUpdate(context.getApplicationContext(), remoteViews);
    }

}

清单

   <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="Test"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".TestWidget">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

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

        <receiver
            android:name=".TestWidgetReceiver"
            android:label="widgetBroadcastReceiver" >
            <intent-filter>
                <action android:name="TEST" />
            </intent-filter>

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

    </application>

In my app widget (TestWidget.java) I have a private static boolean variable (buttonClicked) initialized to false

static 变量充其量只是一个缓存。

Then I stop my main app.

我不明白你的意思。我的猜测是您的意思是您从概览屏幕中删除了与您的应用关联的任务。

The following onUpdates shows buttonClicked value as false. Why is that?

如果到 "stop my main app",您按照我概述的方式进行操作,您将终止您的进程。稍后,例如当您单击应用程序小部件的按钮时,Android 将为您启动一个新的过程,此时您的 static 字段,此时您的静态字段将是其默认值.

I would have thought that widgets have their own memory copy as they continue to run even if the main app has exited?

没有。与您的应用程序小部件关联的视图将存在于主屏幕中。这不包括您的 TestWidget 代码,它不是主屏幕的一部分。

I'm considering dropping this approach to do an alarmManager with a low refresh time (10 secs).

首先,这在 Android 5.1 及更高版本上是不可能的,因为它会显着耗尽电池电量。其次,它不保证你的进程会一直存在。

static 变量充其量只是一个缓存。任何应用程序,包括那些带有应用程序小部件的应用程序,都需要将重要数据存储在其他地方:SharedPreferences、SQLite 数据库、某种其他类型的文件、服务器等。

如果您强制停止应用程序或系统因内存不足决定停止它,所有 类 将从内存中删除,所有静态变量将丢失。在这种情况下,您应该将数据保存到 DB 或 SharedPreferences 或类似的,或者重新考虑方法本身。真的有必要像你这里那样做吗?