i am new to Android,here is class Question bank returns list of json obj received from an api
ArrayAsyncResponse in interface containing only one method process complete ,i readed that http request is asynchronous but unable to relate
Question is model class
case 1) when there is no ArrayAsyncResponse interface exist and i return the list to main activity and print it it shows empty list but when i make call to callback.processComplete() and then return list followed by printing ,it shows data
case2 )if i pass null in this function callback.processComplete() then again returned list is empty
so what basically Interface is helping us
public class QuestionBank {
ArrayList questionArrayList = new ArrayList<>();
private String url = "https://raw.githubusercontent.com/curiousily/simple-quiz/master/script/statements-data.json";
public List<Question> getQuestions(final AnswerListAsyncResponse callBack) {
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(
Request.Method.GET,
url,
(JSONArray) null,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
for (int i = 0; i < response.length(); i++) {
try {
Question question = new Question();
question.setAnswer(response.getJSONArray(i).get(0).toString());
question.setAnswerTrue(response.getJSONArray(i).getBoolean(1));
//Add question objects to list
questionArrayList.add(question);
//Log.d("Hello", "onResponse: " + question.getAnswer());
// Log.d("JSON", "onResponse: " + response.getJSONArray(i).get(0));
//Log.d("JSON2", "onResponse: " + response.getJSONArray(i).getBoolean(1));
} catch (JSONException e) {
e.printStackTrace();
}
}
if (null != callBack) callBack.processFinished(questionArrayList);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
AppController.getInstance().addToRequestQueue(jsonArrayRequest);
return questionArrayList;
}
public class MainActivity extends AppCompatActivity {
private QuestionBank questionBank;
List<Question> questionList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
questionBank=new QuestionBank();
questionList=questionBank.getQuestions(new AnswerListAsyncResponse() {
#Override
public void processFinished(ArrayList<Question> questionsArrayList) {//this function triggers when response is received from api
Log.d("inside", "processFinished: "+questionsArrayList);
}
});
Log.d("sync response", "questionLIst: "+questionList);
}
}
Sorry I can't write this in comment, but I think I can help you.
I don't understand well your question. but I think you need to get a Listener of the async in your class activity.
You can do that with EventBus, like this : https://github.com/greenrobot/EventBus
Related
This is a simple program to gain the JSON data from the internet. answerWithAsyncTask() is an interface that ensures that all the downloaded data will only be added to questionArrayList when the download is complete.
Error: java.lang.IndexOutOfBoundsException: Index: 1, Size: 0
private List<Question> questionList;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Few findViewbyId's here. Ignoring them
questionList = new QuestionBank().getQuestions(new answerWithAsyncTask() {
#Override
public void asyncMe(ArrayList<Question> questionArrayList) {
questionTextview.setText(questionArrayList.get(currentQuestionIndex).getQuestionId());
}
});
updateQuestion(); //This is the newly added line
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.prev_button:
updateQuestion();
break;
}
}
private void updateQuestion() {
String question = questionList.get(1).getQuestionId();
questionTextview.setText(question);
}
UPDATE This is my getQuestions method.
String url ="https://raw.githubusercontent.com/curiousily/simple-quiz/master/script/statements-data.json";
private ArrayList<Question> questionArrayList= new ArrayList<>();
public List<Question> getQuestions (final answerWithAsyncTask callback){
JsonArrayRequest jsonArrayRequest =new JsonArrayRequest(Request.Method.GET, url, (String) null,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
for(int i=0;i<response.length();i++){
Question question = new Question();
try {
question.setQuestionId(response.getJSONArray(i).getString(0));
question.setTorF(response.getJSONArray(i).getBoolean(1));
questionArrayList.add(question);
} catch (JSONException e) {
e.printStackTrace();
}
}
if(null != callback) callback.asyncMe(questionArrayList);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
AppController.getInstance().addToRequestQueue(jsonArrayRequest);
return questionArrayList;
}
and this my interface answerWithAsyncTask
public interface answerWithAsyncTask {
void asyncMe(ArrayList<Question> arrayList);
}
You are getting this error because
private List<Question> questionList;
is empty. In your first code, you are not getting any error because you are not calling updateQuestion() which will try to fetch data from an empty list. The error is in 2nd code because it's trying to access that empty list. Your
return questionArrayList;
is not returning data to questionList. Here, you are trying to do interface callback, to implement it properly, please look at this answer and change your code accordingly,
Java Interface Callback
I created a parent activity and Fragments are added to parent activity.
But i need to call a function (HTTP request) in parent activity and return the result in fragment activity. But i am able to access the function from fragment using
String stage = ((TabActivity)getActivity()).fetchStage(tabid,"12");
But the value is not receiving in fragments stage variable..
my TabActivity Function:
public String fetchStage(String tabid,String userId){
Log.e("URL","http://35.184.41.163/phpmyadmin/app/demo/getstage.php?tabid="+tabid+"&userid="+userId);
RestClientHelper.getInstance().get("http://35.184.41.163/phpmyadmin/app/demo/getstage.php?tabid="+tabid+"&userid="+userId, new RestClientHelper.RestClientListener() {
#Override
public void onSuccess(String response) {
try {
Log.e("onSUcess Stage","onSUcess Stage");
JSONObject stageresponse = new JSONObject(response);
JSONArray posts = stageresponse.optJSONArray("stage");
for (int i = 0; i < posts.length(); i++) {
JSONObject post = posts.optJSONObject(i);
stage = post.optString("stage");
Log.e("onSUcess FOR","onSUcess FOR"+stage);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onError(String error) {
Log.e("onError Stage","onError Stage");
stage = "stage1";
}
});
Log.e("STAGE",stage);
return stage;
}
Your rest client is doing the network call asynchronously. So, your onSuccess isCalling after sometime. You should declare a public function in your fragment and call that from the onSuccess method.
You could supply an instance of RestClientListener to fetchStage.
The method signature would become:
fetchStage(String tabId, String userId, RestClientHelper.RestClientListener listener);
and when you call it from the Fragment you'd write:
((TabActivity)getActivity()).fetchStage(
tabid,
"12",
new RestClientHelper.RestClientListener () {
// this anonymous class has access to your instance members
#Override
public void onSuccess(String response) {
// ...
MyFragment.this.stage = ...
}
#Override
public void onError(String error) {
MyFragment.this.stage = "stage 1";
}
});
This is widely used in Android; e.g setting a LocationListener to a LocationManager.
I am using Retrofit in my application and receiving data from my server.
I receive data from my server in responseBody in Retrofit, but when I want to use these received data, my array is null?!!
Here is my class:
public class ActivityApplicationsList extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_applications_list);
Log.i(TAG , "size of response array: " + populateDataFromServer().size())
//this size is 0 but have to be 4 !
}
private ArrayList<StructApplication> populateDataFromServer() {
final ArrayList<StructApplication> mine = new ArrayList<>();
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
Call<ArrayList<AppModel>> call = service.getApp();
call.enqueue(new Callback<ArrayList<AppModel>>() {
#Override
public void onResponse(Call<ArrayList<AppModel>> call, Response<ArrayList<AppModel>> response) {
ArrayList<AppModel> newAppModel = response.body();
for(int i=0 ; i < newAppModel.size();i++){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(newAppModel.get(i).getAppId()));
structApplication.setId(newAppModel.get(i).getAppId());
structApplication.setAppVersionReleaseDate(newAppModel.get(i).getAppVersionReDate());
structApplication.setAppDeleted(newAppModel.get(i).getAppDeleted());
mine.add(structApplication);
}
}
#Override
public void onFailure(Call<ArrayList<AppModel>> call, Throwable t) {
}
});
return mine;
}
}
And I debugged to make sure that all data received in onResponse:
As you can see, I received all data correctly, but when I use this response in onCreate of this class it's null!
I really appreciate your answer about this weird problem.
This is because you are printing your list size before the response comes. As request is being sent asynchronously, and your are trying to get size before onResponse() callback method.
try to add this line
Log.i(TAG , "size of response array: " + populateDataFromServer().size())
in onResponse() method after mine.add(structApplication); you will see the right size.
Its return first and execute later, try this way...
private ArrayList<StructApplication> populateDataFromServer() {
final ArrayList<StructApplication> mine = new ArrayList<>();
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
Call<ArrayList<AppModel>> call = service.getApp();
Response<ArrayList<AppModel>> response = responseCall.execute();
ArrayList<AppModel> newAppModel = response.body();
for(int i=0 ; i < newAppModel.size();i++){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(newAppModel.get(i).getAppId()));
structApplication.setId(newAppModel.get(i).getAppId());
structApplication.setAppVersionReleaseDate(newAppModel.get(i).getAppVersionReDate());
structApplication.setAppDeleted(newAppModel.get(i).getAppDeleted());
mine.add(structApplication);
}
return mine;
}
It is happening due to api calling because api taking few seconds to get response and meanwhile you returned your mine array. So please return response once you got value from server.
Do like this
Make a global instance for this class like
public class ActivityApplicationsList extends Activity {
ArrayList<StructApplication> mine = new ArrayList();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_applications_list);
populateDataFromServer();
Log.i(TAG , "size of response array: " + mine.size());
}
private void populateDataFromServer() {
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
Call<ArrayList<AppModel>> call = service.getApp();
call.enqueue(new Callback<ArrayList<AppModel>>() {
#Override
public void onResponse(Call<ArrayList<AppModel>> call, Response<ArrayList<AppModel>> response) {
ArrayList<AppModel> newAppModel = response.body();
for(int i=0 ; i < newAppModel.size();i++){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(newAppModel.get(i).getAppId()));
structApplication.setId(newAppModel.get(i).getAppId());
structApplication.setAppVersionReleaseDate(newAppModel.get(i).getAppVersionReDate());
structApplication.setAppDeleted(newAppModel.get(i).getAppDeleted());
mine.add(structApplication);
}
}
#Override
public void onFailure(Call<ArrayList<AppModel>> call, Throwable t) {
}
});
}
}
You need to get the response after the request. See code comments
public class ActivityApplicationsList extends Activity
implements Callback<ArrayList<AppModel>> { // Implement callback here
// These are final, so make them fields
final ArrayList<StructApplication> mine = new ArrayList<>();
final APIService service = ServiceGenerator.createService(APIService.class, "2015-03-01 14:26:00", "123456", "123456");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_applications_list);
populateDataFromServer();
}
// Callback implementation
#Override
public void onResponse(Call<ArrayList<AppModel>> call, Response<ArrayList<AppModel>> response) {
final ArrayList<AppModel> responseBody = response.body();
for(AppModel model : responseBody){
StructApplication structApplication = new StructApplication();
structApplication.setName(String.valueOf(model.getAppId()));
structApplication.setId(model.getAppId());
structApplication.setAppVersionReleaseDate(model.getAppVersionReDate());
structApplication.setAppDeleted(model.getAppDeleted());
mine.add(structApplication);
}
// adapter.notifyDataSetChanged(); // Need this if using ListView
Log.d("SIZE", ""+mine.size()); // Correct size
}
#Override
public void onFailure(Call<ArrayList<AppModel>> call, Throwable t) {
// error
}
private void populateDataFromServer() { // this is void; it can't return
service.getApp().enqueue(ActivityApplicationsList.this);
}
Suggestion, make this constructor
public class StructApplication {
public StructApplication(AppModel model) {
setName(String.valueOf(model.getAppId());
setId(model.getAppId());
setAppDeleted(model.getAppDeleted());
setAppVersionReleaseDate(model.getAppVersionReDate());
}
}
Then, that loop can simply be
mine.add(new StructApplication(model));
if (isConnected()) {
Event eInstance = new Event();
theEvents = eInstance.downloadEvents(eventsNightlife, getActivity());
rAdapter = new RecyclerAdapter(theEvents);
recyclerView.setAdapter(rAdapter);
progrsBar.setVisibility(View.GONE);
....
This is part of the code that runs at "onCreateView". The method downloadEvents uses Volley to download JSON data, extract it and return a list of items (theEvents). Now when my app starts, the recycler view is empty. If I go to my home screen out of the app and then run my app again, this time the data sometimes gets downloaded.
I debugged step by step, and at first launch (i mean when the app is not just resuming), theEvents is empty, so the download didn't return or manage to return anything...
Suggestions on how to execute things before the UI has been shown to the user or what actually needs to be done to approach this task better?
Also, I use a swipeRefreshLayout and at its onRefresh method I do:
public void onRefresh() {
Event eInstance = new Event();
theEvents = eInstance.downloadEvents(eventsNightlife, getActivity());
rAdapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
but it doesn't work. I also tried to
rAdapter = new RecyclerAdapter(theEvents);
rAdapter.notifyDataSetChanged();
recyclerView.swapAdapter(rAdapter, false);
still not working.
EDIT: My downloadEvents method implementing Volley:
public List<Event> downloadEvents(String urlService, Context context) {
eventsList = new ArrayList<>();
RequestQueue requestQueue = Volley.newRequestQueue(context);
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest
(Request.Method.GET, urlService, null, new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray response) {
try {
String durationStr = null;
for (int i = 0; i < response.length(); i++) {
JSONObject eventJson = response.getJSONObject(i);
String title = eventJson.getString("EventTitle");
String body = eventJson.getString("EventBody");
String date = eventJson.getString("EventDate");
String time = eventJson.getString("EventTime");
int duration = Integer.parseInt(eventJson.getString("EventDuration"));
if (duration > 60) {
durationStr = "Duration: " + duration / 60 + " h";
} else if (duration < 60) {
durationStr = "Duration: " + duration + " m";
}
String place = eventJson.getString("EventPlace");
String organ = eventJson.getString("Organization");
Event event = new Event(title, body, date, time, durationStr, place, organ);
eventsList.add(event);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("VOLLEY ERROR", "" + error);
}
}
);
requestQueue.add(jsonArrayRequest);
return eventsList;
}
You can use EventBus for your purpose that is a simple and truth way.
Here, i write an example for how to use EventBus with volley.
Consider that i want to download some data.
This is the class that my download methods is inside it (you can add more methods to it in the future):
Im used volley to download my data:
// Download methods is inside volley
public class MyDownloader{
public static void downloadData(){
DownloadDataEvent dlDataEvent=new DownloadDataEvent();
List<String> myResult=new ArrayList<>();
...
#Override
public void onResponse(JSONArray response) {
super.onResponse(response);
if(respone!=null){
// Do what i want with my received data
dlDataEvent.setData(response);
}
// Post my event by EventBus
EventBus.getDefault().post(dlDataEvent);
...
}
}
}
This is my event:
public class DownloadDataEvent{
private JSONArray mData;
public void setData(JSONArray data){
mData=data;
}
public JSONArray setData(){
return mData;
}
}
Now i want to use my downloadData() method inside my MainActivity:
(I called my downloadData method inside onCreate.)
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
...
// I have to register this class for EventBus subscriber:
if(!EventBus.getDefault().isRegister(this)){
EventBus.getDefault().registerSticky(this);
}
// Call my downloadData method
if(isConnected()){
MyDownloader.downloadData();
}
}
// And for receive the data through EventBus, i have to create a
// method (subscriber) in this template:
public void onEventMainThread(DownloadDataEvent downloadDataEvent){
JSONArray result=downloadDataEvent.getData();
// Do what i want with my received data
}
}
you can create more than one subscriber every where you want to use received data.
I passed JSONArray to my DownloadDataEvent that it is not good. you can deserialize your received data and pass it to your DownloadDataEvent.
I used Volley to download data
Maybe my descriptions were confusing, but EventBus is a well-known library and is very easy to use.
Let's say I have this Dashboard.java:
public class DashboardActivity extends ActionBarActivity {
private TextView login_response;
private static String TAG = DashboardActivity.class.getSimpleName();
final static String API_URL_ACCOUNT = "http://www.example.com/apiv2/account";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
login_response = (TextView) findViewById(R.id.login_response);
Intent intent = getIntent();
if(intent.hasExtra("TOKEN"))
{
String token = intent.getStringExtra("TOKEN");
getShopName(token);
}
else
{
}
And this is the getShopName method:
private void getShopName(String token) {
JsonObjectRequest req = new JsonObjectRequest(API_URL_ACCOUNT + "?token=" + token, null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
VolleyLog.v("Response:%n %s", response.toString(4));
JSONArray account = response.getJSONArray("account");
//Log.d(TAG, "Account: "+account.toString());
JSONObject shop = account.getJSONObject(0);
String name_shop = shop.getString("name_shop");
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.e("Error: ", error.getMessage());
}
});
// add the request object to the queue to be executed
VolleyController.getInstance().addToRequestQueue(req);
}
My goal is to have
if(intent.hasExtra("TOKEN"))
{
String token = intent.getStringExtra("TOKEN");
String shop_name = getShopName(token);
}
The "shop_name" in variable, to reuse in other part.
So, I know that void doesn't return nothing, but, I tried to edit like this answer, without success:
How can I return value from function onResponse of Volley?
Thank you
The issue is not returning a value from a JsonObjectRequest, but rather that you're trying to do an asynchronous operation in a synchronous way.
Here is a great explanation: Asynchronous vs synchronous execution, what does it really mean?
And to your specific question: I advise using an AsyncTask for your network operation.