Currently, in my main app, I am sending multiple texts to status bar object.
My status bar object, is going to display multiple texts sequentially, with sleep time of N seconds for each display interval.
Here's my implementation in my main app.
public synchronized void setNextText(final CharSequence text) {
if (executor == null) {
executor = Executors.newSingleThreadExecutor();
}
executor.execute(new Runnable() {
#Override
public void run() {
Fragment fragment = getTargetFragment();
if (fragment instanceof OnStatusBarUpdaterListener) {
((OnStatusBarUpdaterListener)fragment).setNextText(text);
try {
// Allow 1 seconds for every text.
Thread.sleep(Constants.STATUS_BAR_UPDATER_TIME);
} catch (InterruptedException ex) {
Log.e(TAG, "", ex);
}
}
}
});
}
Now, I would like to have the same behavior in app widget. I was wondering, is using Executor being recommended in app widget environment? If not, what class I should use to achieve the similar objective?
I do have experience in using HandlerThread + AlarmManager in app widget. It works good so far. However, the operation done by the runnable is one time. It doesn't sleep and wait.
The following is the code which I use to update stock price in fixed interval.
// This code is trigger by AlarmManager periodically.
if (holder.updateStockPriceHandlerThread == null) {
holder.updateStockPriceHandlerThread = new HandlerThread("updateStockPriceHandlerThread" + appWidgetId);
holder.updateStockPriceHandlerThread.start();
holder.updateStockPriceWorkerQueue = new Handler(holder.updateStockPriceHandlerThread.getLooper());
holder.updateStockPriceWorkerQueue.post(getUpdateStockPriceRunnable(...
}
However, I have a feeling that, for use case "display multiple texts sequentially, with sleep time of N seconds for each display interval", AlarmManager might not be a good solution. Imagine I have 100 texts. Having to set 100 alarms for 100 texts doesn't sound good...
An AppWidgetProvider is a subclass of BroadcastReceiver. Once your callback (e.g., onUpdate()) returns, your process can be terminated at any point.
If that is not a problem — if you fail to finish the semi-animation that you are doing, that's OK — using an Executor from onUpdate() could work.
If you want to make sure that the text changes go to completion, delegate the app widget updating to a Service, where you use your Executor. Call stopSelf() on the Service when you are done, so it can go away and not artificially keep your process around.
Well the singleThread instance work creates an Executor that uses a single worker thread. meaning only thread to process your operation. But in you case use at least two. Your operations sounds expensive.
To conclude your question stick with the executor service as it thread safe.
Related
I know Android UI is not really meant for executing functions and waiting for them to finish, however, I think there are use cases were it is required, like networking.
My problem is, I want to run a series of network operations that rely on each other and take a bit more time than the split second it takes to the next execution, so some waiting is in order:
Start hotspot
Get network interfaces and IP
Start socket
Initially I tested that all is working using buttons, then it waited between my button presses. But now I'd like to automatize it. I googled but all I found are solutions with Async task, which is deprecated. I tried with threads and join, but that usually causes weird crashes in the runnable, and it is not very elegant. I wonder if there is another solution?
The best thing you can do with SDK it's use Executors to run your work in background sequentially
val newSingleThreadExecutor = Executors.newSingleThreadExecutor()
newSingleThreadExecutor.execute {
// 1...
}
newSingleThreadExecutor.execute {
// 2...
}
But if you want to touch the UI from background should create handler check if view's not null
val handler = Handler(Looper.myLooper()!!)
newSingleThreadExecutor.execute {
handler.post {
view?.visibility = View.GONE
}
}
How about something like this?
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startHotspot();
getNetworkInterfaceAndIP();
startSocket();
}
}, 300);
I am updating a database (SQLite) with a lot of data, which takes several minutes. I am doing this on a Thread. At the same time I want to show the progression of the updates (25%, 50%, etc.) through a Toast. Since I am within a thread, I need to use the runOnUiThread() function to run the Toast. Like this:
getActivity().runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(getActivity(),"Updating the database...", Toast.LENGTH_SHORT).show();
}
});
It was working fine when the workload was not to big. Now that it is, no toast is being displayed at all.
I have been looking for ways to set a very high priority to the toast's thread, without success. Maybe I can bypass using a thread for the toast in the first place?
Thank you for the insights!
EDIT: In fact, the toast is working, but is displayed after all the work has been done. I want to notify the percentage of the complete update to the user, so I need to have the toast displayed during the update and not after.
I tried removing the workload (no database update, only a Log.d inside the for loop of things to add to the database). And the toast are displayed after the loop is finished although the runOnUiThread() method is called inside the loop.
EDIT 2: I managed to do what I wanted after cleaning up the code and starting fresh. I posted the code I used as the answer below.
After having cleaned up the initial thread, I managed to have something working. I post the code here since it can be used as a template to do a specific task:
Run a initial thread that does tasks periodically for a given number of times (here collect some data). After this given number of times, the collected data is pushed to a database and the user is notified of the advancement of the process (which takes a long time).
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask(){
long t0 = System.currentTimeMillis();
#Override
public void run() {
if(System.currentTimeMillis() - t0 > EXPERIMENT_DURATION ){
processData(dataArrayList); // custom tasks - see below
cancel();
}else {
dataArrayList = collectData(); // custom tasks
}
}
}, 0, INTERVAL);
With:
void processData(Arraylist<Data> dataArrayList){
for(Data data : dataArrayList){
// show progression to the user
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
int percent = Math.round(((float)dataArrayList.indexOf(data))/((float)dataArrayList.size())*100)
Toast.makeText(getActivity(),"Update: " + Integer.toString(percent) + "%", Toast.LENGTH_LONG).show();
}
});
updateDataBase(data);
}
}
I highly recommend you use an AsyncTask.
AsyncTask performs in another working Thread, but it offers a method that is executed in the UI Thread, publishProgress(String ...). that you then customize by overwriting onProgressUpdate(String ...).
EDIT: As read in the comments, AsyncTask is not that good of an option because:
All AsyncTask share a Thread (they don't run on workers as I expected)
For long running Tasks, all other AsyncTasks (system's and your's) will be on hold
This includes AsyncTasks from libraries
I'm making image processor app. I need to scan the phone for pictures and list them with their number of pixels. So that's gonna be a a large impact on performance and as I understood, I need to make it work on background thread.
So my question is, what is the best approach for this? I understand that IntentService may be the best solution, but I'm not sure how I will implement progress bar with it, and I need to return Picture objects and later update the UI on shuffle button. I'm doing update with Glide library so that's gonna go smooth.
Reading about Asynctasks, I stumbled about comments how it's bad and leads to leaks in memory and should avoid using it. rXJava is too complicated at the moment.
This is my code:
Main activity:
#OnClick(R.id.shuffle)
public void shuffleList() {
Collections.shuffle(listOfImageFiles);
recyclerViewAdapter = new PictureRecycleViewAdapter(listOfImageFiles, this);
recyclerView.swapAdapter(recyclerViewAdapter, false);
recyclerViewAdapter.notifyDataSetChanged();
}
#OnClick(R.id.scan)
public void processImages() {
//progress bar
listOfPictures = new ArrayList<>();
//Gets data from default camera roll directory. Note that some of the phone companies have different file paths. So instead of hardcoding string paths, I used this instead.
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath();
File filePath = new File(path);
listOfImageFiles = scanPhotos(filePath);
// async?
for (File file : listOfImageFiles
) {
Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
//int is sufficient for most today's pixels. long would be overkill - 4 vs 8 bytes
int pixels = bitmap.getHeight() * bitmap.getWidth();
listOfPictures.add(new Picture(file.getPath(), pixels));
}
}
public List<File> scanPhotos(File directory) {
List<File> listOfPictures = new ArrayList<>();
try {
File[] files = directory.listFiles();
for (File file : files
) {
if (file.isDirectory() && !file.isHidden()) {
listOfPictures.addAll(scanPhotos(file));
} else {
if (file.getName().endsWith(".jpg") || file.getName().endsWith(".jpeg") || file.getName().endsWith(".png")) {
listOfPictures.add(file);
}
}
}
} catch (Exception e) {
Log.e(e.getMessage(), e.getMessage());
}
return listOfPictures;
}
IntentService
IntentService is definitely a valid approach. You can use Broadcasts to return your result to another component of the app, be it Activity or another Service, for example:
Start the IntentService - if you need some parameters, place them in the Extras of the service intent.
Your IntentService runs on the background thread until the computation is finished.
Upon finishing, send a broadcast with computation result placed in intent extras.
In your activity, register a BroadcastReceiver that will listen for your computation result broadcast.
Upon getting the broadcast in your Activity, retrieve the computation result from intent extras.
You might also implement broadcasts received by your Service for things like cancellation of the computation or updating the parameters.
One of the advantages of IntentService is that you can easily integrate it with the JobScheduler API to defer execution until certain system conditions are met.
Alternatives
You can use a bus library, such as https://github.com/greenrobot/EventBus to communicate between Activity and Service - the only problem is, EventBus won't work with remote services (running in a separate process).
Like you've mentioned, using RxJava with IO and computation schedulers is also a good idea.
AsyncTask is fine as long as you not tie it with a hard reference to an activity - don't implement it as an inner class of Activity and if you want to communicate the result back, do it through a WeakReference<T>
AsyncTask is fine, you just need to be careful with its implementation.
However, for longer running tasks there are better options. IntentService is a good option.
When it comes to a responsive UI when using an IntentService you could add two things.
Notifications
Create an ongoing notification that indicates that your App is working on something. This lets users know that their CPU cycles are being eaten by something in the background and they are less likely(?) to be confused and cranky about their device running slower.
Additionally, it gives your App more of an allowance for staying alive when Android is looking for background Apps to kill to release memory.
EventBus
You can make UI reporting extremely simple by using an EventBus library. I am personally a fan of greenbot/EventBus, but there are others.
Example
In your Activity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onProgressEvent(ProgressEvent event) {
mProgressBar.setProgress(event.value);
}
In your IntentService:
EventBus.getDefault().post(new ProgressEvent(5000));
I am using an AsynchTask to host a simulator that runs indefinelly and posts the results after each simulation step.
Limiting the simulation loop in background at a maximum of 25Hz, and only calling a javascript function with the results, it works "fine".
Apart from updating a webgl model in a browser, what looks fast enough, I have two more things to update from the Android UI: the FPS indicator and the panel with TextViews representing some of the values. If we forget about the FPS:
The onProgressUpdate() function is already limited to be called at 25Hz, to refresh the model. Now I use another time variable to limit, inside this method, the call to another method that updates the UI panel textViews. It is limited to 1Hz, less than what I actually wanted but fast enough for the kind of information. The method is as clean as possible, all the views are previously loaded to a variable that I keep to not load them every time.
What is the effect: looks like updating 5 textViews takes like one second where all the UI freezes, the touch moves are very very laggy...
I decreased the priority of the background task with:
#Override
protected Boolean doInBackground(ModelSimulation... params) {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
...
And used Thread.yield() at the end of the doInBackground method. This improves the behavior to what I explained, without these commands, the behavior is even worst.
My questions are:
-Can I reduce even more the priority if instead of using a background task I use a handler and my own Thread?
-Will a service improve the behavior of the UI?
-Why updating 5 textViews takes so long compared with calling a javascript function that finally will have to use the gpu to change the webgl model?
-Is Android not prepared in any sens to do dynamic applications? How applications like the ones to test sensors update so fast the UI? because there are not standar components like the textViews? (like browser going faster than a textView)
Note: even reducing the refreshing limitations, it produce a laggy effect every time the HUD is updated. In fact I talk about 5 textViews but only updating the FPS indicator produces the same pause. Looks like the only fact of having to switch to the UI thread already consumes this time.
Edit 1:
#Override
protected Boolean doInBackground(ModelSimulation... params) {
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
if(simulator.getSimulatorStatus().equals(SimulatorStatus.Connected)){
try {
while (true){
//TODO Propagate
long dur = (System.nanoTime()-time_tmp_data);
if(dur<Parameters.Simulator.min_hud_model_refreshing_interval_ns){
try {
long sleep_dur = (Parameters.Simulator.min_hud_model_refreshing_interval_ns-(System.nanoTime()-time_tmp_data))/1000000;
if(sleep_dur>0){
Thread.sleep(sleep_dur);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
time_tmp_data = System.nanoTime();
SpacecraftState sstate = propagate();
int progress = (int)((extrapDate.durationFrom(finalDate)/mission.sim_duration)*100);
if(sstate!=null){
SimResults results = new SimResults(sstate, progress);
simulator.getSimulationResults().updateSimulation(results.spacecraftState, results.sim_progress);
publishProgress();
}
if(isCancelled())
break;
Thread.yield();
}
} catch (OrekitException e) {
// TODO Auto-generated catch block
e.printStackTrace();
simulator.showMessage(simulator.getContext().getString(R.string.sim_orekit_prop_error)+": "+e.getMessage());
}
}
return true;
}
#Override
protected void onProgressUpdate(Void... values) {
//Update model by push
simulator.getSimulationResults().pushSimulationModel();
//Update GUI HUD
if(time_tmp_gui==0 || (System.nanoTime()-time_tmp_gui)>Parameters.Simulator.min_hud_panel_refreshing_interval_ns){
time_tmp_gui = System.nanoTime();
simulator.getSimulationResults().updateHUD();
}
}
If I comment the line simulator.getSimulationResults().updateHUD(); or directly the contents of the method, it works "fine". And this method is only changing some textviews text:
public synchronized void updateHUD(){
//Log.d("Sim",System.currentTimeMillis()+": "+"pre update gui 1");
activity.runOnUiThread( new Runnable() {
#SuppressLint("ResourceAsColor")
public void run() {
if(view != null){
if(panel_time != null)
panel_time.setText(info.time.replace("T", " "));
if(panel_progress != null)
panel_progress.setProgress(info.progress);
if(panel_vel != null){
panel_vel.setText("Vel. "+String.format("%.2f", info.velocity)+" Km/s");
if(info.velocity>config.limit_velocity)
panel_vel.setTextColor(activity.getResources().getColor(R.color.panel_limit));
else
panel_vel.setTextColor(activity.getResources().getColor(R.color.panel_value));
}
if(panel_accel != null){
panel_accel.setText("Accel. "+String.format("%.2f", info.acceleration)+" Km/s2");
if(info.acceleration>config.limit_acceleration)
panel_accel.setTextColor(activity.getResources().getColor(R.color.panel_limit));
else
panel_accel.setTextColor(activity.getResources().getColor(R.color.panel_value));
}
if(panel_radium != null)
panel_radium.setText("Orbit radium: "+String.format("%.1f", info.orbit_radium)+" Km");
if(panel_mass != null)
panel_mass.setText("Mass: "+String.format("%.1f", info.mass)+" Kg");
if(panel_roll != null)
panel_roll.setText("Rol: "+String.format("%.1f", (180*info.roll/Math.PI))+"º");
if(panel_pitch != null)
panel_pitch.setText("Pitch: "+String.format("%.1f", (180*info.pitch/Math.PI))+"º");
if(panel_yaw != null)
panel_yaw.setText("Yaw: "+String.format("%.1f", (180*info.yaw/Math.PI))+"º");
}
}
});
//Log.d("Sim",System.currentTimeMillis()+": "+"post update gui 1");
}
Edit 2: I can actually remove the runOnUiThread since it is already at that thread, but the effect is the same, this is not the problem.
Edit 3: I tried to comment all the lines of the method updateHUD() and leave only these two:
if(panel_time != null)
panel_time.setText(info.time.replace("T", " "));
The effect is almost the same, if I touch any textView, the animation goes by steps like periodically freezing
Edit 4:
I noticed that the process inside the AsyncTask was taking longer than the available step time so it was never sleeping. I established a safe guard time of 10ms that is slept even if the simulation step is longer than the available time. So, I have minimum 10ms free of each 100ms. The efect stills the same. I am updating at 25Hz the browser and 1Hz a single textview text. If I disable the textview update, the webgl model animates smoothly. On the other hand, if I enable the textview update too, every time the text is updated, there are some miliseconds where the browser animation and its response to touches are blocked. This effect gets worst if I increase the task priority. I tried setting a huge guard of 500ms but the freezing effect stills appearing. I am using XWalkView, can it be something blocking the interaction of this view when UI Thread is acting?
I can't understand why a 4 core 2 RAMgb device needs way more time to compute the same simulation than in Linux or windows desktop PC. I have 25Hz-->40ms of available time and the steps take almost 70ms. In a PC I could keep the simulation at 25Hz in real time. Is there so much shit running in background in Android compared to other OS?
There must be another issue with your code. Try posting your AsyncTask in here.
You could also try something very basic like:
Create a new Thread that loops every 25Hz and update your UI by using the post() method of your UI elements or the runInUiThread() of your Activity. See if there's any code still running inside the UI Thread, that could do heavy work, that can be done outside the UI Thread.
I tried literally everything except for the most logic thing, trying the application without the debugger connected.
The reason to have slower simulation than in a PC, to freese UI events... all because the debugger takes a lot of resources from the device. So, I guess that from this point and avobe I will have to test the application without debugger, what forces me to reboot the phone each time to avoid the "waiting for debugger to connect".
Thank to all who tried.
I could be wrong, but I think that yours problem in synchronization on simulator.getSimulationResults() object. I can't see the realization of the simulator class and realization of the object returned by getSimulationResults(), but I suppose that getSimulationResults() returns the same object every time? If so, then it can be looks like this:
In the AsyncTaks call simulator.getSimulationResults().updateSimulation(...). If this method is synchronized, then this call will be lock the SimulationResults object for AsyncTaks thread.
updateSimulation(...) returns, and publishProgress() is called, but publishProgress() is only schedule the onProgressUpdate(Void... values) in the UI thread.
The new iteration in the AsyncTaks thread can be started befor the UI thread gets the control and executes onProgressUpdate(Void... values). So, AsyncTaks thread goes to the first step.
The UI thread gets the control and executes the onProgressUpdate(Void... values) and synchronized void updateHUD() methods, but updateHUD() can't be executed, because SimulationResults object is locked by the AsyncTaks thread in the updateSimulation(...) method. So the UI thread returns the control to the OS. This may occur many times.
So, onProgressUpdate(Void... values) method and all events in the UI thread can be executed only if the UI thread gets the control in the right moment when updateSimulation(...) method is not called in the AsyncTask thread.
You can check this idea by replacing the public synchronized void update HUD() on the public void update HUD(), and write something randomly in the TextView.
In any case, the use of AsyncTask in this case is not the best idea. AsyncTask's are executed in the TheadPool, but in the Android system this pool can consist from only one thread. So, all AsyncTask's will be executed one by one in the one thread.
in the beginning I thought it would be fairly simple, but I guess it's not.
I want to call a URL every 10 minutes, either when the app is in the background or
in the foreground. How can I realize this?
I'd use a Service with a Handler inside. Using directly Threads is another approach but it's more likely it will be killed if the Android OS needs to free memory.
The Handler part would be something like this:
boolean stopHandler = false;
Runnable runnable = new Runnable() {
#Override
public void run() {
// Do whatever you need
...
if (!stopHandler) {
handler.postDelayed(this, 600000); // 10 minutes
}
}
};
In iOS 7 you can schedule background operations for periodically fetching data from the network. This tutorial is an example of scheduling background fetch operations -