android - thread - manejo de asynctask
¿Cómo obtener el resultado de OnPostExecute() para la actividad principal porque AsyncTask es una clase separada? (14)
¿Por qué la gente lo hace tan difícil?
Esto debería ser suficiente.
No implemente onPostExecute en la tarea asíncrona, sino implementarlo en la Actividad:
public class MainActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState) {
//execute the async task
MyAsyncTask task = new MyAsyncTask(){
protected void onPostExecute(String result) {
//Do your thing
}
}
task.execute("Param");
}
}
Tengo estas dos clases. Mi actividad principal y la que extiende la AsyncTask
, ahora en mi actividad principal, necesito obtener el resultado de OnPostExecute()
en la AsyncTask
. ¿Cómo puedo pasar o llevar el resultado a mi actividad principal?
Aquí están los códigos de muestra.
Mi actividad principal.
public class MainActivity extends Activity{
AasyncTask asyncTask = new AasyncTask();
@Override
public void onCreate(Bundle aBundle) {
super.onCreate(aBundle);
//Calling the AsyncTask class to start to execute.
asyncTask.execute(a.targetServer);
//Creating a TextView.
TextView displayUI = asyncTask.dataDisplay;
displayUI = new TextView(this);
this.setContentView(tTextView);
}
}
Esta es la clase AsyncTask
public class AasyncTask extends AsyncTask<String, Void, String> {
TextView dataDisplay; //store the data
String soapAction = "http://sample.com"; //SOAPAction header line.
String targetServer = "https://sampletargeturl.com"; //Target Server.
//SOAP Request.
String soapRequest = "<sample XML request>";
@Override
protected String doInBackground(String... string) {
String responseStorage = null; //storage of the response
try {
//Uses URL and HttpURLConnection for server connection.
URL targetURL = new URL(targetServer);
HttpURLConnection httpCon = (HttpURLConnection) targetURL.openConnection();
httpCon.setDoOutput(true);
httpCon.setDoInput(true);
httpCon.setUseCaches(false);
httpCon.setChunkedStreamingMode(0);
//properties of SOAPAction header
httpCon.addRequestProperty("SOAPAction", soapAction);
httpCon.addRequestProperty("Content-Type", "text/xml; charset=utf-8");
httpCon.addRequestProperty("Content-Length", "" + soapRequest.length());
httpCon.setRequestMethod(HttpPost.METHOD_NAME);
//sending request to the server.
OutputStream outputStream = httpCon.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
writer.write(soapRequest);
writer.flush();
writer.close();
//getting the response from the server
InputStream inputStream = httpCon.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
ByteArrayBuffer byteArrayBuffer = new ByteArrayBuffer(50);
int intResponse = httpCon.getResponseCode();
while ((intResponse = bufferedReader.read()) != -1) {
byteArrayBuffer.append(intResponse);
}
responseStorage = new String(byteArrayBuffer.toByteArray());
} catch (Exception aException) {
responseStorage = aException.getMessage();
}
return responseStorage;
}
protected void onPostExecute(String result) {
aTextView.setText(result);
}
}
Crea un miembro estático en tu clase de actividad. Luego asigne el valor durante el onPostExecute
Por ejemplo, si el resultado de su AsyncTask es una cadena, cree una cadena estática pública en su actividad
public static String dataFromAsyncTask;
Luego, en el onPostExecute
de AsyncTask, simplemente haga una llamada estática a su clase principal y establezca el valor.
MainActivity.dataFromAsyncTask = "result blah";
Esta respuesta puede ser tardía, pero me gustaría mencionar algunas cosas cuando su Activity
dependa de AsyncTask
. Eso te ayudaría a prevenir bloqueos y manejo de memoria. Como ya se mencionó en las respuestas anteriores con la interface
, también les decimos devoluciones de llamada. Trabajarán como informantes, pero nunca enviarán una referencia fuerte de Activity
o interface
siempre use una referencia débil en esos casos.
Consulte la siguiente captura de pantalla para saber cómo puede causar problemas.
Como puede ver si iniciamos AsyncTask
con una referencia sólida , no hay garantía de que nuestra Activity
/ Fragment
esté vivo hasta que obtengamos datos, por lo que sería mejor usar WeakReference
en esos casos y eso también ayudará en la gestión de la memoria como Nunca mantendremos la fuerte referencia de nuestra Activity
entonces será elegible para la recolección de basura después de su distorsión.
Consulte el fragmento de código a continuación para averiguar cómo usar la impresionante WeakReference:
Interfaz MyTaskInformer.java
que funcionará como informante.
public interface MyTaskInformer {
void onTaskDone(String output);
}
MySmallAsyncTask.java
AsyncTask para realizar una tarea de larga ejecución, que utilizará WeakReference
.
public class MySmallAsyncTask extends AsyncTask<String, Void, String> {
// ***** Hold weak reference *****
private WeakReference<MyTaskInformer> mCallBack;
public MySmallAsyncTask(MyTaskInformer callback) {
this.mCallBack = new WeakReference<>(callback);
}
@Override
protected String doInBackground(String... params) {
// Here do whatever your task is like reading/writing file
// or read data from your server or any other heavy task
// Let us suppose here you get response, just return it
final String output = "Any out, mine is just demo output";
// Return it from here to post execute
return output;
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// Here you can''t guarantee that Activity/Fragment is alive who started this AsyncTask
// Make sure your caller is active
final MyTaskInformer callBack = mCallBack.get();
if(callBack != null) {
callBack.onTaskDone(s);
}
}
}
MainActivity.java
Esta clase se usa para iniciar la interface
mi aplicación AsyncTask
en esta clase y override
este método obligatorio.
public class MainActivity extends Activity implements MyTaskInformer {
private TextView mMyTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = (TextView) findViewById(R.id.tv_text_view);
// Start your AsyncTask and pass reference of MyTaskInformer in constructor
new MySmallAsyncTask(this).execute();
}
@Override
public void onTaskDone(String output) {
// Here you will receive output only if your Activity is alive.
// no need to add checks like if(!isFinishing())
mMyTextView.setText(output);
}
}
Fácil:
Cree una clase de
interface
, dondeString output
es opcional, o puede ser cualquier variable que desee devolver.public interface AsyncResponse { void processFinish(String output); }
Vaya a su clase
AsyncTask
y declare la interfazAsyncResponse
como un campo:public class MyAsyncTask extends AsyncTask<Void, Void, String> { public AsyncResponse delegate = null; @Override protected void onPostExecute(String result) { delegate.processFinish(result); } }
En su actividad principal, necesita
implements
interfazAsyncResponse
.public class MainActivity implements AsyncResponse{ MyAsyncTask asyncTask =new MyAsyncTask(); @Override public void onCreate(Bundle savedInstanceState) { //this to set delegate/listener back to this class asyncTask.delegate = this; //execute the async task asyncTask.execute(); } //this override the implemented method from asyncTask @Override void processFinish(String output){ //Here you will receive the result fired from async class //of onPostExecute(result) method. } }
ACTUALIZAR
No sabía que este es un favorito para muchos de ustedes. Así que aquí está la forma simple y cómoda de usar la interface
.
sigue utilizando la misma interface
. Para su información, puede combinar esto en la clase AsyncTask
.
en clase AsyncTask
:
public class MyAsyncTask extends AsyncTask<Void, Void, String> {
// you may separate this or combined to caller class.
public interface AsyncResponse {
void processFinish(String output);
}
public AsyncResponse delegate = null;
public MyAsyncTask(AsyncResponse delegate){
this.delegate = delegate;
}
@Override
protected void onPostExecute(String result) {
delegate.processFinish(result);
}
}
haz esto en tu clase de Activity
public class MainActivity extends Activity {
MyAsyncTask asyncTask = new MyAsyncTask(new AsyncResponse(){
@Override
void processFinish(String output){
//Here you will receive the result fired from async class
//of onPostExecute(result) method.
}
}).execute();
}
O bien, implementando de nuevo la interfaz en la Actividad.
public class MainActivity extends Activity
implements AsyncResponse{
@Override
public void onCreate(Bundle savedInstanceState) {
//execute the async task
new MyAsyncTask(this).execute();
}
//this override the implemented method from AsyncResponse
@Override
void processFinish(String output){
//Here you will receive the result fired from async class
//of onPostExecute(result) method.
}
}
Como puede ver las 2 soluciones anteriores, la primera y la tercera, debe crear el método processFinish
, la otra, el método está dentro del parámetro de la persona que llama. El tercero es más ordenado porque no hay ninguna clase anónima anidada. Espero que esto ayude
Consejo : Cambie String output
, String response
y String result
a diferentes tipos coincidentes para obtener diferentes objetos.
Hay algunas opciones:
Anida la clase
AsyncTask
dentro de tu clase deActivity
. Suponiendo que no utilice la misma tarea en varias actividades, esta es la forma más fácil. Todo su código permanece igual, solo mueve la clase de tarea existente para que sea una clase anidada dentro de la clase de su actividad.public class MyActivity extends Activity { // existing Activity code ... private class MyAsyncTask extends AsyncTask<String, Void, String> { // existing AsyncTask code ... } }
Cree un constructor personalizado para su
AsyncTask
que tome una referencia a suActivity
. Instalaría la tarea con algo comonew MyAsyncTask(this).execute(param1, param2)
.public class MyAsyncTask extends AsyncTask<String, Void, String> { private Activity activity; public MyAsyncTask(Activity activity) { this.activity = activity; } // existing AsyncTask code ... }
Hola puedes hacer algo como esto:
Crear una clase que implemente AsyncTask.
// TASK public class SomeClass extends AsyncTask<Void, Void, String>> { private OnTaskExecutionFinished _task_finished_event; public interface OnTaskExecutionFinished { public void OnTaskFihishedEvent(String Reslut); } public void setOnTaskFinishedEvent(OnTaskExecutionFinished _event) { if(_event != null) { this._task_finished_event = _event; } } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected String doInBackground(Void... params) { // do your background task here ... return "Done!"; } @Override protected void onPostExecute(String result) { super.onPostExecute(result); if(this._task_finished_event != null) { this._task_finished_event.OnTaskFihishedEvent(result); } else { Log.d("SomeClass", "task_finished even is null"); } } }
Añadir en Actividad Principal
// MAIN ACTIVITY public class MyActivity extends ListActivity { ... SomeClass _some_class = new SomeClass(); _someclass.setOnTaskFinishedEvent(new _some_class.OnTaskExecutionFinished() { @Override public void OnTaskFihishedEvent(String result) { Toast.makeText(getApplicationContext(), "Phony thread finished: " + result, Toast.LENGTH_SHORT).show(); } }); _some_class.execute(); ... }
Lo hago funcionar usando hilos y manejador / mensaje. Pasos como sigue: Declare un diálogo de progreso
ProgressDialog loadingdialog;
Cree una función para cerrar el diálogo cuando la operación haya finalizado.
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
loadingdialog.dismiss();
}
};
Codifique los detalles de su ejecución:
public void startUpload(String filepath) {
loadingdialog = ProgressDialog.show(MainActivity.this, "Uploading", "Uploading Please Wait", true);
final String _path = filepath;
new Thread() {
public void run() {
try {
UploadFile(_path, getHostName(), getPortNo());
handler.sendEmptyMessage(0);
} catch (Exception e) {
Log.e("threadmessage", e.getMessage());
}
}
}.start();
}
Puede hacerlo en unas pocas líneas, simplemente anule onPostExecute cuando llame a su AsyncTask. Aquí hay un ejemplo para ti:
new AasyncTask()
{
@Override public void onPostExecute(String result)
{
// do whatever you want with result
}
}.execute(a.targetServer);
Espero que te haya ayudado, feliz codding :)
Puede llamar al método get()
de AsyncTask
(o al método AsyncTask
sobrecargado get(long, TimeUnit)
). Este método se bloqueará hasta que AsyncTask
haya completado su trabajo, momento en el que le devolverá el Result
.
Sería prudente realizar otro trabajo entre la creación / inicio de su tarea asíncrona y llamar al método get
, de lo contrario, no está utilizando la tarea asíncrona de manera muy eficiente.
Puedes escribir tu propio oyente. Es la misma respuesta de HelmiB pero parece más natural:
Crear interfaz de escucha:
public interface myAsyncTaskCompletedListener {
void onMyAsynTaskCompleted(int responseCode, String result);
}
Luego escribe tu tarea asíncrona:
public class myAsyncTask extends AsyncTask<String, Void, String> {
private myAsyncTaskCompletedListener listener;
private int responseCode = 0;
public myAsyncTask() {
}
public myAsyncTask(myAsyncTaskCompletedListener listener, int responseCode) {
this.listener = listener;
this.responseCode = responseCode;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... params) {
String result;
String param = (params.length == 0) ? null : params[0];
if (param != null) {
// Do some background jobs, like httprequest...
return result;
}
return null;
}
@Override
protected void onPostExecute(String finalResult) {
super.onPostExecute(finalResult);
if (!isCancelled()) {
if (listener != null) {
listener.onMyAsynTaskCompleted(responseCode, finalResult);
}
}
}
}
Finalmente implementar el oyente en la actividad:
public class MainActivity extends AppCompatActivity implements myAsyncTaskCompletedListener {
@Override
public void onMyAsynTaskCompleted(int responseCode, String result) {
switch (responseCode) {
case TASK_CODE_ONE:
// Do something for CODE_ONE
break;
case TASK_CODE_TWO:
// Do something for CODE_TWO
break;
default:
// Show some error code
}
}
Y así es como puedes llamar a asyncTask:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Some other codes...
new myAsyncTask(this,TASK_CODE_ONE).execute("Data for background job");
// And some another codes...
}
Puedes probar este código en tu clase principal. Eso me funcionó, pero he implementado métodos de otra manera.
try {
String receivedData = new AsyncTask().execute("http://yourdomain.com/yourscript.php").get();
}
catch (ExecutionException | InterruptedException ei) {
ei.printStackTrace();
}
Sentí que el enfoque de abajo es muy fácil.
He declarado una interfaz para devolución de llamada
public interface AsyncResponse {
void processFinish(Object output);
}
Luego creó una tarea asíncrona para responder todo tipo de solicitudes paralelas
public class MyAsyncTask extends AsyncTask<Object, Object, Object> {
public AsyncResponse delegate = null;//Call back interface
public MyAsyncTask(AsyncResponse asyncResponse) {
delegate = asyncResponse;//Assigning call back interfacethrough constructor
}
@Override
protected Object doInBackground(Object... params) {
//My Background tasks are written here
return {resutl Object}
}
@Override
protected void onPostExecute(Object result) {
delegate.processFinish(result);
}
}
Luego llamó a la tarea asíncrona al hacer clic en un botón en la clase de actividad.
public class MainActivity extends Activity{
@Override
public void onCreate(Bundle savedInstanceState) {
Button mbtnPress = (Button) findViewById(R.id.btnPress);
mbtnPress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyAsyncTask asyncTask =new MyAsyncTask(new AsyncResponse() {
@Override
public void processFinish(Object output) {
Log.d("Response From Asynchronous task:", (String) output);
mbtnPress.setText((String) output);
}
});
asyncTask.execute(new Object[] { "Your request to aynchronous task class is giving here.." });
}
});
}
}
Gracias
en su Oncreate ():
`
myTask.execute("url");
String result = "";
try {
result = myTask.get().toString();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} `
AsynTask
usar "protocolos" para delegar o proporcionar datos a la AsynTask
.
Delegados y fuentes de datos
Un delegado es un objeto que actúa en nombre de, o en coordinación con, otro objeto cuando ese objeto encuentra un evento en un programa. ( Definición de Apple )
Los protocolos son interfaces que definen algunos métodos para delegar algunos comportamientos.
Aquí hay un ejemplo completo !!!