java - tutorial - thread android studio ejemplo
Devuelve un valor de AsyncTask en Android (9)
Esta pregunta ya tiene una respuesta aquí:
Una pregunta simple: ¿es posible devolver un valor en AsyncTask
?
//AsyncTask is a member class
private class MyTask extends AsyncTask<Void, Void, Void>{
protected Void doInBackground(Void... params) {
//do stuff
return null;
}
@Override
protected void onPostExecute(Void result) {
//do stuff
//how to return a value to the calling method?
}
}
Y luego dentro de mi Activity
/ Fragment
:
// The task is started from activity
myTask.execute()
// something like this?
myvalue = myTask.getvalue()
EDITAR: Esto fue preguntado hace mucho tiempo cuando no estaba familiarizado con Java, ahora que estoy mejor con él, haré un resumen rápido:
El objetivo de la tarea asincrónica es que la tarea es asynchronous
, lo que significa que después de execute()
en la tarea, la tarea comienza a ejecutarse en un hilo propio. devolver un valor de asynctask no tendría sentido porque el hilo de llamada original ya ha seguido haciendo otras cosas (por lo tanto, la tarea es asincrónica).
Piense en el tiempo: en un punto del tiempo, comenzó una tarea que se ejecutará en paralelo con el hilo principal. Cuando se completa la tarea de ejecución paralela, también ha transcurrido el tiempo en el hilo principal. La tarea paralela no puede retroceder en el tiempo para devolver un valor al hilo principal.
Venía de C, así que no sabía mucho sobre esto. Pero parece que mucha gente tiene la misma pregunta, así que pensé en aclararlo un poco.
Usa parámetros genéricos
AsyncTask<Params, Progress, Result>
-
Params
- tipo de datos de entrada de la tarea -
Progress
: cómo informar al mundo sobre el progreso -
Result
: tipo de datos de salida de la tarea
Piensa como
Result = task(Params)
Ejemplo
Cargue YourObject
por cadena URL:
new AsyncTask<String, Void, YourObject>()
{
@Override
protected void onPreExecute()
{
/* Called before task execution; from UI thread, so you can access your widgets */
// Optionally do some stuff like showing progress bar
};
@Override
protected YourObject doInBackground(String... url)
{
/* Called from background thread, so you''re NOT allowed to interact with UI */
// Perform heavy task to get YourObject by string
// Stay clear & functional, just convert input to output and return it
YourObject result = callTheServer(url);
return result;
}
@Override
protected void onPostExecute(YourObject result)
{
/* Called once task is done; from UI thread, so you can access your widgets */
// Process result as you like
}
}.execute("http://www.example.com/");
¿Por qué no llamar a un método que maneja el valor?
public class MyClass extends Activity {
private class myTask extends AsyncTask<Void, Void, Void> {
//initiate vars
public myTask() {
super();
//my params here
}
protected Void doInBackground(Void... params) {
//do stuff
return null;
}
@Override
protected void onPostExecute(Void result) {
//do stuff
myMethod(myValue);
}
}
private myHandledValueType myMethod(Value myValue) {
//handle value
return myHandledValueType;
}
}
Ejemplo de código: la actividad usa AsyncTask para obtener un valor en una cadena de fondo, luego AsyncTask devuelve el resultado a la Actividad llamando a processValue:
public class MyClass extends Activity
{
private void getValue()
{
new MyTask().execute();
}
void processValue(Value myValue)
{
//handle value
//Update GUI, show toast, etc..
}
private class MyTask extends AsyncTask<Void, Void, Value>
{
@Override
protected Value doInBackground(Void... params)
{
//do stuff and return the value you want
return Value;
}
@Override
protected void onPostExecute(Value result)
{
// Call activity method with results
processValue(result);
}
}
}
La manera más fácil es pasar el objeto que llama a la tarea asíncrona (al construirlo si lo desea):
public class AsyncGetUserImagesTask extends AsyncTask<Void, Void, Void> {
private MyImagesPagerFragment mimagesPagerFragment;
private ArrayList<ImageData> mImages = new ArrayList<ImageData>();
public AsyncGetUserImagesTask(MyImagesPagerFragment imagesPagerFragment) {
this.mimagesPagerFragment = imagesPagerFragment;
}
@Override
public Void doInBackground(Void... records) {
// do work here
return null;
}
@Override
protected void onPostExecute(Void result) {
mimagesPagerFragment.updateAdapter(mImages);
}
}
Y en la clase de llamada (su actividad o fragmento) ejecuta la tarea:
public class MyImagesPagerFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
AsyncGetUserImagesTask mGetImagesTask = new AsyncGetUserImagesTask(this);
mGetImagesTask.execute();
}
Y luego onPostExecuteMethod llamará a cualquier método en la clase de origen que desee, por ejemplo:
public void updateAdapter(List<ImageData> images) {
mImageAdapter.setImages(images);
mImageAdapter.notifyDataSetChanged();
}
}
Necesita usar "protocolos" para delegar o proporcionar datos a 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.
DELEGAR : Captura eventos del objeto en el hilo de fondo
AsynkTask:
public final class TaskWithDelegate extends AsyncTask<..., ..., ...> {
//declare a delegate with type of protocol declared in this task
private TaskDelegate delegate;
//here is the task protocol to can delegate on other object
public interface TaskDelegate {
//define you method headers to override
void onTaskEndWithResult(int success);
void onTaskFinishGettingData(Data result);
}
@Override
protected Integer doInBackground(Object... params) {
//do something in background and get result
if (delegate != null) {
//return result to activity
delegate.onTaskFinishGettingData(result);
}
}
@Override
protected void onPostExecute(Integer result) {
if (delegate != null) {
//return success or fail to activity
delegate.onTaskEndWithResult(result);
}
}
}
Actividad:
public class DelegateActivity extends Activity implements TaskDelegate {
void callTask () {
TaskWithDelegate task = new TaskWithDelegate;
//set the delegate of the task as this activity
task.setDelegate(this);
}
//handle success or fail to show an alert...
@Override
void onTaskEndWithResult(int success) {
}
//handle data to show them in activity...
@Override
void onTaskFinishGettingData(Data result) {
}
}
EDITAR: si llama a delegar en doInBackground, y el delegado intenta editar alguna vista, se bloqueará porque la vista solo puede ser manipulada por el hilo principal.
EXTRA
DATASOURCE: proporciona datos al objeto en el hilo de fondo
AsyncTask:
public final class TaskWithDataSource extends AsyncTask<..., ..., ...> {
//declare a datasource with type of protocol declared in this task
private TaskDataSource dataSource;
private Object data;
//here is the task protocol to can provide data from other object
public interface TaskDataSource {
//define you method headers to override
int indexOfObject(Object object);
Object objectAtIndex(int index);
}
@Override
protected void onPreExecute(Integer result) {
if (dataSource != null) {
//ask for some data
this.data = dataSource.objectAtIndex(0);
}
}
@Override
protected Integer doInBackground(Object... params) {
//do something in background and get result
int index;
if (dataSource != null) {
//ask for something more
index = dataSource.indexOfObject(this.data);
}
}
}
Actividad:
public class DataSourceActivity extends Activity implements TaskDataSource {
void callTask () {
TaskWithDataSource task = new TaskWithDataSource;
//set the datasource of the task as this activity
task.setDataSource(this);
}
//send some data to the async task when it is needed...
@Override
Object objectAtIndex(int index) {
return new Data(index);
}
//send more information...
@Override
int indexOfObject(Object object) {
return new object.getIndex();
}
}
Para eso es en onPostExecute()
. Se ejecuta en el hilo de la interfaz de usuario y puede enviar su resultado desde allí a la pantalla (o en cualquier otro lugar que necesite). No se llamará hasta que el resultado final esté disponible. Si desea entregar resultados intermedios, eche un vistazo a onProgressUpdate()
Para obtener el resultado de una AsyncTask , debe hacerlo después del super.onPostExcecute(result)
; Porque esto significa que el fondo y AsyncTask también han terminado. p.ej:
... into your async task
@Override
protected void onPostExecute(MyBeanResult result) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (mWakeLock.isHeld()) {
mWakeLock.release();
}
super.onPostExecute(result);
MyClassName.this.checkResponseForIntent(result);
}
y el método checkResponseForIntent podría decir algo como esto:
private void checkResponseForIntent(MyBeanResult response) {
if (response == null || response.fault != null) {
noServiceAvailable();
return;
}
... or do what ever you want, even call an other async task...
Tuve este problema al usar .get()
de AsyncTask y ProgressBar simplemente no funciona con get()
, en realidad, solo funciona una vez que doInBackground finaliza.
Espero que te ayude
Pase la clase MainActivity a Async, para que pueda acceder a las funciones MainActivity en la clase interna. Esto funciona bien para mí:
public class MainActivity extends ActionBarActivity {
void callAsync()
{
Async as = new Async(this,12,"Test");
as.execute();
}
public void ReturnThreadResult(YourObject result)
{
// TO DO:
// print(result.toString());
}
}
public class Async extends AsyncTask<String, String, Boolean> {
MainActivity parent;
int param1;
String param2;
public Async(MainActivity parent,int param1,String param2){
this.parent = parent;
this.param1 = param1;
this.param2 = param2;
}
@Override
protected void onPreExecute(){};
@Override
protected YourObject doInBackground(String... url)
{
return result;
}
@Override
protected void onPostExecute(YourObject result)
{
// call an external function as a result
parent.ReturnThreadResult(result);
}
}
puedes probar este: myvalue = new myTask().execute().get();
menos, congelará el proceso hasta que asyncron no termine;