2

ここでタスク キラーとウィジェットが機能しなくなる( SO の質問) について質問しましたが、現在、タスク キラーを使用しておらず、しばらくするとウィジェットが機能しなかったというユーザーの報告があります。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 の「一意の ID」として使用します。

アイデアはありますか?私のアプリの機能の 70% はウィジェットですが、一部のユーザーには機能しません :(

よろしくお願いします。私の英語で申し訳ありません。

4

1 に答える 1

1

「動作を停止する」ということで、ユーザーが何を意味するのかを正確に判断する必要があると思います。強制終了(クラッシュ)しますか、それとも単に応答しなくなりますか?使用している携帯電話、実行しているAndroidのバージョン(わからない場合は調べてください)など、携帯電話に関する情報を収集します。また、カスタムを使用しているかどうかを具体的に尋ねてください。 CyanogenModのようなファームウェア。

アプリにログ情報をSDカードに書き込んでもらいます。そうすれば、ログが再度発生したときにユーザーにログをメールで送信するように依頼でき、アプリが誤動作を開始する前の最後のタスクが何であったかを明らかにすることができます。


アップデート

実際にアプリウィジェット内から音楽を再生しているように見えます。これにより、画面上のウィジェットのライフサイクルを順守する必要があります。特に、ホーム画面にフォーカスがなくなると、ウィジェットは優先プロセスではなくなり、BroadcastReceiverのフェイルファスト動作は次のようになります。

注:AppWidgetProviderはBroadcastReceiverであるため、コールバックメソッドが戻った後もプロセスが実行され続けることは保証されません(詳細については、「アプリケーションの基礎」>「ブロードキャストレシーバーのライフサイクル」を参照してください)。アプリウィジェットのセットアッププロセスに数秒かかる場合があり(おそらくWebリクエストの実行中)、プロセスを続行する必要がある場合は、onUpdated()メソッドでサービスを開始することを検討してください。

私の提案は、音楽再生コードをアプリウィジェットからサービスに移動することです。サービスは再生開始時にのみ開始する必要があり、再生が終了したときに破棄する必要があります。これにより、アプリウィジェットのライフサイクルに影響されることなく音楽を再生するためのバックグラウンドプロセスが提供されます。このパターンの例は、Last.FMアプリウィジェット(アプリに付属)です。

于 2010-04-26T07:28:00.803 に答える