Reputation: 988
I have a widget with a button. In my app widget (TestWidget.java) I have a private static boolean variable (buttonClicked) initialized to false.
When I click on the button the boolean buttonClicked is set to true. I have an updatePeriodMillis set to the minimum 30min (1800000ms).
First onUpdate comes: buttonClicked value is true. As expected.
Then I stop my main app. The following onUpdates shows buttonClicked value as 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
Why is that?
App widget - 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);
}
}
Button code - 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);
}
}
Manifest
<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>
Upvotes: 1
Views: 416
Reputation: 14660
If you force stop the app or the system decides to stop it due to low memory, all classes are dropped from the memory and all static variables are lost. In this case, you should either persist your data to the DB or SharedPreferences or similar or rethink the approach itself. Is it really necessary to do it the way you're doing here?
Upvotes: 1
Reputation: 1006664
In my app widget (TestWidget.java) I have a private static boolean variable (buttonClicked) initialized to false
static
variables are a cache, at best.
Then I stop my main app.
I do not know exactly what you mean by that. My guess is that you mean that you remove a task associated with your app from the overview screen.
The following onUpdates shows buttonClicked value as false. Why is that?
If by "stop my main app", you do something like I outlined, you will have terminated your process. At a later point, such as when you click your app widget's button, Android will start a fresh process for you, at which point your static
field, at which point your static field will be its default value.
I would have thought that widgets have their own memory copy as they continue to run even if the main app has exited?
No. The views associated with your app widget will exist, in the home screen. That does not include your TestWidget
code, which is not part of the home screen.
I'm considering dropping this approach to do an alarmManager with a low refresh time (10 secs).
First, that's not possible on Android 5.1 and above, as it substantially drains the battery. Second, it does not guarantee that your process will stay around.
static
variables are a cache, at best. Any app, including those with an app widget, need to store important data somewhere else: SharedPreferences
, SQLite database, some other sort of file, a server, etc.
Upvotes: 2