Как обрабатывать возвращаемое значение из AsyncTask
Я использую класс AsyncTask
со следующей сигнатурой:
public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
...
private String POST(List<NameValuePair>[] nameValuePairs){
...
return response;
}
}
protected String doInBackground(List<NameValuePair>... nameValuePairs) {
return POST(params);
}
Я пытаюсь вызвать его из другого класса через:
ApiAccess apiObj = new ApiAccess (0, "/User");
// String signupResponse = apiObj.execute(nameValuePairs);
String serverResponse = apiObj.execute(nameValuePairs); //ERROR
Но здесь я получаю эту ошибку:
Type mismatch: cannot convert from AsyncTask<List<NameValuePair>,Integer,String> to String
Почему это, когда я указал String
в качестве третьего параметра в строке расширения класса?
Ответы
Ответ 1
Вы можете получить результат, вызвав метод AsyhncTask get() в возвращаемой AsyncTask, но он превратит его из асинхронной задачи в синхронную задачу, поскольку он ждет получения результата.
String serverResponse = apiObj.execute(nameValuePairs).get();
Поскольку у вас есть AsyncTask в отдельном классе, вы можете создать класс интерфейса и объявить его в AsyncTask и реализовать новый класс интерфейса в качестве делегата в классе, к которому вы хотите получить доступ. Хорошее руководство здесь: Как получить результат OnPostExecute() для основного действия, потому что AsyncTask - это отдельный класс?.
Я попытаюсь применить вышеуказанную ссылку к вашему контексту.
(IApiAccessResponse)
public interface IApiAccessResponse {
void postResult(String asyncresult);
}
(ApiAccess)
public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
...
public IApiAccessResponse delegate=null;
protected String doInBackground(List<NameValuePair>... nameValuePairs) {
//do all your background manipulation and return a String response
return response
}
@Override
protected void onPostExecute(String result) {
if(delegate!=null)
{
delegate.postResult(result);
}
else
{
Log.e("ApiAccess", "You have not assigned IApiAccessResponse delegate");
}
}
}
(Ваш основной класс, который реализует IApiAccessResponse)
ApiAccess apiObj = new ApiAccess (0, "/User");
//Assign the AsyncTask delegate to your class context (this links your asynctask and this class together)
apiObj.delegate = this;
apiObj.execute(nameValuePairs); //ERROR
//this method has to be implement so that the results can be called to this class
void postResult(String asyncresult){
//This method will get call as soon as your AsyncTask is complete. asyncresult will be your result.
}
Ответ 2
Я бы предложил реализовать обратный вызов обработчика. Вы передадите обработчик фрагмента (или активности) в AsyncTask, который AsyncTask вызовет, когда он будет завершен. AsyncTask также может передать произвольный объект.
Вот пример AsyncTask, который у меня есть в его собственном файле (не подклассом):
public class MyTask extends AsyncTask<Void, String, String> {
private static final String TAG = "MyTask";
private Handler mCallersHandler;
private Candy mObject1;
private Popsicle mObject2;
// Return codes
public static final int MSG_FINISHED = 1001;
public SaveVideoTask(Handler handler, Candy candyCane, Popsicle grapePop ) {
this.mCallersHandler = handler;
this.mObject1 = candyCane;
this.mObject2 = grapePop;
}
@Override
protected String doInBackground(Void... params) {
// Do all of the processing that you want to do...
// You already have the private fields because of the constructor
// so you can use mObject1 and mObject2
Dessert objectToReturn = mObject1 + mObject2;
// Tell the handler (usually from the calling thread) that we are finished,
// returning an object with the message
mCallersHandler.sendMessage( Message.obtain( mCallersHandler, MSG_FINISHED, objectToReturn ) );
return (null);
}
}
В этом примере предполагается, что вашему AsyncTask нужен кусок Candy и Popsicle. Затем он вернет десерт в ваш фрагмент.
Вы можете создать и запустить AsyncTask в одной строке из вашего фрагмента с помощью:
( new MyTask( mFragmentHandler, candyCane, grapePop ) ).execute();
Но, конечно, сначала вам нужно настроить обработчик фрагмента (myFragmentHandler). Чтобы сделать это, ваш фрагмент (или действие) должен выглядеть (ПРИМЕЧАНИЕ: "реализует Handler.Callback" ):
public class MyFragment extends Fragment implements Handler.Callback {
private Handler mFragmentHandler;
private Candy candyCane;
private Popsicle grapePop;
@Override
public void onCreate(Bundle savedInstanceState) {
// Standard creation code
super.onCreate(savedInstanceState);
setRetainInstance(true);
// Create a handler for this fragment
mFragmentHandler = new Handler(this);
// Other stuff...
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
// Inflate the layout
View v = inflater.inflate(R.layout.my_fragment_layout, parent, false );
// The candyCane and grapePop don't need to be set up here, but
// they MUST be set up before the button is pressed.
// Here would be a good place to at least initialize them...
// Perhaps you have a button in "my_fragment_layout" that triggers the AsyncTask...
Button mButton = (Button) v.findViewById(R.id.mButton);
mButton.setOnClickListener( new OnClickListener() {
@Override
public void onClick(View v) {
( new MyTask( mFragmentHandler, candyCane, grapePop ) ).execute();
}
});
return v;
}
@SuppressWarnings("unchecked")
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MyTask.MSG_FINISHED:
// Let see what we are having for dessert
Dessert myDessert = (Dessert) msg.obj;
break;
}
return false;
}
}
Если вы используете эти фрагменты кода, нажатие кнопки вызывает функцию AsyncTask. Вызывающий фрагмент будет продолжать выполняться во время обработки AsyncTask. Затем, когда AsyncTask будет завершен, он отправит сообщение фрагменту, в котором говорится, что оно закончено, и передать объект с сообщением. На этом этапе фрагмент увидит сообщение и сделает все, что вам нужно.
Примечание: могут быть опечатки. Это вырезано из очень большого и сложного кода.
Ответ 3
Прочитайте AsyncTask. Вы можете получить результат по методу onPostExecute
. Вы не можете сделать что-то вроде:
String serverResponse = apiObj.execute(nameValuePairs);
потому что он асинхронный.
Ответ 4
Проблема заключается в том, что при вызове execute возвращается объект AsyncTask, но не результат. Результат вычисляется в фоновом режиме. Тип результата в конечном итоге будет String (как вы указали) и будет передан в onPostExecute()
.
Вы должны использовать AsyncTask
следующим образом:
public class ApiAccess extends AsyncTask<List<NameValuePair>, Integer, String> {
...
private String POST(List<NameValuePair>[] nameValuePairs){
...
return response;
}
protected void onPreExecute (){
// this is run on the main (UI) thread, before doInBackground starts
}
protected void onPostExecute (String result){
// this is run on the main (UI) thread, after doInBackground returns
}
protected String doInBackground(List<NameValuePair>... nameValuePairs) {
// run in another, background thread
return POST(params);
}
}
Обратите внимание, что в вашем примере вы не возвращаете результат в doInBackground()
, который вы должны.