A newbie for here.
I'm working in an app with Android and a strange thing happens to me with a While loop. I make a series of requests to the database with volley library and it returns the data well. No problem.
The problem, i think, is in the last function DameColorPlato(), because sometimes the code accesses the while loop and it passes through it well, but sometimes it does not, and it returns the default value of the CC variable (#000000) and it does not show me well the colors of the text.
This is my code (In summary):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
Bundle datos = getIntent().getExtras();
id_usuario = datos.getString("id_usuario");
idCentro = datos.getString("id_centro");
fecha_actual = datos.getString("fechaActual");
fecha_actual_SQL = datos.getString("fechaActualSQL");
plato1 = (TextView)findViewById(R.id.textView4);
plato2 = (TextView)findViewById(R.id.textView3);
ObtPlatos_volley(idCentro, fecha_actual_SQL);
ObtColores_volley();
public void ObtPlatos_volley(final String id_centro, final String fecha_actual_SQL){
String url = "http://neton.es/WS_neton/menu_dia.php?id_centro="+id_centro+"&fecha_actual_SQL="+fecha_actual_SQL;
StringRequest eventfulRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONArray jsonArray = new JSONArray(response);
for (int i=0; i<jsonArray.length(); i++) {
platouno = jsonArray.getJSONObject(i).getString("plato1");
platodos = jsonArray.getJSONObject(i).getString("plato2");
platounoColor = jsonArray.getJSONObject(i).getInt("tipo1");
platodosColor = jsonArray.getJSONObject(i).getInt("tipo2");
}
plato1.setText(platouno);
String co1 = DameColorPlato(CodTipoPlato, ColorLetra, platounoColor);
plato1.setTextColor(Color.parseColor(co1));
plato2.setText(platodos);
String co2 = DameColorPlato(CodTipoPlato, ColorLetra, platodosColor);
plato2.setTextColor(Color.parseColor(co2));
} catch (Exception e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Error: ", error.toString());
}
});
VolleySingleton.getInstance(this)
.addToRequestQueue(eventfulRequest);
}
public void ObtColores_volley(){
String url = "http://neton.es/WS_neton/color_platos.php";
StringRequest eventfulRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
int cod_color_letra;
String color_letra;
JSONArray jsonArray = new JSONArray(response);
for (int i=0; i<jsonArray.length(); i++){
cod_color_letra = jsonArray.getJSONObject(i).getInt("cod_tipoplato");
color_letra = jsonArray.getJSONObject(i).getString("color");
CodTipoPlato.add(cod_color_letra);
ColorLetra.add(color_letra);
}
} catch (Exception e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Error: ", error.toString());
}
});
VolleySingleton.getInstance(this)
.addToRequestQueue(eventfulRequest);
}
public String DameColorPlato(ArrayList<Integer> CodColorL, ArrayList<String> ColorL, int tipoplato){
String CC="#000000";
int i=0;
boolean encontrado=false;
while (i < CodColorL.size() && !encontrado) {
if (tipoplato == CodColorL.get(i)) {
CC = ColorL.get(i);
encontrado = true;
}else {
i++;
}
}
return CC;
}
}
With a Toast I have found that ArrayList CodColorL and ArrayList ColorL variables sometimes come with values, and sometimes they come empty. But i cannot found the error.
Thanks in advance!
(sorry for my bad English)
As I explained out in the comments, for anyone else looking at this question, the reason why OP was seeing the issue of unreliable data is because they are making two Volley requests and expecting one to finish before implicitly.
By default, Volley requests are run in a queue but are Asynchronous which means that the requests won't necessarily finish in the order that they were started in the queue. Since OP's one request is dependent on the data from the other the correct way to do this is by synchronously running the requests. This can be done in a few ways such as using a callback from the first request or through starting the second request in the onResponse block of the first one.
One more way to achieve the same is to create your own architecture of running requests where you have a way to run all the requests on a single thread but that is over optimizing for this particular case.
Related
I want to execute taking data from JSON as shown below. But when
Toast.makeText(this, MangIDtrailer.size () + "..... check size of Array IDtrailer .....", Toast.LENGTH_LONG).show();
it returns 0.
I don't know what the cause is.
public class Main2Activity extends AppCompatActivity {
ListView Listmovie;
ArrayList<String> MangIDtrailer;
public static ArrayList<InfoMovie> inforMovieArrayList;
AdapterMovie adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
BottomNavigationView navView = findViewById(R.id.nav_view);
navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
String url1 ="http://the....ying";
inforMovieArrayList = new ArrayList<>();
MangIDtrailer = new ArrayList<>();
MangIDtrailer = GetIDMovie(url1);
inforMovieArrayList = DataMovie(MangIDtrailer);
Listmovie = (ListView) findViewById(R.id.ListMovie);
adapter = new AdapterMovie(this, R.layout.movielist, inforMovieArrayList);
Listmovie.setAdapter(adapter);
Listmovie.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Intent intent = new Intent(Main2Activity.this,Review_Movie.class);
intent.putExtra("IDmovie",i);
//Toast.makeText(MainActivity.this, ""+i, Toast.LENGTH_SHORT).show();
startActivity(intent);
}
});
}
public ArrayList<String> GetIDMovie (String Url) {
final ArrayList<String> ArrayID = new ArrayList<>();
final RequestQueue queue = Volley.newRequestQueue(this);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, Url, null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
String IDTrailer;
JSONArray jsonArrayFreeMovies = response.getJSONArray("FreeMovies");
for (int i=0; i < jsonArrayFreeMovies.length(); i++) {
JSONObject jsonObjectFreeMovies = jsonArrayFreeMovies.getJSONObject(i);
IDTrailer = jsonObjectFreeMovies.getString("trailer_id");
ArrayID.add(IDTrailer);
Toast.makeText(Main2Activity.this, i+"************", Toast.LENGTH_SHORT).show();
}
Toast.makeText(Main2Activity.this, MangIDtrailer.get(2)+"check Data ", Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
}
});
queue.add(jsonObjectRequest);
queue.cancelAll(jsonObjectRequest);
return ArrayID;
}
public ArrayList <InfoMovie> DataMovie (ArrayList<String> MangIDtrailer) {
final ArrayList<InfoMovie> inforMovieArray = new ArrayList<>();
final String linkDetail = "http://tk/api/trailers/movDetail?trailer_id=";
final RequestQueue queue2 = Volley.newRequestQueue(this);
//////////////Check that MangIDtrailer.size () has no data////////////////////////////////////
Toast.makeText(this, MangIDtrailer.size()+".....check size of Array IDtrailer .....",Toast.LENGTH_LONG).show();
for (int i=0; i<MangIDtrailer.size(); i++) {
JsonObjectRequest jsonObjectRequest2 = new JsonObjectRequest(Request.Method.GET, linkDetail + MangIDtrailer.get(i) + "&test_fullVer=1", null,
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
String linkposter, linkbackdrop, namemovie, overviewmovie, Release_date, Urltrailer;
Float Vote_average;
String linkHot = "https://image.tmdb.org/t/p/w500/";
JSONObject jsonObjectInfo = null, jsonObjectMore = null;
JSONObject jsonopFreeMovies1 = response.getJSONObject("FreeMovies");
if (jsonopFreeMovies1.has("FreeMovies")) {
//Toast.makeText(MainActivity.this, "Cos ", Toast.LENGTH_SHORT).show();
JSONObject jsonObjectFreeMovies2 = jsonopFreeMovies1.getJSONObject("FreeMovies");
jsonObjectInfo = jsonObjectFreeMovies2.getJSONObject("Info");
jsonObjectMore = jsonObjectFreeMovies2.getJSONObject("More");
} else {
//Toast.makeText(MainActivity.this, "Khoong cos", Toast.LENGTH_SHORT).show();
jsonObjectInfo = jsonopFreeMovies1.getJSONObject("Info");
jsonObjectMore = jsonopFreeMovies1.getJSONObject("More");
}
namemovie = jsonObjectInfo.getString("title");
Urltrailer = jsonObjectInfo.getString("trailer_urls");
linkposter = linkHot + jsonObjectInfo.getString("thumbnail");
overviewmovie = jsonObjectMore.getString("overview");
linkbackdrop = linkHot + jsonObjectMore.getString("backdrop_path");
Release_date = jsonObjectMore.getString("release_date");
Vote_average = Float.valueOf(jsonObjectMore.getLong("vote_average"));
inforMovieArray.add(new InfoMovie(namemovie, overviewmovie, linkposter, linkbackdrop, Vote_average, Release_date));
Toast.makeText(Main2Activity.this,namemovie + "-" + overviewmovie + "-" + Vote_average, Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
Toast.makeText(Main2Activity.this, "Lỗi", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(Main2Activity.this, "Lỗi Try catch", Toast.LENGTH_SHORT).show();
}
});
queue2.add(jsonObjectRequest2);
}
return inforMovieArray;
}
}
As you suggested
Toast.makeText(this, MangIDtrailer.size()+".....check size of Array IDtrailer .....",Toast.LENGTH_LONG).show();
This is where you are getting size zero, which is absolutely true, because you have only initialized your array MangIDtrailer and it is an empty array. Your function GetIDMovie(url1); has a loop which populates your MangIDtrailer array which is below where you have called the toast. So your array is empty and thus its size returns zero.
One handy tip for you, you should name your functions in camelCase with first letter of your word in lowercase. GetIDMovie(url1) seems more like a class constructor. :)
EDIT:
The above solves your initial problem.
To fully solve your problem, you have to understand that Network Operations are asynchronous, meaning they will execute after sometime or they may return no value at all depending on various conditions, like network bandwidth, your server state, the parameters passed to your HTTP requests, etc.
You have two network calls in your above code; in functions: GetIDMovie() and DataMovie(). The second function requires an array of IDs which is only available if your first request is complete and returns an array of ids. So what you would want to do is, only after you get the array of ids ie. in onResponse of GetIDMovie() after the for loop, you should make a call to DataMovie().
This however is really ugly solution. I hope you will research further for better solution.
My JSON file looks like [{"Delay":5}] I need to check if Delay is not 0 then put it in toast like (Delay = 5 ) but when I run the application stop and don't show anything
private void Affiche (){
StringRequest stringRequest = new StringRequest(Request.Method.GET, URL_WAITING,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONArray waiting = new JSONArray(response);
for (int i = 0; i < waiting.length(); i++) {
JSONObject productObject = waiting.getJSONObject(i);
int Delay = productObject.getInt("Delay");
if ( Delay ==0) {
}else {
Toast.makeText(WaitingActivity.this, Delay, Toast.LENGTH_LONG).show();
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(WaitingActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
Volley.newRequestQueue(this).add(stringRequest);
I expect to show "Delay = 5" but it didn't show anything
If by "when I run the application stop" you mean that the app is crashing, then that's probably because you are trying to show an int instead of a String. The makeText() method accepts either an Int or a CharSequence as the second parameter, but the int that it accepts refers to a resource id. So in your case, since your int is not referring to a resource id, you might want to convert your Delay variable to a String in order to show it in a Toast.
Example.
String delayString = "Delay = " + Integer.toString(Delay);
Toast.makeText(WaitingActivity.this, delayString, Toast.LENGTH_LONG).show();
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.
I'm making multiple requests to Amazon Web Services. I'm getting the 503 error because I'm making too many request too quickly. I want to know how to set the time-out between different requests, not the same ones. I am not looking to set the retry policy. I am also not looking to time-trigger individual requests. I want to time the interval between requests. The reason is that I am looping so quickly and making so many requests, that timing-triggering them is equivalent to submitting them all that the same time. The whole point is to space the requests out evenly.
Since you don't show how you made multiple requests, so I suggest you refer to the following sample, then try applying to your code. Hope it helps!
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final RequestQueue queue = Volley.newRequestQueue(this);
final String url = "http://google.com";
final Handler handler = new Handler();
for (int i = 0; i <= 5; i++) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
StringRequest request = new StringRequest(url, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
Log.i("onResponse", response);
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("onErrorResponse", error.toString());
}
});
queue.add(request);
}
}, 2000); // 2000 miliseconds
}
}
assuming you have Request object , before adding the request to the queue you can do this
request.setRetryPolicy(new DefaultRetryPolicy(5000, 5, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
the 5000 indicates the time between each request in ms
the 5 is the number of times you want to send the request before it gives you timeout
for the sake of someone seeing this, this is how to use timers to manually seclude a task
Timer timer = new Timer();
final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
// Update UI here if u need
}
};
TimerTask task = new TimerTask () {
#Override
public void run () {
//send requests according to your logic here
}
};
timer.schedule(task, 0, 60000); // 60000 = 1 min
Was struggling with this too, til I got some help from another developer. Try something like this:
public class HTTP {
String getUrl;
Context context;
YTVisualizer ytv;
int numberOfCurrentRequests = 0;
public HTTP(Context context, YTVisualizer ytv){
this.context = context;
this.ytv = ytv;
}
public void get(final String url) {
numberOfCurrentRequests++;
new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(250 * numberOfCurrentRequests);
} catch(InterruptedException e) {
e.printStackTrace();
}
// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(context);
String[] parts = url.split("=");
final String key = parts[1];
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
#Override
//runs in thread main
public void onResponse(String response) {
Log.i("Response", response);
String title = new String();
try {
JSONObject obj = new JSONObject(response);
Iterator<String> str = obj.keys();
String key = str.next();
title = obj.getString(key);
} catch (JSONException e) {
e.printStackTrace();
}
ytv.SetVideosFromHTTPClass(key, title, response);
numberOfCurrentRequests--;
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(context, "Error: are you connected to the internet?", Toast.LENGTH_SHORT).show();
numberOfCurrentRequests--;
}
});
// Add the request to the RequestQueue.
queue.add(stringRequest);
}
}).start();
}
}
It pauses inbetween proportional to the amount of current requests. The 1st one sleeps .25 s, the 2nd one .5s, the third one .75s, and so on. Theyre all scheduled in order.
I know this is an old question, but here is a solution written in Kotlin:
Inside your class with the RequestQueue, you can add
val throttleTimeout : Long = 100L
private val throttleQueue : ArrayBlockingQueue<Request<*>> = ArrayBlockingQueue(100);
private val throttleThread = Thread {
while(true){
val rqst = throttleQueue.take()
requestQueue?.add(rqst)
Thread.sleep(throttleTimeout)
}
}
fun <T> addToThrottledRequestQueue(request: Request<T>, tag: String){
request.tag = if (TextUtils.isEmpty(tag)) TAG else tag
throttleQueue.put(request)
}
And just make sure to start the thread in your class initialization. You can also mix this with a function to create non-throttled request and mix them together.
fun <T> addToRequestQueue(request: Request<T>, tag: String) {
request.tag = if (TextUtils.isEmpty(tag)) TAG else tag
requestQueue?.add(request)
}
The addToThrottledRequestQueue function will make sure those requests are throttled while other requests can flow freely.
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.