2

我在这里问了一个关于任务杀手和小部件停止工作的问题(SO Question)但是现在,我有用户报告说他们没有使用任何任务杀手,并且小部件在一段时间后没有工作。我有一个 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 卡,这样您就可以要求用户在再次发生日志时通过电子邮件向您发送日志,并希望在应用程序开始出现异常之前了解最后一个任务是什么。


更新

看来您实际上是在 appwidget 中播放音乐,这将迫使您遵守屏幕上小部件的生命周期。特别是一旦主屏幕不再是焦点,小部件就不再是优先进程,以及BroadcastReceiver 的快速失败行为

注意:因为 AppWidgetProvider 是一个 BroadcastReceiver,所以不能保证您的进程在回调方法返回后继续运行(有关更多信息,请参阅应用程序基础 > 广播接收器生命周期)。如果您的 App Widget 设置过程可能需要几秒钟(可能在执行 Web 请求时)并且您要求您的过程继续,请考虑在 onUpdated() 方法中启动服务。

我的建议是将音乐播放代码移出 appwidget 并放入Service,您只需要在播放开始时启动服务,并且在播放结束时将其删除。这将为您提供一个播放音乐的后台进程,而不受应用小部件生命周期的影响。此模式的一个示例是 Last.FM 应用程序小部件(随应用程序提供)。

于 2010-04-26T07:28:00.803 回答