我需要Widget和PendingIntents的帮助

时间:2010-04-26 06:41:42

标签: android widget android-widget android-pendingintent

我在这里问过一个关于任务杀手和小部件停止工作的问题(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%的功能是小部件,它不适用于某些用户:(

提前致谢并抱歉我的英语。

1 个答案:

答案 0 :(得分:1)

我认为您需要通过“停止工作”来确定用户的确切含义。是强制关闭(崩溃)还是只是没有反应?收集有关手机的任何信息,例如:他们拥有什么样的手机,他们运行的是什么版本的Android(如果他们不知道的话,请查看),等等。另外,请确保您特别询问他们是否使用CyanogenMod等自定义固件。

让您的应用程序将一些日志记录信息写入SD卡,这样您就可以要求用户在日志再次发生时通过电子邮件发送给您,并希望能够在应用程序开始行为不端之前了解最后一项任务。< / p>


<强>更新

看来你实际上是在appwidget中播放音乐,这将迫使你坚持屏幕上小部件的生命周期。特别是当主屏幕不再处于焦点时,小部件不再是优先级过程,fail-fast behaviour of a BroadcastReceiver

  

注意:因为AppWidgetProvider是   一个BroadcastReceiver,你的过程是   不能保证继续运行   回调方法返回(参见   应用基础&gt;广播   接收器生命周期更多   信息)。如果您的App Widget设置   过程可能需要几秒钟   (也许在执行网络时   请求),你需要你的   过程继续,考虑开始   onUpdated()方法中的服务。

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