I have a fragment that takes too long to load due to its OnCreateView() function configuring about 40 textboxes. Is there a way to put this in a background thread and display a progress indicator circle while it loads?
I have tried using AsyncTask, but when the OnCreateView() is called a second time, I get an error stating that I must use the original thread. Also the device is frozen and will not display any indicator while loading through AsyncTask.
You can't do the operation about the UI in the other thread. The official document said:
By design, Android View objects are not thread-safe. An app is expected to create, use, and destroy UI objects, all on the main thread. If you try to modify or even reference a UI object in a thread other than the main thread, the result can be exceptions, silent failures, crashes, and other undefined misbehavior.
So you should try to optimize the code about the fragment's OnCreateView() method instead of trying run it in the background thread. All the UI operation should be excuted in the main thread.
For more information, you can check the official document about the etter performance through threading.
Related
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.
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
I was developing an Android Application where I have to load a large data from Android Database and view it on screen.
In earlier stages of development I used to retrieve data from DB in OnCreate() method and it really slowed the start of activity when data became huge. So I called the retrieving of data and viewing it on screen in OnResume() method, so that my app does not crash while taking too long to load it.
I know activity is not shown until OnResume is completed.
So is it a good approach to call time taking operations in OnResume instead of having all initialization in OnCreate() method?
So is it a good approach to call time taking operations in OnResume instead of having all initialization in OnCreate() method?
no - you should not do it inside OnCreate or OnResume. You should always do it inside working thread - like AsyncTask.
Any opertion that takes to long to finish, if executed on UI thread will block message queue - this means there are no repaint of UI widgets, user cannot tap on your app to do anything. Everything is blocked until your operation finished. After ~5s Android generates ANR (application is not responding) and kills your app.
You should never do IO operations on the main thread. This includes onCreate and onResume methods, which will always be called on the main thread.
To correctly load data you should either use an AsyncTask or the ContentLoader with its callbacks.
Generally speaking, to use a database with your application you should use a ContentProvider and CursorLoaders. There are plenty of tutorials on how to use these and it makes your code better manageable.
what is meant by asynchronously loading data in activity or fragment in android?
This is my question. I searched everywhere. I'm not getting a generalized definition for this?. I can't get the term mentioned in android developer also.
Can anyone provide me the basic explanation of this term?
Asynchronous in Android mean that you do stuff while the user can interact with the User Interface (UI) : you are not blocking the UI while you are doing long stuff. So the user can still navigate, change activities or fragment and your data is still loading.
For data : you load it, parse it and do whatever you want in a NON-UI Thread (using AsyncTask eg) and then notify the UI, and display what you need to.
You have many possibilities to implement Asynchronous load in Android, and you have many different way to manage your request. I personnaly recommend using Retrofit if you need to use a Web API.
It means that you load your data in a separate thread than the UI thread. You launch your HTTP request for example in another thread and when it finished you notify the UI thread to refresh display.
This mean to load data in separate thread rather than load the data in main thread.Loading data in main thread may cause app to block
The AsyncTask class encapsulates the creation of a background process and the synchronization with the main thread. It also supports reporting progress of the running tasks.
To use AsyncTask you must subclass it. AsyncTask uses generics and varargs. The parameters are the following AsyncTask .
An AsyncTask is started via the execute() method.
The execute() method calls the doInBackground() and the onPostExecute() method.
TypeOfVarArgParams is passed into the doInBackground() method as input, ProgressValue is used for progress information and ResultValue must be returned from doInBackground() method and is passed to onPostExecute() as a parameter.
The doInBackground() method contains the coding instruction which should be performed in a background thread. This method runs automatically in a separate Thread.
The onPostExecute() method synchronizes itself again with the user interface thread and allows it to be updated. This method is called by the framework once the doInBackground() method finishes.
I Have several activities handling diferent screens with information that is loaded asynchrounously via AsyncTask.
All data fetching are done in doInBackground()
All UI operations are done in onPostExecute()
In some instances I get the error: "Can't create handler inside thread that has not called Looper.prepare()"
If I do the Looper.prepare(), it crashes if a new activity is instanced for this class.
It's a weird behaviour that I'm able to find why it happens. The other screens with similar behaviour work as expected. There are no differences between them that I can find.
Has anyone had this behaviour?
"Can't create handler inside thread that has not called Looper.prepare()" means that you are trying to call AsyncTask.execute() outside of the UI thread.
There are few rules you should follow using AsyncTask:
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
Also it can mean than you are creating Handler object inside doInBackground method.