2010-08-31 15 views
10

Ich entwickle eine Anwendung mit einem Dienst, die den Fortschritt eines Timers im Infobereich (mit einem Fortschrittsbalken und einem Text) anzeigen. Ich habe unten ein einfacheres Beispiel mit dem gleichen Problem extrahiert.Sehr große Speicherauslastung in Benachrichtigungen

Der Code des Service:

public class TNService extends Service { 
    private NotificationManager nm; 
    private Notification notification; 
    private RemoteViews remoteView; 

    @Override 
    public void onCreate() { 
     nm = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); 
     notification = new Notification(android.R.drawable.stat_sys_download, 
       "My notification", 
       System.currentTimeMillis()); 
     remoteView = new RemoteViews(this.getPackageName(), 
       R.layout.notification); 
     remoteView.setImageViewResource(R.id.icon, android.R.drawable.stat_sys_download); 
     remoteView.setTextViewText(R.id.text, ""); 
     remoteView.setProgressBar(R.id.progress, 100, 0, false); 
     notification.flags = Notification.FLAG_NO_CLEAR; 
     notification.contentView = remoteView; 
     notification.contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, 
       TNActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); 

     Timer timer = new Timer(); 
     timer.schedule(new TNTask(this), 0, 200); 
    } 

    @Override 
    public IBinder onBind(Intent arg0) { 
     return null; 
    } 

    public void updateNotification(int progress) { 
     remoteView.setProgressBar(R.id.progress, 1000, progress, false); 
     remoteView.setTextViewText(R.id.text, "Progress: " + progress); 
     nm.notify(0, notification); 
    } 
} 

Der Code des Timertask:

public class TNTask extends TimerTask { 
    private TNService service; 
    private int progress; 

    public TNTask(TNService s) { 
     this.service = s; 
     this.progress = 0; 
    } 

    @Override 
    public void run() { 
      progress = (progress + 1) % 1000; 
     this.service.updateNotification (progress); 
    } 
} 

Das Problem ist die große Speichernutzung. Hier ist die logcat Ausgabe:

D/dalvikvm(11985): GC_EXPLICIT freed 1258 objects/84016 bytes in 1157ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 52216 objects/1900968 bytes in 130ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 49465 objects/1805248 bytes in 125ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 53106 objects/1909992 bytes in 134ms 
D/dalvikvm(12008): GC_EXPLICIT freed 1604 objects/100944 bytes in 90ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 53011 objects/1937160 bytes in 135ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 49806 objects/1817992 bytes in 143ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 49016 objects/1769536 bytes in 135ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 53509 objects/1941064 bytes in 145ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 49895 objects/1842312 bytes in 146ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 48728 objects/1774496 bytes in 150ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 47557 objects/1701976 bytes in 146ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 53540 objects/1903808 bytes in 156ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 48997 objects/1784048 bytes in 158ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 48326 objects/1776864 bytes in 158ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 47566 objects/1742488 bytes in 169ms 
D/dalvikvm( 85): GC_FOR_MALLOC freed 47606 objects/1703416 bytes in 170ms 
D/dalvikvm( 162): GC_EXPLICIT freed 11238 objects/641368 bytes in 1064ms 

Ich denke, es ist zu viel Speicher ist und nach einer Weile das Telefon hängt mit dieser Ausgabe:

D/dalvikvm( 85): GC_FOR_MALLOC freed 0 objects/0 bytes in 241ms 
I/dalvikvm-heap( 85): Clamp target GC heap from 24.008MB to 24.000MB 
I/dalvikvm-heap( 85): Grow heap (frag case) to 24.000MB for 52-byte allocation 
I/dalvikvm-heap( 85): Clamp target GC heap from 26.008MB to 24.000MB 
D/dalvikvm( 85): GC_FOR_MALLOC freed 0 objects/0 bytes in 241ms 
I/dalvikvm-heap( 85): Clamp target GC heap from 24.008MB to 24.000MB 
I/dalvikvm-heap( 85): Grow heap (frag case) to 24.000MB for 24-byte allocation 
I/dalvikvm-heap( 85): Clamp target GC heap from 26.008MB to 24.000MB 
D/dalvikvm( 85): GC_FOR_MALLOC freed 0 objects/0 bytes in 247ms 
I/dalvikvm-heap( 85): Clamp target GC heap from 24.009MB to 24.000MB 
I/dalvikvm-heap( 85): Grow heap (frag case) to 24.000MB for 28-byte allocation 
I/dalvikvm-heap( 85): Clamp target GC heap from 26.009MB to 24.000MB 
D/dalvikvm( 85): GC_FOR_MALLOC freed 0 objects/0 bytes in 247ms 
I/dalvikvm-heap( 85): Clamp target GC heap from 24.009MB to 24.000MB 

