2010-04-26 67 views
2

我在這裏問了一個關於任務殺手和窗口小部件停止工作的問題(SO Question),但現在我有報告說用戶他們沒有使用任何殺手,窗口小部件沒有工作一段時間後。我有一個Nexus One,我沒有這個問題。我需要關於窗口小部件和PendingIntents的幫助

我不知道這是內存問題還是其他問題。基於API:

甲的PendingIntent本身是一個簡單的 參照由描述原始數據 用於檢索它的 系統所維護的令牌。這意味着,即使其擁有的應用程序的 進程被終止, PendingIntent 本身仍然可以從其他 進程獲得它。

所以,我不知道爲什麼widget停止工作,如果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

我認爲你需要確定到底是什麼用戶意味着「停止工作」,它是否強制關閉(崩潰)或者只是變得沒有反應?收集任何關於他們手機的信息,例如他們擁有哪些手機,他們正在運行的Android版本(查看它們是否不知道)等。另外,請確保你明確詢問他們是否使用CyanogenMod等自定義固件。

讓你的應用程序向SD卡寫入一些記錄信息,這樣你可以要求用戶發送電子郵件你再次發生日誌,希望能夠揭示應用程序開始行爲不當之前的最後一項任務。


更新

看來你實際上是在播放音樂從appwidget,這將迫使你堅持到屏幕上的小部件的生命週期之內。特別是事實上的部件不再是優先級的進程一旦在主頁屏幕上不再是重點,而fail-fast behaviour of a BroadcastReceiver

注意:由於的AppWidgetProvider是 一個BroadcastReceiver,你的過程是 不能保證讓在 之後運行回調方法返回(請參閱 應用基礎>廣播 接收器生命週期更多 信息)。如果您的App Widget設置 進程可能需要幾秒鐘的時間(可能在執行web 請求時)並且您需要繼續執行 進程,請考慮在onUpdated()方法中啓動 服務。

我的建議是移動音樂播放碼出appwidget,併成爲一個Service,你只需要播放啓動時啓動該服務,而當播放結束時,你應該撕裂下來。這將爲您提供播放音樂的後臺進程,而不會受到appwidget生命週期的影響。這種模式的一個例子是Last.FM appwidget(隨應用提供)。

+0

感謝您的評論。該小部件只是停止重現聲音,不顯示任何錯誤或fc。 各種設備是巨大的和Android版本(從1.5到2.1)。 我猜是Android操作系統本身... – YaW 2010-04-26 10:21:16

+0

也許,雖然其他應用程序已經能夠可靠地在Android上產生聲音 - 例如,股票音樂播放器和Last.fm.我已經通過一些進一步的建議修改了我的解 – seanhodges 2010-04-27 08:25:59