How to edit EditText and TextView in UI simultaneously - android

I'm implementing a simple chatting application which has a TextView for text messages and a EditText for input.
I update my TextView by following method:
private void addChatContent(String authorName, String content){
final String newLine = authorName+" : "+content+"\n";
chat_content.append(newLine);
scroll.post(new Runnable(){
#Override
public void run() {
scroll.smoothScrollTo(0, chat_content.getBottom()+5000);
}
});
}
A problem I'm facing is: when there are new incoming messages, UI thread will be busy for refreshing the TextView. It makes my EditText become lag, I can hardly edit my input.
I can not refresh the TextView with another thread, right?
So what should I do to overcome this limitation?
Could somebody give me some light? Thanks in advance.

Ultimately, and unfortunately, there is only one thread dedicated to the UI. If you are updating the TextView there is no way you can simultaneously have a lag-free experience with the EditText. You already know this, but I fear some people answering this question may not, so here's a reference:
The system does not create a separate thread for each instance of a
component. All components that run in the same process are
instantiated in the UI thread, and system calls to each component are
dispatched from that thread. Consequently, methods that respond to
system callbacks (such as onKeyDown() to report user actions or a
lifecycle callback method) always run in the UI thread of the process
...
When your app performs intensive work in response to user interaction,
this single thread model can yield poor performance unless you
implement your application properly. Specifically, if everything is
happening in the UI thread, performing long operations such as network
access or database queries will block the whole UI. When the thread is
blocked, no events can be dispatched, including drawing events.
...
Additionally, the Andoid 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
Hence, the answer is quite clear: don't do this. Why does the Textview have to be absolutely, 100% up to date, as the user is updating the EditText field? Why are you scrolling all the way to the bottom; maybe you can get away with deleting most of the contents of the TextView and, when the user scrolls, dynamically re-adding content to it?
IMO you should focus on reducing the amount of work you need to do on the TextView.

Related

What constitutes "UI interactions" for AsyncTask?

