I've created a Handler that can be accessed from anywhere within the activity and also written a method to make it easier to call the handler:
private Handler textFromBGThread = new Handler() {
#Override
public void handleMessage (Message msg) {
// Get the string from the msg
String outputString = msg.getData().getString("Output");
// Find the TextView
TextView Output = (TextView)findViewById(R.id.ConsoleOutputView);
// Display the output
Log.i("TextOutput","About to display message: " + outputString);
Output.setText(Output.getText() + outputString);
Log.i("TextOutput","Message displayed");
}
};
private void TextOutputWrapper (String outputText) {
Message msg = new Message();
Bundle bndle = new Bundle();
bndle.putString("Output", "\n" + outputText);
msg.setData(bndle);
textFromBGThread.handleMessage(msg);
}
So then this can be called from a background thread simply with:
TextOutputWrapper("Attemping to connect...");
This will work 1+ times, however, the actual visual change will cause a CalledFromWrongThreadException to be thrown. Being new to Java & Android, I'm stuck on why this is happening.
I have noticed that the crash tends to happen when there's a slightly longer time period between calls & that if the calls to TextOutputWrapper(String) are happening very quickly after one another, then it works. For example, this:
int i = 0;
while (i < 200) {
TextOutputWrapper(String.valueOf(i));
i++;
}
works fine.
Having looked at LogCat, it seems that the garbage collector frees up some resources and then the next time TextOutputWrapper(String) is called, it crashes (when Output.SetText(String) is called, to be precise), although I'm not exactly sure why that would cause this error.
There's a few things I'd change here:
Using Handler
A Handler is useful if you want to trigger the UI to update, and do so from a non-UI thread (aka "background" thread).
In your case, it's not serving that purpose. You are directly calling
textFromBGThread.handleMessage(msg);
It's not designed for you to do that. The way you are supposed to use Handler is to implement what you want done to the UI in the handleMessage(Message) method. You did that. But, you shouldn't directly call handleMessage(). If you do that, then handleMessage() will be called from whatever thread invokes TextOutputWrapper(). If that's a background thread, then that's wrong.
What you want to do is to call the handler's sendMessage(Message) method (or one of the other available variants). sendMessage() will put your message in a thread-safe queue, that is then processed on the main thread. The main thread will then invoke your handler's handleMessage(), passing it back the queued message, and allowing it to safely change the UI. So, change TextOutputWrapper() to use this:
private void TextOutputWrapper (String outputText) {
Message msg = new Message();
Bundle bndle = new Bundle();
bndle.putString("Output", "\n" + outputText);
msg.setData(bndle);
textFromBGThread.sendMessage(msg);
}
Java Conventions
This code is a bit hard to read, for an experienced Java developer. In Java, typical coding standards reserve upper case names for things like classes, while methods start with lower case letters. So, please rename the method to:
private void textOutputWrapper (String outputText);
or, better yet, since this is in fact a method, and not a wrapper, per se, rename to something like
private void outputText(String text);
Safe Threading Alternatives
Finally, I might recommend that if you simply want a method that allows you to safely modify the UI from any thread, use another technique. I don't find Handler to be that easy to use for beginners.
private void outputText(final String outputString) {
runOnUiThread(new Runnable() {
#Override
public void run() {
// Find the TextView
TextView output = (TextView)findViewById(R.id.ConsoleOutputView);
// Display the output
Log.i("TextOutput","About to display message: " + outputString);
output.setText(Output.getText() + outputString);
Log.i("TextOutput","Message displayed");
}
});
}
runOnUiThread() is a method available in every Activity.
I'll also point you to some general docs on understanding threading in Android:
http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html
http://android-developers.blogspot.com/2009/05/painless-threading.html
Related
My service has a PhoneStateListener that overrides the onCellInfoChanged method. When running on Android Studio, it'll log whenever the method is called. Based on the logs, it seems that sometimes the method gets called consecutively (a couple milliseconds between logs).
public class OnCellChangeService extends Service
{
// other field declarations
private PhoneStateListener mPhoneStateListener = new PhoneStateListener()
{
#SuppressLint("DefaultLocale")
#Override
public void onCellInfoChanged(List<CellInfo> cellInfoList)
{
String timeStamp = simpleDateFormat.format(new Date());
List<CellNetwork> cellNetworks = setCellNetwork(cellInfoList);
String networkStrength = "";
int index = 1;
for (CellNetwork cell : cellNetworks)
networkStrength += String.format("%s (%d)%s\n", timeStamp, index++, cell.record());
try {
writer.write(networkStrength);
writer.flush();
Log.d("Phone Listener", "Cell Change");
} catch (Exception e) {
e.printStackTrace();
}
}
};
// rest of service
}
All the listener does is take the cellInfoList, calls another method to get a list that contains a list of custom class objects made from a subset of the original list. It writes to a file with a time stamp and other various pieces of information from each list object.
When the listener's method is called again while the same method is executing, does the current method stop and restart for the new call? Does it run in a separate thread of execution? Does it wait for the method to finish executing? If another call does halt or interfere with the same method executing, how could I implement threading and/or synchronization to make sure every called is executed in full while maintaining order?
The callbacks from PhoneStateListener are all made on the main (UI) thread. Therefore, each callback method will run to completion before the next one is called.
I have lots of different http calls to make, and have been constructing custom listeners for each one. The listeners are all the same, except for a method call or two within the handleHttpResponse. So most of the following gets rewritten each and every time:
HttpResponseListener listener = new HttpResponseListener() {
String status;
public void handleHttpResponse(HttpResponse httpResponse) {
getSession(httpResponse);
String result = httpResponse.getResultAsString();
if (result != null) {
getNeeds(result); // <== unique part
}
System.out.println("Network Response:: Success");
}
public void failed(Throwable t) {
status = "fail";
System.out.println("Network Response:: [Fail] " + status);
t.printStackTrace();
}
#Override
public void cancelled() {
status = "cancelled";
System.out.println("[getNeedType()] Network Response:: [Cancelled] " + status);
}
};
How do I de-clutter my code from all these repetitive re-pastes? Within the javadoc it says to:
Passing data to the * rendering thread should be done using {#link
Application#postRunnable(java.lang.Runnable runnable)}
Haven't used runnables before, but I think they're custom blocks of logic to be executed. Despite the 'post' prefix I can't see how to attach a runnable to the end of the listeners execution. I'm thinking to extend the HttpResponseListener to accept a Runnable as an argument, then call the Runnable block at the point in handleHttpResponse when I get my results back. But how do I pass the results string to the runnable? Runnables don't see to take any arguments. Do you extend the runnable as well to do so?
You make a new abstract class implementing HttpResponseListener. Implement cancelled, handleHttpResponse and failed methods the way you use, add the abstract method getNeeds to your class.
From now on, use your new abstract class.
I can call Snackbar.make() from a background thread without any problems. This is surprising to me since I thought UI operations are only allowed from the UI thread. But that is definitely not the case here.
What exactly makes Snackbar.make() different? Why doesn't this cause exceptions like any other UI component when you modify it from a background thread?
First of all: make() doesn't perform any UI related operations, it just creates a new Snackbar instance. It is the call to show() which actually adds the Snackbar to the view hierarchy and performs other dangerous UI related tasks. However you can do that safely from any thread because it is implemented to schedule any show or hide operation on the UI thread regardless of which thread called show().
For a more detailed answer let's take a closer look at the behaviour in the source code of the Snackbar:
Let's start where it all begins, with your call to show():
public void show() {
SnackbarManager.getInstance().show(mDuration, mManagerCallback);
}
As you can see the call to show() gets an instance of the SnackbarManager and then passes the duration and a callback to it. The SnackbarManager is a singleton. Its the class which takes care of displaying, scheduling and managing a Snackbar. Now lets continue with the implementation of show() on the SnackbarManager:
public void show(int duration, Callback callback) {
synchronized (mLock) {
if (isCurrentSnackbarLocked(callback)) {
// Means that the callback is already in the queue. We'll just update the duration
mCurrentSnackbar.duration = duration;
// If this is the Snackbar currently being shown, call re-schedule it's
// timeout
mHandler.removeCallbacksAndMessages(mCurrentSnackbar);
scheduleTimeoutLocked(mCurrentSnackbar);
return;
} else if (isNextSnackbarLocked(callback)) {
// We'll just update the duration
mNextSnackbar.duration = duration;
} else {
// Else, we need to create a new record and queue it
mNextSnackbar = new SnackbarRecord(duration, callback);
}
if (mCurrentSnackbar != null && cancelSnackbarLocked(mCurrentSnackbar,
Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE)) {
// If we currently have a Snackbar, try and cancel it and wait in line
return;
} else {
// Clear out the current snackbar
mCurrentSnackbar = null;
// Otherwise, just show it now
showNextSnackbarLocked();
}
}
}
Now this method call is a little more complicated. I am not going to explain in detail what's going on here, but in general the synchronized block around this ensures thread safety of calls to show().
Inside the synchronized block the manager takes care of dismissing currently shown Snackbars updating durations or rescheduling if you show() the same one twice and of course creating new Snackbars. For each Snackbar a SnackbarRecord is created which contains the two parameters originally passed to the SnackbarManager, the duration and the callback:
mNextSnackbar = new SnackbarRecord(duration, callback);
In the above method call this happens in the middle, in the else statement of the first if.
However the only really important part - at least for this answer - is right down at the bottom, the call to showNextSnackbarLocked(). This where the magic happens and the next Snackbar is queued - at least sort of.
This is the source code of showNextSnackbarLocked():
private void showNextSnackbarLocked() {
if (mNextSnackbar != null) {
mCurrentSnackbar = mNextSnackbar;
mNextSnackbar = null;
final Callback callback = mCurrentSnackbar.callback.get();
if (callback != null) {
callback.show();
} else {
// The callback doesn't exist any more, clear out the Snackbar
mCurrentSnackbar = null;
}
}
}
As you can see first we check if a Snackbar is queued by checking if mNextSnackbar is not null. If it isn't we set the SnackbarRecord as the current Snackbar and retrieve the callback from the record. Now something kind of round about happens, after a trivial null check to see if the callback is valid we call show() on the callback, which is implemented in the Snackbar class - not in the SnackbarManager - to actually show the Snackbar on the screen.
At first this might seem weird, however it makes a lot of sense. The SnackbarManager is just responsible for tracking the state of Snackbars and coordinating them, it doesn't care how a Snackbar looks, how it is displayed or what it even is, it just calls the show() method on the right callback at the right moment to tell the Snackbar to show itself.
Let's rewind for a moment, up until now we never left the background thread. The synchronized block in the show() method of the SnackbarManager ensured that no other Thread can interfere with everything we did, but what schedules the show and dismiss events on the main Thread is still missing. That however is going to change right now when we look at the implementation of the callback in the Snackbar class:
private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
#Override
public void show() {
sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW, Snackbar.this));
}
#Override
public void dismiss(int event) {
sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0, Snackbar.this));
}
};
So in the callback a message is send to a static handler, either MSG_SHOW to show the Snackbar or MSG_DISMISS to hide it again. The Snackbar itself is attached to the message as payload. Now we are almost done as soon as we look at the declaration of that static handler:
private static final Handler sHandler;
private static final int MSG_SHOW = 0;
private static final int MSG_DISMISS = 1;
static {
sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(Message message) {
switch (message.what) {
case MSG_SHOW:
((Snackbar) message.obj).showView();
return true;
case MSG_DISMISS:
((Snackbar) message.obj).hideView(message.arg1);
return true;
}
return false;
}
});
}
So this handler runs on the UI thread since it is created using the UI looper (as indicated by Looper.getMainLooper()). The payload of the message - the Snackbar - is casted and then depending on the type of the message either showView() or hideView() is called on the Snackbar. Both of these methods are now executed on the UI thread!
The implementation of both of these is kind of complicated, so I won't go into detail of what exactly happens in each of them. However it should be obvious that these methods take care of adding the View to the view hierarchy, animating it when it appears and disappears, dealing with CoordinatorLayout.Behaviours and other stuff regarding the UI.
If you have any other questions feel free to ask.
Scrolling through my answer I realize that this turned out way longer than it was supposed to be, however when I see source code like this I can't help myself! I hope you appreciate a long in depth answer, or maybe I might have just wasted a few minutes of my time!
Snackbar.make is completely safe from being called form non-ui thread. It uses an handler inside its manager which operates on the main looper thread and thus hides the caller form the underlying complexities of it.
Only the original thread that created a view hierarchy can touch its views.
If you use the onPostExecute you'll be able to access the views
protected void onPostExecute(Object object) { .. }
I've a method who return a result (return an integer), my method is executed in a Thread for load 40 000 objects, i return an integer who count the number objects loaded. My question is, How return the int with the Thread ? Actually, the result is returned directly and is equal to 0.
public int ajouter(params) throws DaoException, ConnectException {
final ProgressDialog dialog = ProgressDialog.show(mActivity, "Title",
"Message", true);
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
dialog.dismiss();
}
};
Thread t = new Thread() {
public void run() {
try {
Str_Requete = "SELECT * FROM Mytable";
ResultSet result = ExecuteQuery(Str_Base, Str_Requete);
Index = addObjects(result);
handler.sendEmptyMessage(0);
} catch (SQLException e) {
e.printStackTrace();
}
}
};
t.start();
return Index;
}
When i call my method in my mainActivity :
int test = myObjs.ajouter(params);
test is equal to 0, the value is returned directly...
My constraint is didnt use AsyncTask.
The whole point of using a Thread is not to block the calling code while performing the task of the thread. Thread.start() returns immediately, but in the meantime a new thread is started in parallel to the current thread which will execute the code in the run() method.
So by definition there is no such thing as returning a value from a thread execution. You have to somehow send a signal back from the thread that performed the task to the thread in which you need the result. There are many ways of doing this, there's the standard Java wait/notify methods, there is the Java concurrency library etc.
Since this is Android, and I assume your calling code is running on the main thread, it's probably wise to use the functionality of Handler. And in fact, you are already doing that - you have a Handler that closes the dialog when the thread is done with its work - but for some reason you seem to expect the result of that work to be ready before it has even started. It would be reasonable to extend your existing Handler with some code that does something with the calculated value and remove the code that returns the value of a variable before or at the same time as it's being calculated by another thread.
I also strongly encourage you to study some concurrency tutorial such as Oracle's concurrency lesson or Android Thread guidelines to really understand what's going on in the background. Writing concurrent code without mastering the concepts is bound to fail sooner or later, because it's in the nature of concurrency that multiple things are happening at the same time, will finish in random order etc. It may not fail often, but you will go crazy wondering why something that works 90% of the time suddenly fails. That's why topics such as atomicity, thread synchronization etc are critical to comprehend.
Edit: Simple Android example of starting a worker thread, performing some work, posting back event to main thread.
public class MyActivity extends Activity {
private Handler mHandler = new Handler();
...
private void doSomeWorkInBackground() {
new Thread() {
public void run() {
// do slow work, this may be blocking
mHandler.post(new Runnable() {
public void run() {
// this code will run on main thread,
// updating your UI or whatever you need.
// Hence, code here must NOT be blocking.
}
});
}
}.start();
// This code will be executed immediately on the main thread, and main thread will not be blocked
}
You could in this example also use Activity.runOnUiThread(Runnable).
Please consider however that AsyncTask basically wraps this kind of functionality in a very convenient way, so if it suits your purposes you should consider using AsyncTask.
If you dont want to use AsyncTask or ForkJoin, then you could implement an Interface e.g. callback in your main class.
In your Example you dont wait until the Thread is done... thread.join
One Solution:
Your Thread is a extra class with an constructor to hold the reference to the calling class.
public Interface callback
{
public int done();
}
public class main implements callback
{
...
CustomThread t = new CustomThread(this)
...
}
public class CustomThread extends Thread
{
private Callback cb;
public CustomThread(Callback cb)
{
this.cb=cb;
}
.
.
.
//when done
cb.done(int)
}
I have a thread which updates a textview using a runnable:
// runnable to allow updating the UI from the thread
Runnable updateTextView = new Runnable() {
public void run() {
mTextView.setText(mDisplayedText);
mTextView.invalidate();
}
};
However, the text does not update properly. It works for the first few writes, then only writes half the text, leaving the second half of the previous text there.
Turning the screen around causes it to refresh and draws it correctly.
The textview is multiline and I write a string to it which contains \n characters for the end of lines.
The invalidate call above makes no difference.
Any ideas?
UPDATE: mDisplayedText is declared in the activity which also contains my thread class. in the thread run loop, I call:
mDisplayedText = getText()
runOnUiThread(updateTextView);
The loop contains a 100ms sleep, but it only writes to the text when it has changed so in reality it will be less than once a second
ANSWER:
Slightly embarrassing this. The problem was in other code.
I was receiving a UDP socket into the same packet, and reading the packet.getData into a new string.
This was copying the whole packet into the string, rather than just the bytes received in that message.
The second problem was that I needed to call packet.setLength each time to set the length available to the whole packet.
Thanks for the answers!
you can only update your UI on UI thread, put your setText() method inside this
runOnUiThread(new Runnable() {
#Override
public void run() {
btn.setText(someValue);
}
});
Runnable is an interface. Think of it like a set of commands ready for execution. You can use a Handler to work with your code:
Handler textViewHandler = new Handler();
// runnable to allow updating the UI from the thread
Runnable updateTextView = new Runnable() {
public void run() {
mTextView.setText(mDisplayedText);
}
};
textViewHandler.post(updateTextView);
Also, please consider #Karakuri's suggestion.