Alright - Before you say this is a duplicate, I've looked over every stack overflow article I can find, and none of them work and/or answer the question properly/simply. All I need is to repeat a function with a volley request inside of it every x-seconds.
Basically, I have a fairly simple Volley request inside a function, which works absolutely perfectly on one call.
Volley function:
private void SetBusPositions() {
TextView textE = (TextView) findViewById(R.id.FirstInfo);
RequestQueue Queue = ServerRequestsQueue.getInstance(context.getApplicationContext()).getRequestQueue();
int SendMethod = Request.Method.GET;
String ServerURL = "my url";
JsonArrayRequest JOR = new JsonArrayRequest(SendMethod, ServerURL, null, new Listener<JSONArray>() {
#Override
public void onResponse(JSONArray resp) {
textE.setText(resp.toString());
System.out.println("Response is: " + resp.toString());
//for each object in JSON Array
for (int i = 0; i < resp.length(); i++) {
}
}
}, new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
//process
}
});
Queue.add(JOR);
}
I just want to call this function periodically, to receive data from the server and update my bus-position data. There has to be a fairly simple way to do this? I know I must be missing something, but none of the other answers seem to help.
Also, as I'm using Google maps, my class is already extending FragmentActivity. I've seen methods that extend Runnable to get this working -- but my Java is a bit rusty here. I've been doing too much JS.
private final class RepeatedOperation extends AsyncTask<Map, Void, String> {
#Override
protected String doInBackground(Map... params) {
SetBusPosition(params[0]);
}
#Override
protected void onPostExecute(String result) {
}
}
private void callAsynchronousTask() {
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
RepeatedOperation performBackgroundTask = new RepeatedOperation();
// RepeatedOperation this class is the class that extends AsyncTask
performBackgroundTask.execute(map);
} catch (Exception e) {
// TODO Auto-generated catch block
}
}
});
}
};
timer.schedule(doAsynchronousTask, 0, 50000); //execute in every 50000 ms
}
Try this out and add this in the same scope as SetBusPosition()
Related
(note: I'm using the Android Volley library for the network connection)
public class PostureActivity extends AppCompatActivity {
private static final String LOG_TAG = PostureActivity.class.getName();
private static final String EMB_URL = "https://api.thingspeak.com/channels/xxxxxxx/feed/last.json?round=1";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
connect(); // call at the start
final Handler handler = new Handler();
Runnable scrape = new Runnable() {
#Override
public void run() {
connect(); // call every x ms
handler.postDelayed(this, 3000);
}
};
handler.postDelayed(scrape, 3000);
}
private void connect() {
MySingleton.getInstance(this.getApplicationContext()).getRequestQueue();
JsonObjectRequest collectData = new JsonObjectRequest(
Request.Method.GET, // HTTP method
EMB_URL, // URL string that returns the desired JSON // TODO: change appropriate url
null, // optional JSON to send as request
response -> { // retrieved data
try {
JSONObject myResponse = new JSONObject(response.toString());
// TODO: cast to double to show the average
String ultrasonic = myResponse.getString("field1");
String flex1 = myResponse.getString("field2");
String flex2 = myResponse.getString("field3");
TextView neck = findViewById(R.id.neck_number);
TextView back = findViewById(R.id.back_number);
TextView butt = findViewById(R.id.butt_number);
neck.setText(ultrasonic);
back.setText(flex1);
butt.setText(flex2);
} catch (JSONException e) { // what if response is null?
e.printStackTrace();
Toast.makeText(getApplicationContext(), "Response values are empty.", Toast.LENGTH_LONG).show();
finishAffinity();
finishAndRemoveTask();
}
},
error -> { // retrieved error/failure
error.printStackTrace();
Toast.makeText(getApplicationContext(), "Could not connect to website.", Toast.LENGTH_LONG).show();
finishAffinity();
finishAndRemoveTask();
}
);
MySingleton.getInstance(this).addToRequestQueue(collectData);
}
As you can see, connect() essentially retrieves, parses, and displays the data, and I run it via a handler. How do split the code so that this entire function doesn't simply populate the UI thread? I'm not very familiar with handler/loopers or java threads outside of async tasks, so I was hoping that I could be pointed in the right direction as to how to optimize the function better.
I am learning ThreadPoolExecutor by following this tutorial. To demonstrate its usage, I made a simple android project, it has a recyclerview that will show some Strings. Initially, the array of Strings(String[] myDataset = new String[10]) has 10 nulls. My threadPoolExecutor generates some random strings and fills up the array. So whenever a new String is generated and placed inside the array, I should call notifyDataSetChanged() so that the recyclerView will update and show those random Strings.
the problem
I don't understand how to call notifyDataSetChanged() and so I am pinned down. I got this exception:
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Since I know AsyncTask, I understand that this error means I cannot call this method in background thread but I have to call it in main thread/ui thread ( so in AsyncTask, it would look like this:
#Override
protected void onPostExecute(String result) {
weakReference.get().notifyDataSetChanged(); // something like that
}
). I need it's ThreadPoolExecutor counterpart. I did google and found this but I am not sure how to do this.
The necessary code segment is given below:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] myDataset;
private final ThreadPoolExecutor threadPoolExecutor;
private Future future;
private Runnable getRunnable(final int i) {
Runnable runnable = new Runnable() {
#Override
public void run() {
String randomString = MyAdapter.getRandomString(i)+" "+i; // <--create random string
Log.e(TAG, randomString);
myDataset[i] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
}
};
return runnable;
}
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error. Where/How should I call it?
};
threadPoolExecutor.execute(commands[j1]);
}
}
public MyAdapter(String[] myDataset) {
this.myDataset = myDataset; // {null, null, ... ...}
this.threadPoolExecutor = DefaultExecutorSupplier.getInstance().forBackgroundTasks(); // returns new ThreadPoolExecutor( ... parameters... );
// future[i] = threadPoolExecutor.submit(command); future[i].cancel(true); use it like this
doSomeBackgroundWork();
}
// ... the rest of the recyclerview related code
}
Could anyone help me? Thank you for reading.
There is a Handler class under the hood in all cases where you need to communicate to UIThread from the another Thread (AsyncTask use it as well).
Some of possible choices:
Use Handler, connected to main looper:
Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
Use "runOnUiThread" that you've mentioned:
runOnUiThread(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
Use the "post" method of your UI-View (RecyclerView, for example):
yourRecyclerView.post(new Runnable() {
#Override
public void run() {
notifyDataSetChanged();
}
});
In case anyone needs, here is what I did based on sergiy-tikhonov's answer.
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error
recyclerViewWeakReference.get().post(new Runnable() { // <---- this is the change
#Override
public void run() {
notifyDataSetChanged();
}
});
};
threadPoolExecutor.execute(commands[j1]);
}
}
So as you can see, I tried the third option. First I created a WeakReference<RecyclerView> recyclerViewWeakReference = new WeakReference<RecyclerView>(myRecyclerView) in the parent fragment (or activity if you are using that). Then I passed the weak reference into MyAdapter. I used weakReference because that is what you do with AsyncTask,so my instincts alerted me to do so. I hope this is helpful.
#SuppressWarnings({ "rawtypes" })
public void addAttendance(ArrayList<Properties> attendanceusers) {
//tl.removeView(tr);
tl.removeAllViews();
//addHeaderAttendance();
ctr=0;
for (Iterator i = attendanceusers.iterator(); i.hasNext();) {
Properties p = (Properties) i.next();
property_list.add(p);
/** Create a TableRow dynamically **/
tr = new TableRow(this);
picurl=p.getPic();
profile = new ImageView(this);
profile.setPadding(20,50,20,50);
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
try {
InputStream in = new URL(picurl).openStream();
bmp = BitmapFactory.decodeStream(in);
} catch (Exception e) {
// log error
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if (bmp != null)
profile.setImageBitmap(bmp);
}
}.execute();
profile.setOnClickListener(this);
//myButton.setPadding(5, 5, 5, 5);
Ll = new LinearLayout(this);
params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
params.setMargins(0, 0, 0, 0);
Ll.setPadding(0, 0, 20, 0);
Ll.addView(profile,params);
tr.addView((View)Ll);
ctr++;
// Add the TableRow to the TableLayout
tl.addView(tr, new TableLayout.LayoutParams(
LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT));
}
}
The code above is the dynamic design. There is an image there wherein i put images on my android screen that i get from my database using web service. The problem is the images are blinking and also it is too big. I think it is blinking because i am creating a thread within a thread but im still confused on how to fix it. My async task there is how i get the image.
public ArrayList<Properties> attendanceUse(String result) {
ArrayList<Properties> attendanceusers = new ArrayList<Properties>();
try {
JSONArray jArray = new JSONArray(result);
for (int i = 0; i < jArray.length(); i++) {
JSONObject json_data = jArray.getJSONObject(i);
Properties user = new Properties();
user.setPic(json_data.getString("student_ImgUrl"));
attendanceusers.add(user);
//attendanceusers.get(index)
}
} catch (JSONException e) {
Log.e("log_tag", "Error parsing data " + e.toString());
}
return attendanceusers;
}
The code above is how i get my image in connection with a query on php inside my htdocs. The student_imgurl is the column i select from my mysql database to get the url.
private void update()
{
final Handler handler = new Handler();
Timer timer = new Timer();
String splithis;
splithis=mySpinner.getSelectedItem().toString();
splited = splithis.split(" ");
course = splited[0];
room = splited[1];
sections = splited[2];
TimerTask task = new TimerTask() {
#Override
public void run() {
data = bw.getAttendanceFromDB(term, course,sections,day,room);
handler.post(new Runnable() {
public void run() {
try {
//asyntask class ito
ArrayList<Properties> attendanceusers = attendanceUse(data);
addAttendance(attendanceusers);
} catch (Exception e) {
// error, do something
}
}//run
});//handler mo
}//runmo
};//timer
timer.schedule(task, 0,1000); //timer
}
This is my update void. It is on a timer so that the images will update in real time.
mySpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> arg0, View arg1,
int arg2, long arg3) {
data ="";
new Thread(new Runnable() {
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
update();
}
});
}
}).start();
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
// TODO Auto-generated method stub
return;
}
});
The code above is my code inside my Oncreate. i have a spinner/dropdown wherein they will choose course-room-section and then whenver they picked an item, the images of the students in that course-room-section will be displayed.
What i want is to stop the blinking of my UI and to resize the imageview.
My thoughts are that this could be better done by replacing your void update() method with an AsyncTask<Void, Properties, Void> class to pull in the attendee data from the database and also download the profile image at the same time in its doInBackground() method. As each attendee is loaded you can update your UI via the publishProgress(Properties) and onProgressUpdate(Properties[]) methods. This should result in a less janky UI update.
Bear in mind that if you have multiple AsyncTasks running at the same time you should execute them on the AsyncTask.THREAD_POOL_EXECUTOR to allow them to run in parallel (the default executor is AsyncTask.SERIAL_EXECUTOR)
As to the image size problems, it may be you don't appear set the correct ImageView.ScaleType on your view to get it to fit properly. I expect it should be ImageView.ScaleType.CENTER_INSIDE.
I'm getting this in my logcat: skipped X frames! The application may be doing too much work on its main thread where X is usually between 40 and 60.
I'm new to android development and I'm not sure how much Volley does with AsyncTasks. In my fragment, I send a request to the server for JSON like such:
{ "networks" : [ { "name" : "a name", "external_id" : "an external_id" }, ... ] }
The ... is just a place holder for elements that are of the same form as the first one I showed in the array.
I do basically all of the work in the onActivityCreated function in my Fragment.
Here is the code and I'm not sure if I'm doing anything wrong. Also, the point of the for loop is to loop through the JSONArray and turn each element into its own model which I then pass to an ArrayList which ultimately gets adapted for the UI via an Adapter.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mSessionPreferences = this.getActivity().getSharedPreferences(
getString(R.string.session_shared_preferences_key), Context.MODE_PRIVATE);
networks = getListView();
networkItems = new ArrayList<Network>();
listAdapter = new NetworksListAdapter(getActivity(), networkItems);
networks.setAdapter(listAdapter);
JSONObject params = new JSONObject();
params.put("user_id", mSessionPreferences.getString(getString(R.string.user_id_key), null))
.put("auth_token", mSessionPreferences.getString(getString(R.string.user_auth_token_key), null));
Response.Listener<JSONObject> listener = new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
JSONArray jsonNetworks = response.optJSONArray("networks");
int len = jsonNetworks.length();
for (int i = 0; i < len; i++) {
JSONObject jsonNetwork = jsonNetworks.optJSONObject(i);
Network network = new Network(jsonNetwork.optString("external_id"),
jsonNetwork.optString("name");
networkItems.add(network);
}
listAdapter.notifyDataSetChanged();
}
};
Response.ErrorListener errorListener = new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyErrorHandler volleyError = new VolleyErrorHandler(error);
Log.v("Sigh", volleyError.getMessage(), "nope"));
}
};
APIRequestManager.getInstance().doRequest().getNetworks(params, listener, errorListener);
}
The last line takes care of having Volley send the request.
Is there anything that looks suspicious? Note, this isn't on an emulator, it is on my actual device. I was under the impression that it handles the Async stuff. Should I be doing things with my listeners in the background explicitly? If so, how would I do that?
if the length of jsonNetworks is large, you should run this method on background.
try this:
#Override
public void onResponse(JSONObject response) {
new Thread(new Runnable() {
#Override
public void run() {
JSONArray jsonNetworks = response.optJSONArray("networks");
int len = jsonNetworks.length();
for (int i = 0; i < len; i++) {
JSONObject jsonNetwork = jsonNetworks.optJSONObject(i);
Network network = new Network(jsonNetwork.optString("external_id"),
jsonNetwork.optString("name");
networkItems.add(network);
}
runOnUiThread(new Runnable() {
#Override
public void run() {
listAdapter.notifyDataSetChanged();
}
});
}
}).start();
}
I'm trying to initiate List<String> inside of AsyncTask and the whole task is no longer called, no errors though.
Just to be clear, the only reason I am after the List is that it gives me an opportunity to cut big chunk of elements at once using sublist.
If there's an easy way or you spotted a bug, please share.
protected void updateTask()
{
runOnUiThread(new Runnable()
{
public void run()
{
PatternTask myTask = new PatternTask();
myTask.execute();
}
});
}
private class PatternTask extends AsyncTask<String, Void, String> {
int eyes_closed;
int eyes_opened;
int eyes_avarage;
String response = "NO DATA";
protected String doInBackground(String... patterns) {
String[] looper = getPattern().split("\n");
List<String> patternList = new ArrayList<String>();
for (int i = 0; i < looper.length; i++)
{
patternList.add(looper[i]);
}
if (patternList.size() > 1200)
{
patternList.subList(0, patternList.size() - 1200).clear();
...
...
}
else
return response;
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(FdActivity.this, result, Toast.LENGTH_LONG).show();
}
}