AsyncTask is a standard way to perform long running operations asynchronously on a background thread without holding up the UI thread. One should not perform any UI interactions from the doInBackground() method.
My question: What are examples of UI interactions that are forbidden? Would it be any of the following:
LayoutInflater.inflate()
View.findViewById()
TextView.setText()
I'm inclined to say yes, but we have some code right now that does all of these (and more) and is called from the doInBackground() method, and yet the code is working. I've seen other people indicate they receive an exception when attempting to perform UI activity from doInBackground(), but that is not our experience.
Our code is generating an on-screen report that is not visible until the entire operation is complete. On rare occasion (hard to reproduce) when attempting to cancel the operation very quickly, we will see the application get into a weird state, but it doesn't crash.
Before changing our code in hopes of finding this rare condition, I wanted to see if anyone had some thoughts on why our code is "working" as-is.
The only other tidbit of information that might be helpful is that our doInBackground method has the following code template:
protected Boolean doInBackground(Void... voids) {
if (null == Looper.myLooper()) {
Looper.prepare();
}
publishProgress(0.0);
// Perform ui/non-ui logic here
Looper myLooper = Looper.myLooper();
if (null != myLooper && Looper.getMainLooper() != myLooper) {
myLooper.quit();
}
return true;
}
The Looper is needed for some of the report generating code (omitted) that uses a new Handler() to generate data. I'm not sure if creating the Looper is somehow making our ui interactions legal.
(I do have a stack trace that clearly shows our UI activity being called from doInBackground, in case you thought we might be spinning off some separate threads to update our UI)
AsyncTask is not meant for really long running work, it should complete within a few seconds. It is a one-shot completely managed thread context, which should not have its own Looper attached to it. That actually will break the backing AsyncTask functionality - starving off other future AsyncTask operations you may be starting. If you have something which requires a Looper, you should be using your own Thread or ThreadPool rather than an AsyncTask. You'll also want to make sure you retain a reference to your AsyncTask so it can be cancelled appropriately - this is a source of many memory leaks and/or exceptions due to invalid state when onPostExecute() is called.
The intent of the publishProgress() method is to give your app the ability to get updates it can reflect on the UX. You are correct, setText(), etc. should not be run in the doInBackground() callback. That callback is executed in arbitrary thread context in which you do not control and cannot make UI updates.
You may be able to use inflateLayout() and findViewById(), but this is not a good practice to do this outside of initialization as these are potentially expensive operations. Inflation has to parse the binary layout and create view objects on the fly. Finding by ID walks the entire view hierarchy to find the component you desire. A better practice would be to cache these at creation (for an Activity or Fragment) or when creating a view as part of an adapter (such as a ViewHolder in RecyclerView.

Is it okay to read data from UI elements in another thread?

I just found out that some of my code is (unindentionally) running in a worker thread and reads some data from UI elements:
e.g. checkbox.isChecked(), textView.getText()
and to my surprise, this works just fine...
I expected that it would crash with an exception (like the following exception that I get when I want to write to UI elements):
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
According to the Android docs:
Only objects running on the UI thread have access to other objects on
that thread. Because tasks that you run on a thread from a thread pool
aren't running on your UI thread, they don't have access to UI
objects.
So, is it really okay to read data from UI elements from other threads?
If not: why is there no exception?
is it really okay to read data from UI elements from other threads?
No, but not for the reasons that you may be thinking.
First, as soon as you fork the background thread, the user might press BACK or otherwise do something that destroys your activity. It is not safe to try to use widgets of a destroyed activity.
Second, there is little to no thread synchronization logic in the View class or its subclasses. The main application thread may be modifying the data at the same time that you are trying to use it, resulting in possible concurrency issues.
I would pass the data from the widgets into the thread (e.g., thread constructor).
why is there no exception?
AFAIK, that specific exception is only thrown on setters or other things that modify the contents of the widget. This does not mean that any code that does not raise that exception is guaranteed to be safe.
You can't redraw (invalidate) your Views outside main thread which is also UI thread. Setting text for TextView also causes redrawing view ofc... But getting text just returns String (or Editable?), so its working...
Note that there are some other restrictions and limitations, e.g. when you pass EditText to some AsyncTask, but while it's working the main Activity (holding passed view) finish then you might get NPE, because view is also gone... In this and similar cases WeakReferences are very usefull

Having a TextView show text set by another Thread android

I have some worker thread that gets an address using the geocoder, and when its done, i want to show the result on the application thread's TextView, using setText() from the worker thread leads to an exception, so what is the best practice to do that?
Android UI Views must not be touched from external threads, Any code that calls Methods on Views must run in UI thread only. You Should use AsyncTask . It provides useful callback methods to update UI from task running in a separate thread.
It's hard to say without seeing your code or knowing what exception:
but check if:
your textView object is linked with the one in your layout (findViewById(R.id.textView1))
is it visible in you layout?(sometimes when dimensions of elements dont't add up, views get pushed off screen)
Is it really a String you're trying to set? (not an int or so?)
Try this inside your thread where you want to set text:
// you should finalize your text before using it in another thread,
// else the IDE would show an error
final String text = yourTextToBeSet;
runOnUiThread(new Runnable() {
#Override
public void run() {
textView.setText(text);
}
});
The exception you get when you call setText() on non-UI thread is because you can't call anything related to UI when you are on a non-UI thread. You can, as above, easily call runOnUiThread(Runnable) to divert code execution on UI thread.
Keep in mind that this solution is not good enough if your code is not as simple as that, which in such situations, using AsyncTask is recommended.

Implementing a cyclic executive in android?

I am writing an android app and I need to be able to do certain things periodically/continuously. I am coming from a C/C++ embedded firmware background and this new-fangled way of doing things is going to take some getting used to. It seems that there is no such thing as a "main loop" in Android, that everything is event-driven... I also understand that by default all code you write operates on the GUI thread, and I should probably make a new thread to execute the equivalent of a "main loop"...
So far what I have is an implementation of the AsyncTask class who's "doInBackground" method contains an infinite loop (my main loop), I create an instance of this class and run it immediately when my app starts. The problem I am having is in the interaction between this thread and the user interface... when something occurs in my main loop thread and I want to update the GUI understand that I must call "publishProgress", which is executed on the GUI thread. There are a few problems with this, primarily that many things I have tried to do in this "onProgressUpdate" method do not work, or do not occur in a predictable amount of time.
My question, is there a better way to accomplish what I am trying to do? In general, what do most people do when they have code that they want to run periodically and/or continuously while their application is running, code that must interact with the user interface in a timely manner (by timely I mean with zero delay).
Thank you.
public class MainLoopThread extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... arg0)
{
while(true)
{
//Do stuff
//Update GUI
publishProgress();
}
}
protected void onProgressUpdate(Void...voids)
{
//Update GUI
}
}
It is unclear what you are trying to do, however just let me say using AsyncTask in this way may have negative consequences.
AsyncTask internally uses a thread pool pattern for running the stuff from doInBackground(). On Android OS before 1.6 and starting from 3.0 the pool size is just 1, meaning no parallel computations for a bunch of AsyncTasks. More details on this here.
So, this may result that only this current AsyncTask is running, while others even if started will have to wait untill the current one is done.
Depending on your needs for things to be done periodically Android exposes:
AlarmManager
Handler - it allows to post a runnable on UI thread with a delay or periodically
Timer + Activity.runOnUiThread(Runnable action) inside of TimerTask
UPDATE: basing on your comments it looks like you need a Service, that starts a thread that periodically sends broadcasts with the data for UI. Then your UI (Activity) registers broadcast receivers to catch those broadcasts, extract the data and use for UI updates.
So your saying that onProgessUpdate() isn't working? That seems weird because it should.
Another option that you have is just to make a Thread that loops.
The trick is that if you want to update the UI thread you will have to make a call to view.post() and give it a runnable that will actually perform the update. The idea here is that you must schedule an update on the UI thread, you can't just take it and say NOW!

Handler class and the timing of when its message queue is emptied

I was curious about the nature of the handleMessage() and sendMessage() behavior of the Handler class. I want to be able to send message to another thread in such a way that the destination thread can process the message queue when it wants to. It seems, however, that the message is processed by handleMessage() practically as soon as it's sent.
I'm trying to design a game loop thread that does something like this:
void run(){
while (gameIsActive){
handleInput();
updateGameState();
}
}
handleInput(){
//Remove an item from the handler's message queue
//which can be UI events (click, touch, etc.)
}
However, I as soon as sendMessage() is called (from the parent/calling thread), the Handler.handleMessage() is processed (in the child/receiving thread), even if the child/receiving thread is blocking in a while loop.
I've seen this problem solved in other games by using a thread-safe list (ConcurrentLinkedQueue). The UI thread just posts events to this queue, and the game loop can remove the events as it seems fit. I just assumed the Handler class was designed for this purpose. It seems it's more intended for asynchronous callbacks to the parent thread.
Well, I can't find a good answer about this timing (and it would be useful to know in other instances), but, as a workaround I just used a public List that was thread safe, and my UI thread can access this public list and add information to it. In particular I used a ConcurrentLinkedQueue. I found this in the official JetBoy game sample provided by Google, so maybe they don't even encourage using the Handler in this situation :)

Categories

Resources