I need help with Widget and PendingIntents

I asked a question here about Task Killers and widgets stop working ( SO Question ), but now I have reports that they are not using any Task Killers and the widgets have not worked after a while. I have a Nexus One and do not have this problem.

I don't know if this is a memory issue or something else. API based:

A PendingIntent itself is simply a reference to a token maintained by the system that describes the original data used to retrieve. This means that even if its own application process is killed, the PendingIntent itself will remain usable by other processes that have been provided to it.

So I don't know why the widget stops working if Android doesn't kill the PendingIntent by itself, what's the problem?

This is my manifest code:

    <receiver android:name=".widget.InstantWidget" android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data android:name="android.appwidget.provider"
            android:resource="@xml/widget_provider" />
    </receiver>

      

And the widget code:

public class InstantWidget extends AppWidgetProvider {

    public static ArrayList<Integer> alWidgetsId = new ArrayList<Integer>();

    private static final String PREFS_NAME = "com.cremagames.instant.InstantWidget";
    private static final String PREF_PREFIX_NOM = "nom_";
    private static final String PREF_PREFIX_RAW = "raw_";

    /**
     * Esto se llama cuando se crea el widget. Metemos en las preferencias los valores de nombre y raw para tenerlos en proximos reboot.
     * @param context
     * @param appWidgetManager
     * @param appWidgetId
     * @param nombreSound
     * @param rawSound
     */
    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
            int appWidgetId, String nombreSound, int rawSound){

        //Guardamos en las prefs los valores
        SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
        prefs.putString(PREF_PREFIX_NOM + appWidgetId, nombreSound);
        prefs.putInt(PREF_PREFIX_RAW + appWidgetId, rawSound);
        prefs.commit();

        //Actualizamos la interfaz
        updateWidgetGrafico(context, appWidgetManager, appWidgetId, nombreSound, rawSound);
    }

    /**
     * Actualiza la interfaz gráfica del widget (pone el nombre y crea el intent con el raw)
     * @param context
     * @param appWidgetManager
     * @param appWidgetId
     * @param nombreSound
     * @param rawSound
     */
    private static void updateWidgetGrafico(Context context, AppWidgetManager appWidgetManager,
            int appWidgetId, String nombreSound, int rawSound){
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget);

        //Nombre del Button
        remoteViews.setTextViewText(R.id.tvWidget, nombreSound);

        //Creamos el PendingIntent para el onclik del boton
        Intent active = new Intent(context, InstantWidget.class);
        active.setAction(String.valueOf(appWidgetId));
        active.putExtra("sonido", rawSound);

        PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);

        actionPendingIntent.cancel();
        actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);

        remoteViews.setOnClickPendingIntent(R.id.btWidget, actionPendingIntent);

        appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
    }

    public void onReceive(Context context, Intent intent) {     
        final String action = intent.getAction();
        //Esto se usa en la 1.5 para que se borre bien el widget
        if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
            final int appWidgetId = intent.getExtras().getInt(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,
                    AppWidgetManager.INVALID_APPWIDGET_ID);
            if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
                this.onDeleted(context, new int[] { appWidgetId });
            }
        } else {
            //Listener de los botones
            for(int i=0; i<alWidgetsId.size(); i++){
                if (intent.getAction().equals(String.valueOf(alWidgetsId.get(i)))) {
                    int sonidoRaw = 0;
                    try {
                        sonidoRaw = intent.getIntExtra("sonido", 0);
                    } catch (NullPointerException e) {
                    }

                    MediaPlayer mp = MediaPlayer.create(context, sonidoRaw);
                    mp.start();
                    mp.setOnCompletionListener(completionListener);
                }
            }

            super.onReceive(context, intent);
        }
    }

    /** Al borrar el widget, borramos también las preferencias **/
    public void onDeleted(Context context, int[] appWidgetIds) {
        for(int i=0; i<appWidgetIds.length; i++){
            //Recogemos las preferencias
            SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
            prefs.remove(PREF_PREFIX_NOM + appWidgetIds[i]);
            prefs.remove(PREF_PREFIX_RAW + appWidgetIds[i]);
            prefs.commit();
        }

        super.onDeleted(context, appWidgetIds);
    }

    /**Este método se llama cada vez que se refresca un widget. En nuestro caso, al crearse y al reboot del telefono.
    Al crearse lo único que hace es guardar el id en el arrayList
    Al reboot, vienen varios ID así que los recorremos y guardamos todos y también recuperamos de las preferencias el nombre y el sonido*/
    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
            int[] appWidgetIds) {

        for(int i=0; i<appWidgetIds.length; i++){
            //Metemos en el array los IDs de los widgets
            alWidgetsId.add(appWidgetIds[i]);

            //Recogemos las preferencias
            SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
            String nomSound = prefs.getString(PREF_PREFIX_NOM + appWidgetIds[i], null);
            int rawSound = prefs.getInt(PREF_PREFIX_RAW + appWidgetIds[i], 0);

            //Si están creadas, actualizamos la interfaz
            if(nomSound != null){
                updateWidgetGrafico(context, appWidgetManager, appWidgetIds[i], nomSound, rawSound);
            }
        }
    }

    MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener(){

        public void onCompletion(MediaPlayer mp) {
            if(mp != null){
                mp.stop();
                mp.release();
                mp = null;
            }
        }

    };

}

      

Sorry for the comments in Spanish.

I have the ability to place different widgets on my desktop, so I use widgetId as the "unique identifier" for the PendingIntent.

Any ideas please? 70% of my app's functionality is widgets and it doesn't work for some users :(

Thanks in advance and sorry for my english.

+2


a source to share


1 answer


I think you need to define exactly what the user means "stops working". Does it make it close (crash) or does it just become unresponsive? Collect whatever information you can about their phone, for example. what phone they have, what version of Android they are running (see if they don't know), etc. Also make sure you specifically ask them if they are using custom firmware like CyanogenMod.

Make your app write the registration information to the SD card so you can ask the user to email you when it happens again and hopefully shed some light on what was the last one before the app started bad behave.


Update



You seem to be actually playing music from within the app, which will force you to stick to the widget lifecycle on the screen. In particular, the fact that the widget is no longer the foreground process when the main screen is no longer in focus, and the BroadcastReceiver's failover behavior :

Note. Since the AppWidgetProvider is a BroadcastReceiver, your process is not guaranteed to continue running after the callback methods return (see Usage Basics> Broadcast Receiver Lifecycle for Information). If your App Widget setup process can take a few seconds (possibly with network requests) and you require your process to continue, consider starting the Service in the onUpdated () method.

My suggestion was to move the music playback code from appwidget and into Service , you only need to start when playback starts and you should break it when playback ends. This will provide you with a background process for playing music without affecting the lifecycle of your application. An example of this template is the Last.FM app (comes with the app).

+1


a source







All Articles