Eine App von mir akkumuliert viele Thread
Instanzen, die der GC nicht aufnehmen und löschen kann. Dieses Speicherleck stürzt die App auf lange Sicht ab.Warum werden meine Threads nicht abstürzen und ein Speicherleck verursachen?
Ich bin nicht 100% sicher, woher sie kommen, aber ich habe eine deutliche folgende Macht Gefühl der Code in Frage:
public class UraHostHttpConnection extends AbstractUraHostConnection {
private Handler uiThreadHandler = new Handler(Looper.getMainLooper());
private Executor taskExecutor = new Executor() {
public void execute(Runnable command) {
new Thread(command).start();
}
};
private ConnectionTask task = null;
@Override
public void sendRequest(final HttpUriRequest request) {
this.task = new ConnectionTask();
this.uiThreadHandler.post(new Runnable() {
public void run() {
task.executeOnExecutor(taskExecutor, request);
}
});
}
@Override
public void cancel() {
if (this.task != null)
this.task.cancel(true);
}
}
Dieser Code ermöglicht es mir, einige zu laufen HTTP Verbindungen parallel, die einander nicht auf dem Standard AsyncTask
Executor
(die nur eine einzige Thread-Warteschlange ist) blockieren.
Ich habe überprüft, dass die AsyncTask
s tatsächlich ihre onPostExecute()
Methoden erreichen und nicht nur für immer laufen. Nach dem Überprüfen einiger Speicherabbilder vermute ich, dass die Umhüllung Thread
-Objekte nicht mehr ausgeführt wird, nachdem die AsyncTask
s abgeschlossen sind.
Ist es möglich, dass der obige Code immer noch für mein Speicherleck verantwortlich ist, oder sollte ich mich anderswo umsehen?
Jede Hilfe wird geschätzt.
Edit: Es sollte beachtet werden, dass sendRequest
nur einmal aufgerufen wird. Andere Teile des Codes, die nicht im obigen Beispiel enthalten sind, stellen dies sicher.
Edit 2: der Super-Klasse sieht wie folgt aus:
public abstract class AbstractUraHostConnection {
protected IUraHostConnectionListener listener = null;
public void setListener(IUraHostConnectionListener listener) {
this.listener = listener;
}
public abstract void sendRequest(HttpUriRequest request);
public abstract void cancel();
}
Die AsyncTask sieht wie folgt aus:
private class ConnectionTask extends AsyncTask<HttpUriRequest, Object, Void> {
final byte[] buffer = new byte[2048];
private ByteArrayBuffer receivedDataBuffer = new ByteArrayBuffer(524288);
@Override
protected Void doInBackground(HttpUriRequest... arg0) {
UraHostHttpConnection.taskCounter++;
AndroidHttpClient httpClient = AndroidHttpClient.newInstance("IVU.realtime.app");
try {
// Get response and notify listener
HttpResponse response = httpClient.execute(arg0[0]);
this.publishProgress(response);
// Check status code OK before proceeding
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent();
int readCount = 0;
// Read one kB of data and hand it over to the listener
while ((readCount = inputStream.read(buffer)) != -1 && !this.isCancelled()) {
this.receivedDataBuffer.append(buffer, 0, readCount);
if (this.receivedDataBuffer.length() >= 524288 - 2048) {
this.publishProgress(receivedDataBuffer.toByteArray());
this.receivedDataBuffer.clear();
}
}
if (this.isCancelled()) {
if (arg0[0] != null && !arg0[0].isAborted()) {
arg0[0].abort();
}
}
}
} catch (IOException e) {
// forward any errors to listener
e.printStackTrace();
this.publishProgress(e);
} finally {
if (httpClient != null)
httpClient.close();
}
return null;
}
@Override
protected void onProgressUpdate(Object... payload) {
// forward response
if (payload[0] instanceof HttpResponse)
listener.onReceiveResponse((HttpResponse) payload[0]);
// forward error
else if (payload[0] instanceof Exception)
listener.onFailWithException((Exception) payload[0]);
// forward data
else if (payload[0] instanceof byte[])
listener.onReceiveData((byte[]) payload[0]);
}
@Override
protected void onPostExecute(Void result) {
listener.onReceiveData(this.receivedDataBuffer.toByteArray());
listener.onFinishLoading();
UraHostHttpConnection.taskCounter--;
Log.d(TAG, "There are " + UraHostHttpConnection.taskCounter + " running ConnectionTasks.");
}
}
Nicht wirklich sicher, aber kann diese Ihnen helfen? http://www.androiddesignpatterns.com/2013/04/activities-threads-memory-leaks.html – dumazy
Irgendetwas in den Konstruktoren der Super-Klassen von AbstractUraHostConnection, die vielleicht erschrecken? Wie sieht ConnectionTask aus? – ddmps
Code beider Klassen hinzugefügt. – Chris