android - tesis - ¿Por qué mis hilos no se mueren y causan una pérdida de memoria?
tesis sobre youtube pdf (1)
Una aplicación mía está acumulando muchas instancias de Thread
que el GC no puede recoger y eliminar. Esta pérdida de memoria bloquea la aplicación a largo plazo.
No estoy 100% seguro de dónde vienen, pero tengo una clara sensación de que el siguiente podría ser el código en cuestión:
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);
}
}
Este código me permite ejecutar varias conexiones HTTP en paralelo que no se bloquearán entre sí en el Executor
AsyncTask
defecto (que es solo una cola con hilos únicos).
Comprobé que las AsyncTask
están llegando a sus métodos onPostExecute()
y no solo se ejecutan para siempre. Después de inspeccionar algunos volcados de memoria, sospecho que los objetos de Thread
envoltura no pararán de ejecutarse después de que se AsyncTask
completado los AsyncTask
.
¿Es posible que el código anterior siga siendo responsable de la pérdida de memoria o debería comenzar a buscar en otro lado?
Cualquier ayuda es apreciada.
Editar: Cabe señalar que sendRequest
solo se llama una vez. Otras partes del código que no están en la muestra anterior se aseguran de eso.
Editar 2: la superclase se ve así:
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();
}
El AsyncTask se ve así:
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.");
}
}
Sustituye a ThreadPoolExecutor para tu Executor para que tengas control sobre el tamaño del grupo. Si ThreadPoolExecutor es básicamente un Ejecutor con métodos expuestos, puede ser simplemente un caso en el que el tamaño de grupo máximo predeterminado sea muy alto.
Documento oficial aquí .
Eche un vistazo en particular a:
setCorePoolSize(int corePoolSize)
//Sets the core number of threads.
setKeepAliveTime(long time, TimeUnit unit)
//Sets the time limit for which threads may remain idle before being terminated.
setMaximumPoolSize(int maximumPoolSize)
//Sets the maximum allowed number of threads.
También hay una alternativa si quieres codificar menos (mejor idea dependiendo de cuánto control realmente quieras y cuánto código cambiarás para obtenerlo).
Executor taskExecutor = Executors.newFixedThreadPool(x);
donde x = el tamaño de tu grupo