使用asynctask从加载程序返回一些数据时遇到麻烦。
使用一些日志,我可以看到loadInBackground正在被调用,它调用了改造以加载JSON,并退出了调用liverResult。关键是当它退出时,负载尚未完成,因此它将空列表传递给deliveryResult,我也得到了一个onLoadFinished,列表大小为0。
为什么它不等待改造调用,然后用加载的数据调用liverResult?
日志(最后一个是对实际数据进行改造后得到的响应,但此时它已称为deliveryResult):
08-07 22:10:57.309 6872-6872 / com.example.butterknife V / RAG:onCreateLoader() 08-07 22:10:57.315 6872-6872 / com.example.butterknife V / RAG:GetRecipeAsyncTask onStartLoading():
08-07 22:10:57.413 6872-6889 / com.example.butterknife V / RAG:loadInBackground列表大小:0
08-07 22:10:58.533 6872-6872 / com.example.butterknife V / RAG:deliverResult数据大小:0 onLoadFinished()数据大小:0
08-07 22:10:59.734 6872-6872 / com.example.butterknife V / RAG:改造成功内的loadInBackground成功():4
感谢您的时间。
github在这里:https://github.com/rag-lab/ButterKnifeLab
具有全部功能的片段:
package com.example.rodrigoaugusto.butterknife;
import android.content.Context;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.Inflater;
import butterknife.BindView;
import butterknife.ButterKnife;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class FragmentRecipe extends Fragment implements android.support.v4.app.LoaderManager.LoaderCallbacks<List<Recipes>>{
View v;
private RecyclerView myrecview;
private List<Recipes> lstRecipes = new ArrayList<>();
private static final int thumbLoaderID= 22;
private Bundle queryBundle = new Bundle(); //usado no loader das recipes
private static final String SEARCH_URL = ""; //chave do bundle
//@BindView(R.id.listRecipes_recView) RecyclerView myrecview;
public FragmentRecipe() {
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
//ButterKnife.bind(container);
v = inflater.inflate(R.layout.fragment_listrecipe, container, false);
myrecview = (RecyclerView) v.findViewById(R.id.listRecipes_recView);
Recipe_RV_Adapter listRecipe_recViewAdapter = new Recipe_RV_Adapter(container.getContext(),lstRecipes);
myrecview.setLayoutManager(new LinearLayoutManager(getActivity()));
myrecview.setAdapter(listRecipe_recViewAdapter);
return v;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LoaderManager loaderManager = getLoaderManager();
Loader<String> thumbsLoader = loaderManager.getLoader(thumbLoaderID);
queryBundle.putString(SEARCH_URL, Api.BASEURL);
if (thumbsLoader == null) {
loaderManager.initLoader(thumbLoaderID, queryBundle, this);
} else {
loaderManager.restartLoader(thumbLoaderID, queryBundle, this);
}
}
/* method not being used*/
private void getRecipes() {
/*
Steps stepItem = new Steps("12", "short desc", "description", "videourl", "thumbUrl");
Steps[] stepItens = {stepItem,stepItem};
Ingredients ingredient = new Ingredients("measure", "ingredient", "qtd");
Ingredients[] ingredients = {ingredient,ingredient};
lstRecipes.add(new Recipes("id","servings","name1","image", stepItens, ingredients));
lstRecipes.add(new Recipes("id","servings","name2","image", stepItens, ingredients));
lstRecipes.add(new Recipes("id","servings","name3","image", stepItens, ingredients));
*/
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Api api = retrofit.create(Api.class);
Call<List<Recipes>> call = api.getRecipes();
call.enqueue(new Callback<List<Recipes>>() {
@Override
public void onResponse(Call<List<Recipes>> call, Response<List<Recipes>> response) {
//Snackbar snackbar = Snackbar
// .make(layout, "success", Snackbar.LENGTH_LONG);
//snackbar.show();
//lstRecipes = response.body();
List<Recipes> recipes = response.body();
for(Recipes r: recipes){
Recipes tmp = new Recipes(r.getId(), r.getServings(), r.getName(), r.getImage(), r.getSteps(), r.getIngredients());
lstRecipes.add(tmp);
}
}
@Override
public void onFailure(Call<List<Recipes>> call, Throwable t) {
Log.v("RAG", "erro:"+t.toString());
/*
Snackbar snackbar = Snackbar
.make(layout, t.getMessage(), Snackbar.LENGTH_LONG);
snackbar.show();
*/
}
});
}
//
//LOADER
//
@Override
public Loader<List<Recipes>> onCreateLoader(int id, final Bundle args) {
Log.v("RAG", "onCreateLoader()");
return new GetRecipeAsyncTask(getContext());
}
@Override
public void onLoadFinished(Loader<List<Recipes>> loader, List<Recipes> data) {
Log.v("RAG", "onLoadFinished() data size:" + data.size());
}
@Override
public void onLoaderReset(Loader<List<Recipes>> loader) {
//Log.v("RAG", "onLoaderReset()");
}
/*
//END LOADER
*/
static class GetRecipeAsyncTask extends AsyncTaskLoader<List<Recipes>>
{
List<Recipes> tmpLstRecipes;
public GetRecipeAsyncTask(Context context) {
super(context);
}
@Override
protected void onStartLoading() {
/*
super.onStartLoading();
forceLoad();
*/
//if (args == null) return;
Log.v("RAG", "GetRecipeAsyncTask onStartLoading():");
//pega do cache ou carrega
if (tmpLstRecipes != null) {
deliverResult(tmpLstRecipes);
} else {
this.forceLoad();
}
}
@Override
public List<Recipes> loadInBackground() {
tmpLstRecipes = new ArrayList<>();
try {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
Api api = retrofit.create(Api.class);
Call<List<Recipes>> call = api.getRecipes();
call.enqueue(new Callback<List<Recipes>>() {
@Override
public void onResponse(Call<List<Recipes>> call, Response<List<Recipes>> response) {
//Snackbar snackbar = Snackbar
// .make(layout, "success", Snackbar.LENGTH_LONG);
//snackbar.show();
//lstRecipes = response.body();
List<Recipes> recipes = response.body();
for(Recipes r: recipes){
Recipes tmp = new Recipes(r.getId(), r.getServings(), r.getName(), r.getImage(), r.getSteps(), r.getIngredients());
tmpLstRecipes.add(tmp);
}
Log.v("RAG", "loadInBackground inside retrofit success():"+ tmpLstRecipes.size());
}
@Override
public void onFailure(Call<List<Recipes>> call, Throwable t) {
Log.v("RAG", "loadInBackground onFailure():"+ t.toString());
/*
Snackbar snackbar = Snackbar
.make(layout, t.getMessage(), Snackbar.LENGTH_LONG);
snackbar.show();
*/
}
});
} catch (Exception e) {
e.printStackTrace();
}
Log.v("RAG", "loadInBackground list size:"+tmpLstRecipes.size());
return tmpLstRecipes;
}
@Override
public void deliverResult(List<Recipes> data) {
Log.v("RAG", "deliverResult data size:"+data.size());
// Hold a reference to the old data so it doesn't get garbage collected.
// We must protect it until the new data has been delivered.
List<Recipes> oldData = tmpLstRecipes;
tmpLstRecipes = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
//releaseResources(oldData);
}
}
}
}
答案 0 :(得分:0)
从“ call.enqueue()”更改为“ call.execute.body()”。
问题是“入队”异步执行请求,并在将来准备就绪时返回响应。当执行“ loadInBackground”时,它要求改型执行一个请求,仅此而已,任务就完成了。
为确保仅在完全执行请求后退出任务,应使用“ call.execute.body”。这样,改造将同步执行您的请求,因此GetRecipeAsyncTask仅在您有响应时完成。
通常,您使用AsyncTask / AsyncTaskLoader执行同步操作异步。
这里有一些链接,第一个解释了使用改进的同步/异步请求之间的区别,第二个包含了有关AsyncTask和AsyncTaskLoader的信息:
https://futurestud.io/tutorials/retrofit-synchronous-and-asynchronous-requests