Multiple UI Threads with one Working Thread - java

I have an app with following Diagram:
Start Screen -> Control Screen <-> Settings Screen <-> Message Screen
The Working-Thread that always have to run is a HTTP Request which gathers Information constantly so it is started at Start Screen.
The Start Screen jumps into Control Screen.
The Information gathered in Working-Thread have to be passed to the Message Screen and being displayed all, even if I change to Settings or Control Screen. Is this made by running 2 UI Threads simultaneausly to update the Message Screen in the background?
(I first throught about What's app where the Messages are constantly displayed)

There is usually only 1 UI thread. If you want to have longer running tasks (especially if you want them to keep going regardless of the UI) then you should use a service. The service can to it's work in the background even when the app is closed. This way you can have one class responsible for all of your network traffic and data collection and all of your activities can just bind to that.

First of all, there is only ONE UI Thread, and it is the main thread that runs the application, looking at this answer will help you understand Threads in android much better:
The UIThread is the main thread of execution for your application.
This is where most of your application code is run. All of your
application components (Activities, Services, ContentProviders,
BroadcastReceivers) are created in this thread, and any system calls
to those components are performed in this thread.
Now, you want to perform actions that require access to the UI Thread(e.g. displaying something on the screen, animating a view, ...etc), So, you have more than an option to achieve that:
1- use the method runOnUIThread():
This method uses the current Activity context or the application context in order to run the wrapped-in-it code inside the UI thread(main thread), only you have to use its signature anywhere in the running activity:
runOnUiThread(new Runnable() {
#Override
public void run() {
mInfo.setText(str);
}
});
or even you can run it from outside the current activity by holding the activity or the application context anywhere and hence you will be able to run it even from a normal class:
MainActivity.mContext.runOnUiThread(new Runnable() {
#Override
public void run() {
mInfo.setText(str);
}
});
2- use AsyncTask:
AsyncTask is the android way to do a background work in a background thread then apply the result to the main thread(UI Thread).
All you have to do is use the method doInBackground() provided by AsyncTask
in order to handle background work that has to be done in a worker thread(like handling HTTP requests in your case), and then use the method postExecute() in order to reflect the result to the UI Thread:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... params) {
// handle your worker thread work here
return "result";
}
#Override
protected void onPostExecute(String result) {
// update your UI here
}
}
have a look here to put your hands better on it.

Related

Android: How to show a spinner while performing a long-running operation?

I have a ProgressBar in .xml that I want to show when a long-running operation. I use
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
to set its visibility in some onButtonClick method. If the above code is all that is in the method, it works just fine. The problem is when I have a method like this:
public void onButtonClick (android.view.View view){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
longRunningMethod(); // This method takes 5-10 seconds to run
progressSpinner.setVisibility(View.GONE);
}
The UI just locks up until longRunningMethod is done. That method works just fine, but the spinner never shows.
I tried running everything on a different thread with this:
public void onButtonClick (android.view.View view){
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(this::longRunningMethod);
}
And I added the spinner visibility changing stuff to longRunningMethod:
private void longRunningMethod(){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
// Logic that takes 5-10 seconds to run.
progressSpinner.setVisibility(View.GONE);
}
When I do this, the UI doesn't lock up, but nothing in longRunningMethod works. The spinner won't show and the logic also doesn't seem to work, although this may just be a problem with that logic not playing nice on not-the-UI-thread. I am very confused that the spinner visibility won't update from here though.
For running long task operations, you should use Worker Thread.
You must run your task in worker thread and then return task results to UI.
First method is using AsyncTask:
AsyncTask was intended to enable proper and easy use of the UI thread. However, the most common use case was for integrating into UI, and that would cause Context leaks, missed callbacks, or crashes on configuration changes. It also has inconsistent behavior on different versions of the platform, swallows exceptions from doInBackground, and does not provide much utility over using Executors directly.
Second one is using Pure Thread
All Android apps use a main thread to handle UI operations. Calling long-running operations from this main thread can lead to freezes and unresponsiveness. For example, if your app makes a network request from the main thread, your app's UI is frozen until it receives the network response. You can create additional background threads to handle long-running operations while the main thread continues to handle UI updates.
official document
and the last method is using Coroutine (just for kotlin)
if you are using kotlin i suggest this one otherwise use AsyncTask
Just try to run your tasks in other Thread and return results by using a callback like interface
first of all I recommend that get familiar with threads in android :
https://developer.android.com/guide/components/processes-and-threads
then , there is some lib to manage threads
AsyncTask , Couroutin , Rxjava . . .
Taking Sina's suggestion to use a Thread, the class now looks like:
public class MainActivity extends AppCompatActivity{
// Irrelevant code (fields, init, other button handlers, etc...)
private void onButtonClick(android.view.View view){
LongRunningThread longRunningThread = new LongRunningThread();
longRunningThread.start();
}
private class LongRunningThread extends Thread{
public void run(){
runOnUiThread(MainActivity.this::showSpinner);
// Logic that takes 5-10 seconds to run
runOnUiThread(MainActivity.this::hideSpinner);
}
}
private void showSpinner(){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.VISIBLE);
}
private void hideSpinner(){
ProgressBar progressSpinner = (ProgressBar)findViewById(R.id.progressSpinner);
progressSpinner.setVisibility(View.GONE);
}
}
Doing this shows the progress spinner while the long-running logic is running and then hides the progress spinner after the long-running logic has completed. The UI stays responsive throughout and doesn't lock up.
Compared to the original code, this uses Thread instead of ExecutorService. It also runs UI logic via AppCompatActivity.runOnUiThread().
It would seem the answer to doing any long-running tasks alongside UI updates, without locking up the UI, is to create a Thread and call Thread.start(). The Thread should contain the long-running logic as well as the UI logic. The UI logic within that Thread must be run using runOnUiThread().

