Проблема с виджетами: при выходе из основного приложения сбрасываются статические переменные в виджете - PullRequest
0 голосов
/ 10 июня 2018

У меня есть виджет с кнопкой.В моем виджете приложения (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>

Ответы [ 2 ]

0 голосов
/ 11 июня 2018

Если принудительно остановить приложение или система решит остановить его из-за нехватки памяти, все классы будут удалены из памяти, а все статические переменные будут потеряны.В этом случае вам следует либо сохранить ваши данные в БД или SharedPreferences, либо аналогично, либо переосмыслить сам подход.Неужели нужно делать так, как ты здесь делаешь?

0 голосов
/ 10 июня 2018

В моем виджете приложения (TestWidget.java) у меня есть частная статическая логическая переменная (buttonClicked), инициализированная как false

static переменные в лучшем случае являются кэшем.

Тогда я прекращаю свое основное приложение.

Я не знаю точно, что вы подразумеваете под этим.Я предполагаю, что вы имеете в виду, что вы удаляете задачу, связанную с вашим приложением, с экрана обзора.

В следующем onUpdates значение buttonClicked показывается как false.Почему это так?

Если «остановить мое основное приложение», вы сделаете что-то, как я обрисовал, вы прекратите свой процесс.Позже, например, когда вы нажмете кнопку виджета вашего приложения, Android запустит новый процесс для вас, в этот момент ваше поле static, в котором ваше статическое поле будет значением по умолчанию.

Я бы подумал, что виджеты имеют собственную копию памяти, так как они продолжают работать, даже если основное приложение завершилось?

Нет.Представления, связанные с вашим виджетом приложения, будут отображаться на главном экране.Сюда не входит ваш код TestWidget, который не является частью главного экрана.

Я собираюсь отказаться от этого подхода, чтобы создать alarmManager с небольшим временем обновления (10 секунд).

Во-первых, это невозможно на Android 5.1 и выше, так как это существенно разряжает батарею.Во-вторых, это не гарантирует, что ваш процесс останется без изменений.

static переменные в лучшем случае являются кешем.Любое приложение, в том числе с виджетом приложения, должно хранить важные данные где-то еще: SharedPreferences, база данных SQLite, какой-либо другой тип файла, сервер и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...