Estuve leyendo sobre AsyncTask
, y probé el sencillo programa de abajo. Pero no parece funcionar. ¿Cómo puedo hacer que funcione?
public class AsyncTaskActivity extends Activity {
Button btn;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = (Button) findViewById(R.id.button1);
btn.setOnClickListener((OnClickListener) this);
}
public void onClick(View view){
new LongOperation().execute("");
}
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
return null;
}
@Override
protected void onPostExecute(String result) {
}
@Override
protected void onPreExecute() {
}
@Override
protected void onProgressUpdate(Void... values) {
}
}
}
Sólo estoy tratando de cambiar la etiqueta después de 5 segundos en el proceso de fondo.
Este es mi main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="false"
android:max="10"
android:padding="10dip">
</ProgressBar>
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start Progress" >
</Button>
<TextView android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Replace"/>
</LinearLayout>
Vale, estás intentando acceder a la GUI a través de otro hilo. Esto, en general, no es una buena práctica.
La AsyncTask ejecuta todo en doInBackground()
dentro de otro hilo, que no tiene acceso a la GUI donde están tus vistas.
preExecute()
y postExecute()
te ofrecen acceso a la GUI antes y después de que el trabajo pesado ocurra en este nuevo hilo, incluso puedes pasar el resultado de la operación larga a postExecute()
para luego mostrar cualquier resultado del procesamiento.
Vea estas líneas donde se actualiza posteriormente su TextView:
TextView txt = findViewById(R.id.output);
txt.setText("Executed");
ponlas en onPostExecute()
Entonces verás el texto de tu TextView actualizado después de que el doInBackground
se complete.
**Me he dado cuenta de que tu oyente onClick no comprueba qué vista ha sido seleccionada. Creo que la forma más fácil de hacer esto es a través de declaraciones de conmutación. Tengo una clase completa editada abajo con todas las sugerencias para ahorrar confusión.
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.Settings.System;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.view.View.OnClickListener;
public class AsyncTaskActivity extends Activity implements OnClickListener {
Button btn;
AsyncTask<?, ?, ?> runningTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn = findViewById(R.id.button1);
// because we implement OnClickListener we only have to pass "this"
// (much easier)
btn.setOnClickListener(this);
}
@Override
public void onClick(View view) {
// detect the view that was "clicked"
switch (view.getId()) {
case R.id.button1:
if (runningTask != null) runningTask.cancel(true);
runningTask = new LongOperation();
runningTask.execute();
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// cancel running task(s) to avoid memory leaks
if (runningTask != null) runningTask.cancel(true);
}
private final class LongOperation extends AsyncTask<Void, Void, String> {
@Override
protected String doInBackground(Void... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// we were cancelled, stop sleeping!
}
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed"); // txt.setText(result);
// might want to change "executed" for the returned string passed
// into onPostExecute() but that is upto you
}
}
}
Seguro que se está ejecutando correctamente, pero estás intentando cambiar los elementos de la UI en el hilo de fondo y eso no sirve.
Revisa tu llamada y AsyncTask de la siguiente manera:
Clase de llamada
Nota: Personalmente sugiero usar onPostExecute()
en cualquier lugar donde ejecutes tu hilo AsyncTask y no en la clase que extiende el propio AsyncTask. Creo que hace que el código sea más fácil de leer, especialmente si usted necesita el AsyncTask en varios lugares manejando los resultados ligeramente diferentes.
new LongThread() {
@Override public void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}.execute("");
Clase LongThread (extiende AsyncTask):
@Override
protected String doInBackground(String... params) {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "Executed";
}
Mueve estas dos líneas:
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
fuera del método doInBackground
de tu AsyncTask y ponlas en el método onPostExecute
. Tu AsyncTask
debería tener este aspecto:
private class LongOperation extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... params) {
try {
Thread.sleep(5000); // no need for a loop
} catch (InterruptedException e) {
Log.e("LongOperation", "Interrupted", e);
return "Interrupted";
}
return "Executed";
}
@Override
protected void onPostExecute(String result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText(result);
}
}