I have an issue with the scope of a variable in Android using Retrofit:
In the MainActivity I use Retrofit to get the JSON reply into a POJO (ApiResponse), create a extendedJourney Object and add it to the extendedJourneyArrayList:
public class MainActivity extends Activity {
private ArrayList<ExtendedJourney> extendedJourneyArrayList = new ArrayList<>();
...
getAPIReply(...){
service.getInfo(..., new getCallback());
...}
private class getCallback implements Callback<ApiResponse> {
public void success(ApiResponse apiResponse, Response response) {
try {
consumeApiData(apiResponse);
}
...
}
}
private void consumeApiData(ApiResponse apiResponse){
ExtendedJourney extendedJourney = new ExtendedJourney(apiResponse, params);
extendedJourneyArrayList.add(extendedJourney);
}
public void getData(View view){
getAPIReply(...);
//Do stuff with the extendedJourneyArrayList
}
Inside consumeApiData() everything is OK, i.e. the extendedJourney Object is correctly created from the apiResponse and other params and the extendedJourneyArrayList is correctly updated with the new extendedJourney.
However, in getData(View view), extendedJourneyArrayList is empty.
How can this be solved? Thanks :D
You are making an asynchronous call.
That means, that after the call to service.getInfo(..., new getCallback()); the flow continues normally, until it's intrrrupted by the callback.
So you code in getData(View v) is probably excecuting before the response is received.
So you should do what you want with the data on the callback ( for example in the end of the consumeApiData(..) after the data is added in the list ), or do a synchronous request ( which you must do in a separate thread ).
Thanks #Kushtrim for your answer. To solve the problem I make of use an AsyncTask to perform synchronous requests, the code now looks like this:
public class MainActivity extends Activity {
private ArrayList<ExtendedJourney> extendedJourneyArrayList = new ArrayList<>();
...
public void getData(View view){
for(int i = 0; i < NUM_REQUESTS; i++){
new getAPIReply().execute(params);
}
}
private class getAPIReply extends AsyncTask<Params, Void, ApiResponse>{
#Override
protected ApiResponse doInBackground(Coords[] coords) {
return service.getRouteInfo(params);
}
#Override
protected void onPostExecute(ApiResponse apiResponse){
try {
consumeApiData(apiResponse);
} catch (JSONException e) {...}
}
private void consumeApiData(ApiResponse apiResponse) throws JSONException{
ExtendedJourney extendedJourney = new ExtendedJourney(apiResponse, params);
extendedJourneyArrayList.add(extendedJourney);
if(extendedJourneyArrayList.size() == NUM_REQUESTS) {
//Do stuff
}
}
Related
I am implementing android pagination library in my app and would like to pass "id" of an item from my activity to the data source where my network call is made
AddCommentActivity.java
//I want to pass this string to the network call.
String image_id = getIntent().getStringExtra("image_id");
CommentViewModel commentViewModel = new ViewModelProvider(this).get(CommentViewModel.class);
CommentDataSource.java
public class CommentDataSource extends PageKeyedDataSource<Long, Comment> {
public CommentDataSource(){
progress_bar = new MutableLiveData<>();
}
#Override
public void loadInitial(#NonNull final LoadInitialParams<Long> params, #NonNull final LoadInitialCallback<Long, Comment> callback) {
RestApi restApi = RetrofitApi.create();
Call<CommentResponse> call = restApi.getComments(FIRST_PAGE, "I want the image_id from activity here");
call.enqueue(new Callback<CommentResponse>() {
#Override
public void onResponse(Call<CommentResponse> call, Response<CommentResponse> response) {
}
CommentDataSourceFactory.java
public class CommentDataFactory extends DataSource.Factory<Long, Comment> {
public MutableLiveData<CommentDataSource> commentLiveDataSource = new MutableLiveData<>();
public CommentDataFactory() {
}
#Override
public DataSource<Long, Comment> create() {
CommentDataSource commentDataSource = new CommentDataSource();
commentLiveDataSource.postValue(commentDataSource);
return commentDataSource;
}
CommentViewModel.java
public class CommentViewModel extends ViewModel {
public LiveData<PagedList<Comment>> commentPagedList;
public LiveData<CommentDataSource> liveDataSource;
public LiveData progressBar;
public CommentViewModel(){
CommentDataFactory commentDataFactory = new CommentDataFactory();
liveDataSource = commentDataFactory.commentLiveDataSource;
progressBar = Transformations.switchMap(liveDataSource, CommentDataSource::getProgressBar);
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(CommentDataSource.PAGE_SIZE)
.build();
commentPagedList = new LivePagedListBuilder<>(commentDataFactory, config).build();
}
public LiveData<PagedList<Comment>> getCommentData(){
return commentPagedList;
}
public void getRefreshedData(){
getCommentData().getValue().getDataSource().invalidate();
}
}
How to do that.? I checked Passing variable to paging library class which is exactly what I want to do but I dont understand it and the code gives errors. Errors such as
Cannot create an instance of class CommentViewModel
CommentViewModel has no zero argument constructor
Okay do:
commentViewmodel1.getCommentData().observe(this, new Observer<PagedList<Comments>>(){
#Override
public void onChanged(PagedList<Comment>
comments){
adapter.submitList(comments);
}
});
Hi I am trying to get an arraylist of data from a an async task class to another main class:
I was following the answer below but I am a little lost:
How to get the result of OnPostExecute() to main activity because AsyncTask is a separate class?
So I have my class that extends async task and calls to the database to get my object array:
public class GetVideoInfoFromDataBase extends AsyncTask {
// Paginated list of results for song database scan
static PaginatedScanList<AlarmDynamoMappingAdapter> results;
// The DynamoDB object mapper for accessing DynamoDB.
private final DynamoDBMapper mapper;
public interface AlarmsDataBaseAsyncResponse {
void processFinish(PaginatedScanList<AlarmDynamoMappingAdapter> output);
}
public AlarmsDataBaseAsyncResponse delegate = null;
public GetVideoInfoFromDataBase(AlarmsDataBaseAsyncResponse delegate){
mapper = AWSMobileClient.defaultMobileClient().getDynamoDBMapper();
this.delegate = delegate;
}
#Override
protected Object doInBackground(Object[] params) {
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
results = mapper.scan(AlarmDynamoMappingAdapter.class, scanExpression);
return results;
}
#Override
public void onPostExecute(Object obj) {
delegate.processFinish(results);
}
}
There are no errors but I think I have done something incorrectly in it causing my error.
So in my main activity to call the results I have:
GetVideoInfoFromDataBase asyncTask =new GetVideoInfoFromDataBase(new GetVideoInfoFromDataBase.AlarmsDataBaseAsyncResponse(){
#Override
public void processFinish(PaginatedScanList<AlarmDynamoMappingAdapter> output) {
}
}).execute();
I have two problems here
I am getting the error:
"incompatible types: AsyncTask cannot be converted to GetVideoInfoFromDataBase"
In the mainactivity where i have:
`new GetVideoInfoFromDataBase(new GetVideoInfoFromDataBase.AlarmsDataBaseAsyncResponse()`
it wants me to cast it like this:
(GetVideoInfoFromDataBase) new GetVideoInfoFromDataBase(new GetVideoInfoFromDataBase.AlarmsDataBaseAsyncResponse()
That doesn't seem right but I thought i would check.
I am not sure how to return the result when overriding the onprocessfinished.
Thanks in advance for your help
First create an Interface
public interface AsyncInterface {
void response(String response);
}
Assign it in the asynctask class as below :-
Context context;
Private AsyncInterface asyncInterface;
AsyncClassConstructor(Context context){
this.context = context;
this.asyncInterface = (AsyncInterface) context;
}
Then inside onPostExecute method of asynctask class :-
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
asyncInterface.response(s);
}
Then implement this interface in your activity :-
class MainActivity extends AppCompatActivity implements AsyncInterface {
and then import the method of asyncInterface
#Override
public void response(String response) {
//Here you get your response
Log.e(TAG, response);
}
Modify Constructor of class.
Need default constructor. By the way, create method to set Interface.
public void setInterface(AlarmsDataBaseAsyncResponse delegate){
this.delegate = delegate;}
In MainActivity, push your logic in:
object.setInterface(new AlarmsDataBaseAsyncResponse(){
#Override
public void processFinish(PaginatedScanList<AlarmDynamoMappingAdapter> output) {
//your logic
}
});
How do I return a list from AsyncTask to a class instead of a method?
I have this AsyncTask which connects to a webpage, reads the page into a string and then splits it all into variables. This is the page
This is the AsyncTask:
public class sellableIds_AsyncTask extends AsyncTask<String, Void, List> {
String bodyHtml;
List<String> items;
private Context context;
private sellableIdsFilter sellableIdsFilter;
public sellableIds_AsyncTask(Context context) {
this.context = context;
}
public void setSellableIdsFilter(sellableIdsFilter sellableIdsFilter) {
this.sellableIdsFilter = sellableIdsFilter;
}
#Override
protected List doInBackground(String... params) { //Incompatible return type
try {
HttpClient httpClient = new DefaultHttpClient();
HttpGet get = new HttpGet(params[0]);
HttpResponse response = httpClient.execute(get);
bodyHtml = EntityUtils.toString(response.getEntity()).replace("[", "");
;
items = Arrays.asList(bodyHtml.split("\\s*,\\s*"));
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return items;
}
#Override
protected void onPostExecute(List result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
this.sellableIdsFilter.sellableIdsFilter(result);
}
public interface sellableIdsFilter {
public void sellableIdsFilter(List result);
}
}
And called in the activity:
sellableIds_AsyncTask sellableIds_AsyncTask = new sellableIds_AsyncTask(this);
sellableIds_AsyncTask.setSellableIdsFilter(this);
sellableIds_AsyncTask.execute(sellableIdsUrl);
How can I return this list to the activity where I call it from and have the list able to use anywhere within the class? instead to the method sellableIdsFilter();
Create a listener interface
public interface OnSellableIdsFilterListener {
void onSellableIdsFilterSuccess(List<Integer> ids);
}
make the class you need to get the ids implement that interface
public class SomeClassThatNeedsTheIDs implements OnSellableIdsFilterListener {
void someVoid() {
//Pass the instance of this class to the constructor of the asynctask as a listener
new YourAsyncTask(this, this).execute();
}
#Override
public void onGetSellableIdsFilterSuccess(List<Integer> ids) {
// do whatever you want with the ids
}
}
Change your asynctask adding a listener to the constructor
public class YourAsyncTask extends AsyncTask< , , > {
Context context;
OnSellableIdsFilterListener listener;
public YourAsyncTask(Context context, OnSellableIdsFilterListener listener) {
this.listener = listener;
this.context = context;
}
// In onPostExecute check if listener!=null and invoke the method, passing the ids
public void onPostExecute() {
if(listener!=null) {
listener.onSellableIdsFilterSuccess(ids);
}
}
}
You can add a method which returns List 'items' in your 'sellableIds_AsyncTask' class. After calling asyncTasks execute , call get() to ensure async task has finished its task. Then call the getter function to get your List 'items'. Hope this helps.
I have to respond to result of volley request. but because it is asynchronous. I need to wait for the results before I proceed. If I don't I will get nullobjects.
How do I set flags and wait until flags are off.
categoryslashid = new JSONObject[Category_IDs.size()];//size of the list containing all categories
taskfinished = new boolean[Category_IDs.size()];
//boolean is initialized to false
//Request to category/{id} to get
for(int i= 0;i<Category_IDs.size();i++)
{ makevolleyrequesforCategorySlashID(Const.URL_FULL_STOREURL+Const.URL_PRODUCT_GET_CATEGORY_ID,i);
}
public void makevolleyrequesforCategorySlashID(URL,id)
{
//volley implementation
public void onResponseoverride
{
categoryslashid[i]=response;
taskfinished[i]=true;
}
}
Now I must proceed after I get all the booleans in task finished become true.
public boolean areAllTrue()
{
for(boolean b : taskfinished) if(!b) return false;
return true;
}
Implement an Interface and use it to call back when your data is ready. Something like this:
public interface OnDownloadTaskCompleted {
public void onTaskCompleted(List<ofSomething> list, boolean error, String message);
}
Then you should pass an instance of this to your request and override onTaskCompleted()
private void downloadData(){
final DownloadUsingVolley downloader = new DownloadUsingVolley(getActivity());
downloader.retrieveData(new OnDownloadTaskCompleted() {
#Override
public void onTaskCompleted(List<ofSomething> list, boolean error, String message) {
//do something with your data
}
});
}
I'm assuming that you have a class where you implemented volley stuff (DownloadusingVolley) and a method do call on it and make the request itself (retrieveData).
retrieveData can be implemented like this:
private void retrieveData(String url, final OnDownloadTaskCompleted taskCompleted){
final JsonObjectRequest request = new JsonObjectRequest(url, null, new Response.Listener<JSONObject>(){
#Override
public void onResponse(JSONObject response) {
try {
//parse
taskCompleted.onTaskCompleted(result,false,null);
}catch (JSONException e){
taskCompleted.onTaskCompleted(0,true,e.getMessage());
}
}
},new Response.ErrorListener(){
#Override
public void onErrorResponse(VolleyError volleyError) {
taskCompleted.onTaskCompleted(0,true,volleyError.getMessage());
}
});
//adding request into the queue
ApplicationClass.getInstance().addToRequestQueue(request,"aTag");
}
Here you can find a nice tutorial about volley:
Asynchronous HTTP Requests in Android Using Volley
I have APIHelper class, whose static methods asynchronously make request to server, receive json string, parse and return Object or ArrayList:
...
public static ArrayList<Item> getItemsInCategory(int id_category) throws ExecutionException, InterruptedException, JSONException {
DoRequest doRequest = new DoRequest();
String jsonString = doRequest.execute(API_PATH + PRODUCT_SEARCH + CATEGORY_ID + id_category).get();
JSONObject jsonObject = new JSONObject(jsonString);
JSONArray jsonArray = jsonObject.getJSONArray("products");
return Item.fromJson(jsonArray);
}
public static Item getItem(int id_item) throws ExecutionException, InterruptedException, JSONException {
DoRequest doRequest = new DoRequest();
String jsonString = doRequest.execute(API_PATH + PRODUCT_GET_INFO + id_item).get();
JSONObject jsonObject = new JSONObject(jsonString);
return Item.fromJson(jsonObject);
}
...
Now I want to make methods without calling get() method from AsyncTask class DoRequest.
My DoRequest class:
public class DoRequest extends AsyncTask<String, Void, String> {
ResultListener mResultListener;
public abstract interface ResultListener{
Object onResultAvailable(String result) throws JSONException;
}
DoRequest(ResultListener resultListener){
mResultListener = resultListener;
}
#Override
protected String doInBackground(String... URL) {
ServiceHandler serviceHandler = new ServiceHandler();
String jsonStr = serviceHandler.makeServiceCall(URL[0]);
return jsonStr;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
try {
mResultListener.onResultAvailable(result);
} catch (JSONException e) {
e.printStackTrace();
}
}
Help me change my methods in APIHelper class that they return values after callback from DoRequest.
You can use an event bus like otto to have the async task publish an event to the event bus once in onPostExecute and then have the whoever the Helper class is returning its results to, listen to the event on the bus and then handle the callback there.
http://square.github.io/otto/
An example of using this would be:
First you are going to have to use a custom post method to be able to post to threads from the background.
public class MainThreadBus extends Bus {
private final Handler handler = new Handler(Looper.getMainLooper());
#Override public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
super.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
post(event);
}
});
}
}
}
Now that we have that set up, inside of the class that calls the helper class we create a register on the bus and call the helper method:
class someClass(){
///some stuff
public void performRestCall(int id_category){
bus.register(this);
ApiHelper.getItemsInCategory(id_category);
}
#Subscribe
public void restCallCompleted(GetCategoryEvent e){
ArrayList<Item> list = e.getList();
//do whatever else you need to
bus.unRegister(this);
}
}
Now in the asyncTask onPostExecute we perform all the work we were doing after the asyncTask finished and tell everyone on the bus that this event has completed. We pass the list in the object instead of return it from a method.:
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
bus.register(this);
try {
JSONObject jsonObject = new JSONObject(jsonString);
bus.post(new GetCategoryEvent( Item.fromJson(jsonObject));
} catch (JSONException e) {
e.printStackTrace();
}
}
Your solution will end up being something along these lines.