I'm investigating cross platform library published by dropbox.
following code is from it.
I have questions
(1)it makes handler which is connected with main looper.
I have heard that this kind of way we can create ui thread handler.
but does it has any relation with original ui thread(Activity ui thread)
or it really creates another seperated ui threads?
if there are 2 ui threads, then it it possible that one ui thread access another ui components and modify its ui?
public class AndroidEventLoop extends EventLoop
{
Handler mHandler;
public AndroidEventLoop()
{
mHandler = new Handler(Looper.getMainLooper());
}
public void post(final AsyncTask task)
{
mHandler.post(new Runnable()
{
#Override
public void run()
{
task.execute();
}
});
}
}
public abstract class EventLoop {
public abstract void post(AsyncTask task);
}
and it called in Activity
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
EventLoop mainThread = new AndroidEventLoop();
No it's not creating a second main thread. All tasks that are posted to AndroidEventLoop are processed on the Main Thread.
This for example, enables you to do changes in the UI after data was processed on a background thread.
http://developer.android.com/reference/android/os/Looper.html#getMainLooper()
https://blog.nikitaog.me/2014/10/11/android-looper-handler-handlerthread-i/
Related
I need to run something inside my extends Application class on the UI thread
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
//I need to run code here on the main thread
}
}, 1000);
do I need to pass the activity? It will be difficult since I am calling it from multiple activities
Methods called by Android in the Application class already run on the UI thread (a.k.a 'main' thread).
When in doubt you can use Thread.currentThread() to find out.
Remember to avoid doing costly operations on the main thread.
You need to call runOnUiThread
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});
The scenario is
I have two threads and a UI thread. The UI thread when clicked on login button creates a ClientThread which creates a socket and runs until the socket is connected, whenever a message is received i use a handler to post message to another thread called ProcessDataThread, now on receiving some messages from server i need to update UI related stuff from ProcessDataThread, I searched around alot and i found these two ways runonUiThread function which i guess can only be run from the Activity Class which is useless and the Asynctask method which i am not sure how to pass the activity context to...
Here is the code
The code executed when clicked on Login Button in the MainActivity
public void onLoginClick(View view)
{
global_constants.clientObject = new ClientThread();
global_constants.clientThread = new Thread(global_constants.clientObject);
global_constants.clientThread.start();
}
The code in ClientThread run method
public class ClientThread implements Runnable {
.......
#Override
public void run() {
......
while(!Thread.currentThread().isInterrupted() && (!CloseThread))
{
byte[] buff;
....
global_constants.updateConversationHandler.post(new ProcessDataThread(buff));
}
}
}
The method code in ProcessDataThread after parsing out the incoming data and stuff
public class ProcessDataThread implements Runnable {
.........
void ProcessLoginFailedPacket(byte[] buff)
{
// how to call the UI thread from here for updating some UI elements??
}
}
[EDIT]
i stored the activity context in a global variable and then did it this way, but i dont know whether it will be safer or not
((Activity)global_constants.MainContext).runOnUiThread(new Runnable(){
public void run()
{
TextView txtErr = (TextView) ((Activity)global_constants.MainContext).findViewById(R.id.errMsg);
txtErr.setVisibility(0);
txtErr.setText(reason);
}
});
You can post a runnable which does the UI operation to main thread as follows,
public class Utils {
public static void runOnUiThread(Runnable runnable){
final Handler UIHandler = new Handler(Looper.getMainLooper());
UIHandler .post(runnable);
}
}
Utils.runOnUiThread(new Runnable() {
#Override
public void run() {
// UI updation related code.
}
});
My app combines a SurfaceView running on its own thread with a bunch of regular Views running on the main application thread. For the most part, this works fine. However, when I try to have something on the SurfaceView's thread trigger a change to one of the UI elements in the main application thread, I get android.View.ViewRoot$CalledFromWrongThreadException.
Is there a right way around this? Should I use an asynctask? Runonuithread()? Or is mixing a SurfaceView on its own thread with other UI elements on the main thread just an inherently bad thing to do?
If my question doesn't make sense, here's pseudocode that may be clearer.
// Activity runs on main application thread
public class MainActivity extends Activity {
RelativeLayout layout;
TextView popup;
MySurfaceView mysurfaceview;
protected void onCreate(Bundle savedInstanceState) {
...
setContentView(layout);
layout.addView(mysurfaceview); // Surface View is displayed
popup.setText("You won"); // created but not displayed yet
}
public void showPopup() {
layout.addView(popup);
}
}
// Surface View runs on its own separate thread
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, OnTouchListener {
private ViewThread mThread;
public boolean onTouch(View v, MotionEvent event) {
...
if (someCondition == true) {
mainactivity.showPopup();
// This works because onTouch() is called by main app thread
}
}
public void Draw(Canvas canvas) {
...
if (someCondition == true) {
mainactivity.showPopup();
// This crashes with ViewRoot$CalledFromWrongThreadException
// because Draw is called by mThread
// Is this fixable?
}
}
}
You cannot change UI elements from other threads other than the UI thread. However, you can still post commands to the UI thread from another thread to update the elements.
You can do the following:
Instantiate a handler in the UI thread like:
final Handler handler = new Handler();
Then in your other thread, you can post a message to the UI thread to update your view through the handler as follows:
handler.post(new Runnable(){
public void run(){
//Update your view here
}
});
And you should be goos to go. Just remember, you MUST instantiate the handler in the UI thread.
Or
If you are running your other thread inside an Activity instance, you can use:
this.runOnUiThread(new Runnable(){
public void run(){
//your UI update code here
}
});
Should I use an asynctask? Runonuithread()?
Either of these should be fine. I typically use AsyncTask for most things that need to update the UI after doing a background process. But either should work.
AsyncTask Docs
runOnUiThread example and Docs
Why don't you use callback? In MySurfaceView, you can add an interface,
public interface onClickSurfaceView{
public void change();
}
then in your activity implement MySurfaceView.onClickSurfaceView, and you can have a reference to surfaceView. Then call the surfaceView.setListener(this) to register the activity as a subscriber,
and you can call the update your UI in the change() method.
simple answer is you cannot update UI elements from a non-UI thread.
you need to have a callback method or something that calls back to the main thread to update the UI
You need to use this -
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Populate UI
}
});
This question already has answers here:
Can't create handler inside thread that has not called Looper.prepare()
(30 answers)
Closed 2 years ago.
I have an Android app running a thread. I want a Toast message to show with a message.
When I do this, I get the below exception:
Logcat trace:
FATAL EXCEPTION: Timer-0
java.lang.RuntimeException: Can't create handler inside thread that has not
called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at android.widget.Toast$TN.<init>(Toast.java:322)
at android.widget.Toast.<init>(Toast.java:91)
at android.widget.Toast.makeText(Toast.java:238)
Is there a work around for pushing Toast messages from threads to the User Interface?
I got this exception because I was trying to make a Toast popup from a background thread.
Toast needs an Activity to push to the user interface and threads don't have that.
So one workaround is to give the thread a link to the parent Activity and Toast to that.
Put this code in the thread where you want to send a Toast message:
parent.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(parent.getBaseContext(), "Hello", Toast.LENGTH_LONG).show();
}
});
Keep a link to the parent Activity in the background thread that created this thread. Use parent variable in your thread class:
private static YourActivity parent;
When you create the thread, pass the parent Activity as a parameter through the constructor like this:
public YourBackgroundThread(YourActivity parent) {
this.parent = parent;
}
Now the background thread can push Toast messages to the screen.
Android basically works on two thread types namely UI thread and background thread. According to android documentation -
Do not access the Android UI toolkit from outside the UI thread to fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Now there are various methods to solve this problem. I will explain it by code sample
runOnUiThread
new Thread()
{
public void run()
{
myactivity.this.runOnUiThread(new runnable()
{
public void run()
{
//Do your UI operations like dialog opening or Toast here
}
});
}
}.start();
LOOPER
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
AsyncTask
AsyncTask allows you to perform asynchronous work on your user interface. It performs the blocking operations in a worker thread and then publishes the results on the UI thread, without requiring you to handle threads and/or handlers yourself.
public void onClick(View v) {
new CustomTask().execute((Void[])null);
}
private class CustomTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... param) {
//Do some work
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
}
Handler
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.
Message msg = new Message();
new Thread()
{
public void run()
{
msg.arg1=1;
handler.sendMessage(msg);
}
}.start();
Handler handler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
if(msg.arg1==1)
{
//Print Toast or open dialog
}
return false;
}
});
Here's what I've been doing:
public void displayError(final String errorText) {
Runnable doDisplayError = new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), errorText, Toast.LENGTH_LONG).show();
}
};
messageHandler.post(doDisplayError);
}
That should allow the method to be called from either thread.
Where messageHandler is declared in the activity as ..
Handler messageHandler = new Handler();
From http://developer.android.com/guide/components/processes-and-threads.html :
Additionally, the Android UI toolkit is not thread-safe. So, you
must not manipulate your UI from a worker thread—you must do all
manipulation to your user interface from the UI thread. Thus, there
are simply two rules to Android's single thread model:
Do not block the UI thread
Do not access the Android UI toolkit from outside the UI thread
You have to detect idleness in a worker thread and show a toast in the main thread.
Please post some code, if you want a more detailed answer.
After code publication :
In strings.xml
<string name="idleness_toast">"You are getting late do it fast"</string>
In YourWorkerThread.java
Toast.makeText(getApplicationContext(), getString(R.string.idleness_toast),
Toast.LENGTH_LONG).show();
Don't use AlertDialog, make a choice. AlertDialog and Toast are two different things.
runOnUiThread(new Runnable(){
public void run() {
Toast.makeText(getApplicationContext(), "Status = " + message.getBody() , Toast.LENGTH_LONG).show();
}
});
this works for me
You can simply use BeginInvokeOnMainThread(). It invokes an Action on the device main (UI) thread.
Device.BeginInvokeOnMainThread(() => { displayToast("text to display"); });
It is simple and works perfectly for me!
EDIT : Works if you're using C# Xamarin
I got this error in a JobService from the following code:
BluetoothLeScanner bluetoothLeScanner = getBluetoothLeScanner();
if (BluetoothAdapter.STATE_ON == getBluetoothAdapter().getState() && null != bluetoothLeScanner) {
// ...
} else {
Logger.debug(TAG, "BluetoothAdapter isn't on so will attempting to turn on and will retry starting scanning in a few seconds");
getBluetoothAdapter().enable();
(new Handler()).postDelayed(new Runnable() {
#Override
public void run() {
startScanningBluetooth();
}
}, 5000);
}
The service crashed:
2019-11-21 11:49:45.550 729-763/? D/BluetoothManagerService: MESSAGE_ENABLE(0): mBluetooth = null
--------- beginning of crash
2019-11-21 11:49:45.556 8629-8856/com.locuslabs.android.sdk E/AndroidRuntime: FATAL EXCEPTION: Timer-1
Process: com.locuslabs.android.sdk, PID: 8629
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:203)
at android.os.Handler.<init>(Handler.java:117)
at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.startScanningBluetoothAndBroadcastAnyBeaconsFoundAndUpdatePersistentNotification(BeaconScannerJobService.java:120)
at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.access$500(BeaconScannerJobService.java:36)
at com.locuslabs.sdk.ibeacon.BeaconScannerJobService$2$1.run(BeaconScannerJobService.java:96)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
So I changed from Handler to Timer as follows:
(new Timer()).schedule(new TimerTask() {
#Override
public void run() {
startScanningBluetooth();
}
}, 5000);
Now the code doesn't throw the RuntimeException anymore.
In my OnCreate method I have created a thread that listens to incoming message!
In OnCreate() {
//Some code
myThread = new Thread() {
#Override
public void run() {
receiveMyMessages();
}
};
myThread.start();
// Some code related to sending out by pressing button etc.
}
Then, receiveMyMessage() functions…
Public void receiveMyMessage()
{
//Receive the message and put it in String str;
str = receivedAllTheMessage();
// << here I want to be able to update this str to a textView. But, How?
}
I checked this article but it did not work for me, no luck!
Any updates to the UI in an Android application must happen in the UI thread. If you spawn a thread to do work in the background you must marshal the results back to the UI thread before you touch a View. You can use the Handler class to perform the marshaling:
public class TestActivity extends Activity {
// Handler gets created on the UI-thread
private Handler mHandler = new Handler();
// This gets executed in a non-UI thread:
public void receiveMyMessage() {
final String str = receivedAllTheMessage();
mHandler.post(new Runnable() {
#Override
public void run() {
// This gets executed on the UI thread so it can safely modify Views
mTextView.setText(str);
}
});
}
The AsyncTask class simplifies a lot of the details for you and is also something you could look into. For example, I believe it provides you with a thread pool to help mitigate some of the cost associated with spawning a new thread each time you want to do background work.
Android supports message-passing concurrency using handlers and sendMessage(msg). (It is also possible to use handlers for shared-memory concurrency.) One tip is to call thread.setDaemon(true) if you wish the thread to die when the app dies. The other tip is to have only one handler and use message.what and a switch statement in the message handler to route messages.
Code and Code