Android应用小部件实例发送相同的待定意图

时间:2017-01-05 19:34:57

标签: android android-activity android-widget android-service android-pendingintent

我在使用小部件时看到了奇怪的行为。我确信它之前有用,但无论如何,它现在还没有。不知道我改变了什么,或者它确实有效。下面的代码创建了我正在尝试做的简化版本。应用程序提供一个小部件。当它出现在屏幕上时,会提示用户输入数字。然后,该小数字显示在小部件上。单击小组件会将数字打印到日志中。可以使用不同的数字创建此窗口小部件的多个实例。单击每个应该生成一个对应于为每个小部件选择的数字的日志条目。

但是,我看到某种缓存行为。假设我创建了小工具35。点击3将会记录3。点击5将会记录3。删除3并点击5将再次记录3。这是如何运作的?看起来第一个小部件已创建并且其视图已缓存?

的Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.developer.app">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name=".AppApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity
            android:name=".activity.DashboardActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".activity.DummyWidgetConfigActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
            </intent-filter>
        </activity>

        <receiver android:name=".DummyWidgetProvider">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>
            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/dummy_widget_info" />
        </receiver>

        <service
            android:name=".service.DummyWidgetService"
            android:exported="false" />

    </application>

</manifest>

DummyWidgetConfigActivity.java

package com.developer.app.activity;

import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.developer.app.DummyWidgetProvider;
import com.developer.app.R;

/**
 * TODO: Add a class header comment!
 */

public class DummyWidgetConfigActivity extends AppCompatActivity {

    private static final String TAG = "DummyWgtConfigActivity";

    private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

    private TextView numberInput = null;
    private Button submitButton = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setResult(RESULT_CANCELED);

        setContentView(R.layout.activity_dummy_config);

        Intent intent = getIntent();
        Bundle extras = intent.getExtras();
        if (extras != null) {
            appWidgetId = extras.getInt(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
        }

        if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish();
        }

        numberInput = (TextView) findViewById(R.id.number_input);
        submitButton = (Button) findViewById(R.id.submit);

        submitButton.setOnClickListener(v -> {
            try {
                final int number = Integer.parseInt(numberInput.getText().toString());
                onNumberSelect(number);
            } catch (Exception e) {
                Toast.makeText(this, "Must be a number", Toast.LENGTH_SHORT).show();
            }
        });
    }

    public void onNumberSelect(final int number) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
        SharedPreferences.Editor editor = sp.edit();

        editor.putInt("widget_" + appWidgetId, number);
        editor.commit();

        Log.d(TAG, "Creating widget with id " + appWidgetId);

        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(this);
        DummyWidgetProvider.updateAppWidget(this, appWidgetManager, appWidgetId);

        Intent resultValue = new Intent();
        resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        setResult(RESULT_OK, resultValue);
        finish();
    }
}

DummyWidgetProvider.java

package com.developer.app;

import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.RemoteViews;

import com.developer.app.service.DummyWidgetService;

/**
 * TODO: Add a class header comment!
 */

public class DummyWidgetProvider extends AppWidgetProvider {

    private static final String TAG = "DummyWidgetProvider";

    public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds) {
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    public static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager, final int appWidgetId) {
        final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        final int number = sp.getInt("widget_" + appWidgetId, -1);

        if (number == -1) {
            Log.e(TAG, "Could not find the number associated with widget " + appWidgetId);
            return;
        }

        Log.d(TAG, "Updating widget with id " + appWidgetId);

        Intent intent = DummyWidgetService.createShowNumberIntent(context, number, appWidgetId);
        PendingIntent pendingIntent = PendingIntent.getService(context, 1, intent, 0);

        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_dummy);

        views.setTextViewText(R.id.widget_number, Integer.toString(number));

        views.setOnClickPendingIntent(R.id.widget_number, pendingIntent);

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

DummyWidgetService.java

package com.developer.app.service;

import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.widget.Toast;

/**
 * TODO: Add a class header comment!
 */

public class DummyWidgetService extends IntentService {

    private static final String TAG = "DummyWidgetService";
    private static final String DUMMY_WIDGET_SERVICE_WORKER_THREAD = "com.developer.app.THREAD.dummy_widget_service";
    private static final String EXTRA_NUMBER_ID = "com.developer.app.service.DummyWidgetService.extra.NumberId";
    private static final String EXTRA_WIDGET_ID = "com.developer.app.service.DummyWidgetService.extra.WidgetId";
    public final static String EXTRA_ACTION = "com.developer.app.service.DummyWidgetService.extra.ACTION";

    private final static int ACTION_UNKNOWN = -1;
    private final static int ACTION_SHOW_NUMBER = 1;

    public DummyWidgetService() {
        super(DUMMY_WIDGET_SERVICE_WORKER_THREAD);
    }

    public static Intent createShowNumberIntent(final Context context, final int number, final int widgetId) {
        Intent intent = new Intent(context, DummyWidgetService.class);

        intent.putExtra(EXTRA_ACTION, ACTION_SHOW_NUMBER);
        intent.putExtra(EXTRA_NUMBER_ID, number);
        intent.putExtra(EXTRA_WIDGET_ID, widgetId);

        return intent;
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        switch (intent.getIntExtra(EXTRA_ACTION, ACTION_UNKNOWN)) {
            case ACTION_SHOW_NUMBER:
                final int number = intent.getIntExtra(EXTRA_NUMBER_ID, -1);
                final int widgetId = intent.getIntExtra(EXTRA_WIDGET_ID, -1);

                //Toast.makeText(this, "Widget number: " + number, Toast.LENGTH_SHORT).show();
                Log.i(TAG, "Widget number: " + number);

                break;
            default:
                Log.w(TAG, "Unknown action requested!");
        }
    }
}

dummy_widget_info.xml

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:configure="com.developer.app.activity.DummyWidgetConfigActivity"
    android:updatePeriodMillis="86400000"
    android:initialLayout="@layout/widget_dummy"
    android:resizeMode="none"
    android:widgetCategory="home_screen">
</appwidget-provider>

activity_dummy_config.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <EditText
        android:id="@+id/number_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="number"/>

    <Button
        android:id="@+id/submit"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Submit"/>

</LinearLayout>

widget_dummy.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/widget_number"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:textAlignment="center"/>

</LinearLayout>

感谢您的任何建议!我确实看到其他应用程序能够正确处理多个实例。因此,我认为这纯粹是我的错误。

1 个答案:

答案 0 :(得分:3)

问题在于,挂起的意图是由具有相同内部ID的相同进程(可能是Android OS应用程序)创建的

PendingIntent pendingIntent = PendingIntent.getService(context, 1, intent, 0);

在上面的代码中,1用于每个小部件实例。因此,这只是重用意图。该值可以更改为appWidgetId。此外,该标志可以从0更改为FLAG_CANCEL_CURRENTFLAG_UPDATE_CURRENT