Wer weiß, wie kann ich es tun, ohne so viele Speicher mit ?

Danke!

+0

einen offenen Fehler zu diesem Thema finden Sie: http://code.google.com/p/android/issues/detail?id=13941 –

Antwort

2

Versuchen Sie DDMS zu verwenden, um die Zuordnungen auszugeben - das sollte Ihnen zeigen, welche Objekte zugewiesen werden und wo.

Meine Vermutung ist, dass der Fortschrittsbalken bei jedem Aufruf von setProgressBar (5 mal pro Sekunde) einige Bitmaps zuweist und das ist, was durch den Speicher geht. Was nicht klar ist, ist, warum Sie dann auslaufen - der GC scheint es aufzuheben, also muss etwas undicht sein.

+1

I Ich habe einmal einen Fortschrittsbalken in der Benachrichtigung verwendet und festgestellt, dass das Telefon sehr langsam wurde, als es versuchte, den Fortschritt zu aktualisieren. Also musste ich die Update-Frequenz sehr reduzieren. –

+0

Ja! Es war auch meine Lösung, aber ich weiß nicht, wie ich dieses Verhalten vermeiden oder reduzieren kann. – Urizev

10

Ich stolperte über das gleiche Problem ... Es scheint so, als ob das Problem nicht verschwindet, wenn ich RemoteView und Notification nicht im Dienst "zwischenspeichern", sondern in der "update" -Routine von Grund auf neu erstellen. Ja, ich weiß, es ist nicht effizient, aber zumindest startet das Telefon nach 10-15 Minuten nicht neu, weil es nicht genügend Arbeitsspeicher hat.

+0

Danke, scheint den Trick zu tun. – plouh

+0

Ich hatte genau das gleiche Problem, wo meine Handys entweder abgestürzt sind (HTC Legende) oder ein Teil des Betriebssystems abgestürzt ist (Sony Ericsson Xperia Mini Pro). Durch das erneute Erstellen des RemoteViews bei jedem Update wurde das Problem behoben. – slott

+0

Sehr schöner Trick, Danke – NullPointer

1

Das Problem mit dieser Problemumgehung ist, dass wenn es eine laufende Benachrichtigung ist, wird es „Hop“ in der Statusleiste und Benachrichtigungsbereich als andere laufende Mitteilungen aktualisiert werden.

Ich habe verschiedene Dinge ausprobiert, einschließlich der Deklaration der RemoteView- und Notification-Member als flüchtig (weil RemoteView Cross-Thread ist), was zu funktionieren schien, aber das Problem nur verlangsamte.

Worauf ich mich einigte, war die Verwendung eines Chokemembers und das "Caching" der RemoteView- und Notification-Funktion bis zu X-mal, um sie dann neu zu erstellen.

Wenn ihre Mitglieder auf null gesetzt sind, scheint das kleine Leck freigegeben zu sein.

0

Ich hatte ein ähnliches Problem. Ich hatte eine Service, die eine Notification mit einem Fortschrittsbalken präsentiert, der einem Dateidownload entsprach. Die App abstürzen würde mit einem OutOfMemoryError, etwa zehn Sekunden, nachdem der Benutzer die Notification geklickt sie auf die App zu bringen.

Ich fand, dass .setOngoing(true); zum Builder Hinzufügen dieses Problem behoben.

public NotificationCompat.Builder setOngoing (boolean laufend)

Legt fest, ob dies eine laufende Benachrichtigung. Laufende Meldungen unterscheiden sich von normalen Meldungen auf folgende Weise:

  • Laufende Meldungen werden über den regulären Benachrichtigungen in der Benachrichtigungsfeld sortiert.

  • Laufende Benachrichtigungen haben keine X-Schaltfläche zum Schließen und sind nicht von der Schaltfläche Alle löschen betroffen.

Beispiel:

NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setAutoCancel(true) 
                 .setDefaults(Notification.DEFAULT_ALL) 
                 .setContentTitle("Downloading").setContentText("Download in progress...) 
                  .setSmallIcon(android.R.drawable.stat_sys_download) 
                  .setSound(null) 
                  .setDefaults(0) 
                  .setOngoing(true);