Android Webview loadUrl does not work when coming from a worker thread

I am developing an app that contains a web view. A certain times during the app it does a call to Webview loadUrl.
Sometimes the call will come directly from an event on the UI thread and other times it comes from an event on a background worker thread. When it originates from the background thread I do a call to runOnUIThead() to ensure the actual call to loadURL happens on the UI thread.
What I am experiencing is that loadUrl() works fine when originating from the UI thread, however it fails to work when it comes from a worker thread (even though the actual call to loadUrl happens via a runnable I pass into runOnUIThread()).
Having set a break point I can see that in both instances loadUrl() is being called on the UI thread. Yet it works in one case but not the other.
I am currently sifting through the Android Webview source code to see if I can track down why sometimes it works and sometimes it doesn’t. If anyone can shed any light on the matter it would be greatly appreciated.
--- UPDATE ---
I have tried a few suggestions from this post here: WebView loadUrl works only once
Mainly doing the following before calling loadUrl:
webView.clearCache(true);
webView.loadUrl("Url");
And:
webView.clearCache(true);
webView.clearView();
webView.reload();
webView.loadUrl("about:blank");
webView.loadUrl("Url");
Unfortunately neither of them work.
In general, its not safe to create view outside of main thread.
In your particular case, this is not allowed, because WebView creates Handler() in its constructor for communication with UI thread. But since Handler's default constructor attaches itself to current thread, and current thread does not have Looper running, you're getting this exception.
You might think that creating a looper thread (that must be alive at least as long as WebView) might help you, but this actually a risky way to go. And I wouldn't recommend it.
You should stick with creating WebViews in main thread. All controls are usually optimized for fast construction, as they are almost always created in UI thread.
or You can call webview like this
runOnUiThread(new Runnable() {
#Override
public void run() {
// your webview method
}
});

Android handle thread with dynamic activity

