Мне нужна помощь с Widget и PendingIntents - PullRequest
2 голосов
/ 26 апреля 2010

Я задал здесь вопрос о Task Killers, и виджеты перестали работать ( SO Question ), но теперь у меня есть сообщения о том, что пользователи не используют Task Killer, а виджеты не работать через некоторое время. У меня есть Nexus One, и у меня нет этой проблемы.

Я не знаю, это проблема памяти или что-то в этом роде. На основании API:

Сам PendingIntent - это просто ссылка на токен, поддерживаемый система описания исходных данных использовал, чтобы получить его. Это означает, что, даже если его собственное приложение процесс убит, PendingIntent сам останется пригодным для использования от других процессы, которые ему дали.

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

Это мой манифест-код:

    <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>

И код виджета:

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;
            }
        }

    };

}

Извините за комментарии на испанском.

У меня есть возможность разместить разные виджеты на рабочем столе, поэтому я использую widgetId в качестве «уникального идентификатора» для PendingIntent.

Есть идеи, пожалуйста? 70% функциональности моего приложения - это виджеты, и у некоторых пользователей оно не работает: (

Заранее спасибо и извините за мой английский.

1 Ответ

1 голос
/ 26 апреля 2010

Я думаю, что вам нужно точно определить, что пользователь подразумевает под «прекращением работы». Это заставляет закрыть (сбой) или просто перестает отвечать? Соберите любую информацию о своем телефоне, которую вы можете, например, какой у них телефон, какую версию Android они используют (посмотрите, если они не знают) и т. д. Кроме того, убедитесь, что вы специально спросили их, используют ли они специальные прошивки, такие как CyanogenMod.

Заставьте ваше приложение записать некоторую информацию о регистрации на SD-карту, чтобы вы могли попросить пользователя отправить вам журнал по электронной почте, когда это произойдет снова, и, надеюсь, пролить некоторый свет на то, какой была последняя задача перед тем, как приложение начало плохо себя вести.


Обновление

Похоже, что вы на самом деле воспроизводите музыку из виджета приложения, что заставит вас придерживаться жизненного цикла виджета на экране. В частности, тот факт, что виджет больше не является приоритетным процессом, когда домашний экран больше не находится в фокусе, и отказоустойчивое поведение BroadcastReceiver :

Примечание. Поскольку AppWidgetProvider BroadcastReceiver, ваш процесс не гарантированно продолжать работать после методы обратного вызова возвращаются (см. Основы применения> Трансляция Жизненный цикл приемника для более Информация). Если ваш виджет настройки приложения процесс может занять несколько секунд (возможно, при выполнении веб запросы), и вы требуете, чтобы ваш процесс продолжается, рассмотрите возможность начала Служба в методе onUpdated ().

Мое предложение будет заключаться в том, чтобы переместить код воспроизведения музыки из виджета приложения в Сервис , вам нужно только запустить сервис, когда начинается воспроизведение, и вы должны его отключить, когда воспроизведение закончится. Это обеспечит вам фоновый процесс для воспроизведения музыки без влияния жизненного цикла вашего приложения. Примером этого шаблона является виджет приложения Last.FM (поставляется вместе с приложением).

...