I'm developing an Android network application. I want to have one worker thread that will handle all incoming data over a socket). The thread will be active all the time.
I can start the thread from one of my activities, but how to handle it after the activity is changed?
By handle, I mean how to interact with the thread (passing data from thread to current UI or sending data to the thread from the current UI).
I'm looking at AsyncTask but I'm not sure if it can be used with my situation
I think you want to look at one of two models.
The first is to use a Service which will "own" the socket connection. The second is to spin up a HandlerThread to "own" the connection. Which you choose is based on what the socket is doing.
If the socket connection contains data that you need to receive, typically stuff you'll cache locally, then a Service is more appropriate, because running in a Service means that your work will get done eventually. On the other hand if you are doing something like downloading an image that is only ever displayed in your UI, then using a raw HandlerThread might be the right choice. The reasoning here is that the moment your UI goes away your socket connection might as well be closed. Put another way, the choice is based on how ephemeral the use of the data is.
Now, if you're going to use a Service, please, please, use an IntentService which will handle the grotesque details of shutting down the Service at the appropriate time. It will also put your processing code on the correct Thread. A command sent to a Service by default runs on the main/UI thread of your application. An IntentService marshals the work to a background thread.
If you're going to use a HandlerThread, I would make a class that extends HandlerThread and then use the singleton pattern to handle accessing the same HandlerThread from any Activity.
One important question to ask yourself though is when your Service or HandlerThread shuts down. When is the work done and how is this signaled? Is it just when no more data arrives? When your apps UI goes into the background?
For this requirement I suggest that you create and use JAX-WS or JAX-RS.Here are a lot of examples which fully describe that. In that case you can use AsyncTask easily. Look at this answer, I suppose that this solution can meet your needs.
I would go with this:
Handler mHandler = new Handler();
Thread t = new Thread(new Runnable(
#Override
public void run () {
while (true /* or your breaking condition */) {
//Here you put your socket algorithm
mHandler.post(new Runnable() {
#Override
public void run () {
//Here you put every change you want to do on UI thread
}
}
//This is necessary to reduce process consumption
try {
Thread.sleep(30L);
} catch (Exception e) {
e.printStackTrace();
}
}
}));
t.start();
How to know on which UI
Each handler you create is attached to the class on which it was created.
As you can see here Android | Handler
For example, if you have a class that extends Activity and you create a Handler there, it'll work on that UI thread.

Background HTTP tasks

I have a splash page on my app, and what I want to do is initiate a background task to start downloading things so they're ready when the user needs them.
So for example a picture of the week or something.
Splash screen starts (start background task)
Splash screen finishes (background task still working)
Home screen starts (temporary "loading" place holder)
User navigates to another activity
Download finishes
User returns to home screen which is updated with picture of the week
Now I'm aware of Async Tasks, but I've heard that async tasks get canceled on finish() and I've also heard they don't get canceled.
Is this sort of task, background loading best handled in a service?
Can anyone provide me with a tutorial on loading things like this in the background?
EDIT:
People are mentioning that UI work should be in the UI and non-UI in the non-UI. I've considered handlers, but does the listener work when the current activity isn't active?
Lets say I start a handler in the main and then switch pages. When I come back I'm pretty sure the main UI won't be updated because the listener didn't trigger while I was viewing another page.
I'm assuming I'll have to create a class just for storing variables or something. Once the service finishes, it'll set a "pic_of_week_loaded" to true and when the main reloads it checks this var. How would I go about this?
You can extend from Application class, create a thread (or AsyncTask) within it that will basically download stuff from Internet. After it finishes, you can notify the home screen activity to show up the contents into the place holder.
Another choice is to use the Android Asynchronous Http Client
I would just do it in the activity but make sure to check that the activity is still active when you go to display it (make sure onDestroy hasn't been called on it yet)..
Then I would cache the result so you don't have to load it again from the web (store the date in the filename so you know which date the picture is for so you know if the cache holds the latest one already).
Consider using IntentService to do your background job, so you will not be bond to activity life cycle.
EDIT: as per comments.
Using own application object is quite simply. Just create it like this:
final public class MyApplication extends Application {
....
}
then update your Manifest to look like this:
<application
android:name=".MyApplication"
...
and theoretically that's it. You can now add your own methods there, incl. async task operations. And you can get handle to your application object with just MyApplication app = (MyApplication)getApplicationContext(); and then do app.XXX(); Some important notes though: if you'd like to use AsyncTask in your ApplicationObject, be aware of Android Bug #20915. The workaround, as per discussin there is to do this in onCreate():
public void onCreate() {
// Workaround for android bug #20915
// http://code.google.com/p/android/issues/detail?id=20915
try {
Class.forName("android.os.AsyncTask");
} catch (Exception e) {
e.printStackTrace();
}
super.onCreate();
...
I will try to explain why (1) keeping global state in Application sublcass and (2) using AsyncTask are both bad approaches for this case.
(1) OS may kill your app process if the app is not in the foreground and there is no running services in the app at the moment. More details on this in this post: How to declare global variables in Android?
(2) AsyncTask is tricker than it looks at first. :) For instance, if ((OS < 1.6) || (OS >= 3.0)) all tasks are run on the same background worker thread by default. I mean ALL tasks for the current Java process are executed on the only background thread. So the next possibly started tasks in other activities will have to wait untill that first/init one is done. I think this is not what you'd like to get (if user navigates from Home activity to some other activity and that another activity will need to load some data using AsyncTask, then make sure your first/init task is done quickly, because it will block any subsequent tasks). Of course, if the amount of work for the first/init task is not big, then you can don't worry about this point. More details on how AsyncTask works in this post: Running multiple AsyncTasks at the same time -- not possible?
So my advice would be to use IntentService to download the image/date you need on startup. Once it downloads the data it sets the flag in SharedPreferences and sends a broadcast. By this time the Home activity may be in 3 states:
active (passed onResume(), but not yet onPause())
paused (passed onPause(), but not yet returned to onResume)
destroyed
While it is active it uses broadcast receiver to listen to the service. When it is paused it unregisters the broadcast receiver, but when it comes to active the first thing it does is to check the flag from SharedPreferences. If flag is still not set, then registers broadcast receiver.
1. Well AsyncTask was introduced into android to give what its know for Painless Threading, but i don't think thats the case here....
2. Now Its always good to keep the UI work on the UI thread, and Non-UI work on the Non-UI thread, and that became a rule from the arrival of HoneyComb version of Android.
3. Now as you want to download few things at background, which DOES NOT INCLUDE reflecting the output on the UI. Then i think you look into CountDownLatch Class in the java.util.concurrent.
4. Else you can always run a Non-UI thread at background.
I prefer to use Threads and Handlers for background tasks. The thread will execute some operation in the background, then when the task is complete, update the handler to update the UI. Here is some sample code below:
Handler handler = new Handler() { //Declare this as a global variable to the class
#Override
public void handleMessage(Message msg) {
//display each item in a single line
if(msg.obj.equals("update subcat")){
subcategoryAdapter.notifyDataSetChanged();
}else if (msg.obj.equals("update products")){
productsAdapter.notifyDataSetChanged();
}else if (msg.obj.equals("update cat")){
categoryAdapter.notifyDataSetChanged();
}
}
};
Here is your background task:
Thread background = new Thread(new Runnable() {
#Override
public void run() {
getSubCategories(((Categories)searchResults.get(pos)).ID);
handler.sendMessage(handler.obtainMessage(pos, "update subcat"));
}
});
background.start();

When off the main thread, how can I get some code to run on the main thread as quickly as possible?

I have an user interface that is partly web based (WebView). It is connected to the Android UI through a Javascript Interface. When you tap on an element in the WebView, javascript calls up to Android and Android receives the call on the javascript/web thread. Not the UI (main) thread.
It arrives in Android in 1 or less milliseconds. No problem there. However, because I want to now change the UI, I have to switch over to the UI thread. (Android throws an exception if you modify the UI from off the main thread). I am currently using a Handler on the UI Thread and calling post().
This code (a Runnable) is then called anywhere between 120 and 300 ms later. This is a very noticeable lag for the UI to change from the user's touch.
Is there any way to get some code to run on the UI thread faster? Here is some example code:
An interface class:
public class JSInterface {
public void test() {
// Arrives here in 1ms after calling AndroidInterface.test(). Arrives n the web thread.
runOnUiThread(new Runnable() {
#Override
public void run() {
// Arrives here 100ms to 300ms after calling AndroidInterface.test(). Arrives on the main (UI) thread.
}
});
}
}
Added to a webview like this:
webview.addJavascriptInterface(new JSInterface(), "AndroidInterface");
Called in javascript like this:
AndroidInterface.test();
Thanks!
Is there any way to get some code to run on the UI thread faster?
runOnUiThread() and kin put a message on a message queue that the main application thread works off of. Much of the time, the main application thread's job is to pull a message off the queue and process it. However, the main application thread is responsible for calling most of your callbacks as well.
If you are seeing "120 and 300 ms" delays, that means one of two not-mutually-exclusive things:
The queue has quite the backlog
The main application thread is busy executing some other code
The relationship between WebView, the queue, and the main application thread is rather mysterious, compared to normal widgets, because WebView isn't a classic widget rendered in Java code. Hence, I have no idea if there is something that is occurring in your Web content that might explain this, or if this is fairly normal for WebView. You might try an experiment with simpler Web content and see whether you get similar delays, or if the delays are somehow more tied to the specific Web content that you are rendering.
Push come to shove, use Handler and postAtFrontOfQueue(). As the JavaDocs for that method note, though, this is dangerous.

Categories