I've read a few posts on the topic, . Here is my strategy; I have a drawing app that shows the user-created drawings in a ListView. Once the user selects a drawing, the DrawingEdit Activity is fired up, with the drawing being loaded in onCreate, roughly as follows:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the layout for the activity
setContentView(R.layout.drawing_view);
// do potentially expensive loading of drawing here
loadDrawing();
}
Where loadDrawing() is loading the individual shapes of the user's drawing from a database. Now, for 95% of the drawings, and for the 'typical' use case, this works fine, as the Activity loads quickly with little to no delay. However, for very very complex drawings, the delay can be up to two seconds. To help resolve this issue, I've tried an AsyncTask, which works great for the 5% of drawings that are excessively large, but for the other 95% seems like overkill, and actually makes the loading a little slower due to having to fire up the AsyncTask. It's not the worst solution, but I'm looking at alternatives.
Now, in addition to the drawing data that I have stored in the database, I also have a static PNG version of the drawing that I can make use of... so the solution that I'd like to use is along the lines of:
Load a temporary 'splash' screen view at the beginning of onCreate(), using the static drawing as a placeholder that the user can immediately review while the drawing is loading.
Start the sometimes expensive loadDrawing() routine.
Upon completion switch the View and show the secondary view with the fully interactive drawing canvas that includes the shapes from loadDrawing().
My problem is that if I model the above solution roughly as below, the 'splash' layout is never displayed, and there is still the same delay until loadDrawing() is complete and then the final layout is displayed. Any info on what is happening here? Should I move the loadDrawing() to onResume so that the initial splash UI has a chance to load before loadDrawing() is triggered?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// set the layout for the activity
setContentView(R.layout.splash_view);
// do potentially expensive loading of drawing here
if (loadDrawing()) {
// set the layout for the activity
setContentView(R.layout.drawing_view);
}
}
If loadDrawing is a method within your Activity, it will block the UI thread until it is complete. In short, the UI isn't displayed until after the startup steps (create/start/resume) have finished. It wouldn't matter if you put loadDrawing in onResume as it will still block the UI creation.
You need to use some sort of background asynchronous thread to get this to work - this is why AsyncTask is useful to allow the UI to be drawn/manipulated while something else needs to happen at the same time.
Related
I have the following question/problem:
according to my knowledge with setContentView(...) we bring a layout into view.
So far so good but at which point will it be shown on the screen?
//quote
public class MainActivity extends Activity {
GlobalDataBase g_db;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_user_layout);
//here will come the rest of the declaration and
//a database initialization which is a little time consuming
//example
g_db = (GlobalDataBase) getApplicationContext();
g_db.initialize();
}
// other subroutines and so on
} //point where the layout will be shown on the device
//endquote
Only after the last } ,which defines the end of the activity, the layout will be shown on the display.
Now my question is, if it is possible to force the layout be shown on the display directly after the setContentView(...)
In my program the loading of the database will take a lot of time depending on the internet speed. Sometimes it takes around 20 seconds where the screen is blank.
I want to show a message like "Database loading..." on the screen before I inizialize the database. But that doesnt work because the message is shown only at the end of the } which defines the end of the activity.
Any suggetions?
Thanks for your support
This is happening because you are using DB on main thread. If you do DB initialization on background thread then it will solve the problem.
You can use Running Android tasks in background threads to implement this DB initalization on background and communicate to main thread when query is complete with result if needed.
I have been trying to find guidance on how to make a loading splash screen in Android, and have found tutorials such like A Simple Android Splash Screen. Because this tutorials glosses over certain things, one thing that's a bit unclear to me is how do you programmatically determine whether the app is trying to load (before the onCreate() method for your activity executes and your activity loads)?
You might be interested in the Instrumentation Class. It is used to monitor such things, and should provide you with whatever you want know. Also, putting the Dalvik messages to Verbose in LogCat will give you an idea of everything the system is doing prior to calling your Activity's onCreate() method.
The onCreate() method of android is called when its time for it to render the UI. The condition that you are specifying by
one thing that's a bit unclear to me is how do you programmatically
determine whether the app is trying to load (before the onCreate
method for your activity executes and your activity loads)?
would be using someting in manifest like splashScreen="#drawable/Splash" but this unfortunately doesnt exist
So the solution would be like this
Calling a temporary XML file before loading your actual content
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
// Do your loading code here
// Create an AsyncTask if the task is time consuming
//Now Load your actual UI
setContentView(R.layout.activity_main);
}
you said
The issue is for my app while onCreate() is executing there is a black
screen for some time
The short black screen is for very few seconds and there is no way around for it.
Given a one-activity app like this:
public class MyActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
loadingScreen = new RelativeLayout(this);
// add a bitmap to loadingScreen
setContentView(loadingScreen);
Dict dict = new Dict();
dict.init();
// this method takes ~10 seconds
}
}
In theory, it looks like this would display a loading screen while the dictionary initializes. In practice, nothing is displayed until onCreate() returns, so the user sees a blank white screen for 10 seconds. What's the best way to display a please wait while this app loads screen?
I believe I could create a separate thread for dict.init(), but that seems like overkill in this case because I don't want the app to be usable or interactive while dict.init() runs. I'd like it to run on the main thread (and hang the rest of the app while it executes), I just want to display something on the screen first.
PS, I tried moving dict.init() to onStart(), that appeared to have no effect.
edit: Perhaps I should have clarified this to avoid getting "You're doing it wrong" type answers, but the init takes 2 or 3 seconds on modern phones and tables, 10 seconds is a worst-case on old phones. This app is a word game, it can't be used without the dictionary. Moving dict.init() to an async task will not improve the user's experience, and the question I asked is whether it's possible to display a splash screen without doing that. I gather that the answer is "No."
I'd like it to run on the main thread (and hang the rest of the app while it executes)
No, no wouldn't and your users wouldn't like you much either
I believe I could create a separate thread for dict.init(), but that seems like overkill in this case because I don't want the app to be usable or interactive while dict.init() runs.
This is exactly what you should do. You can use a variety of ways including an AsyncTask
and show a ProgressDialog while the work is being done.
so the user sees a blank white screen for 10 seconds.
If it is taking this long then you might want to rethink your flow. Even if you go with a separate Thread and show a ProgressBar, most users aren't going to want to stare at that for 10 seconds. You should load the data in the background and allow them to do something else while it loads, if possible. You could use something like an IntentService depending on how you are getting the data.
Example of AsyncTask
Painless Threading
Is it possible to load a new activity in the background before switching the view to that activity?
For example, I would like to have a slash screen activity that gets called and displays a splash screen. While this splash screen is displayed, the next activity is loaded, and when it is done loading (when it's onCreate() is finished) then the splash screen activity ends, and the new activity is displayed.
I know another option would be to display the splash screen in the new activity, and use async task to load all the data before removing the splash image... but I am stuck on that approach as well. The activity first has to load a fair amount of data, and then it has to dynamically add GUI elements based on that data. Once the GUI is fully loaded, I then want to remove the splash screen. The problem is that I cannot touch the UI thread from doInBackground(). How do I create my activity behind a splash screen, if I cannot update the UI from doInBackground? I know that onProgressUpdate() can access the UI thread, but I can't figure out how to implement it.
Any ideas? Thank you!
Since you don't have an example of your code, I am not sure what kind of data you are loading and how you are dynamically configuring the UI based on the data, but I'll try to answer as much as I can. As a result, the answer may sound a little generic.
First, define 2 layout xml files - one for the splash screen and one for your "main" activity.
So you'll end up with /res/layout/splash_screen.xml and /res/layout/main.xml
In your onCreate(), load the splash_screen layout:
setContentView(R.layout.splash_screen);
In your async task, you will load up whatever data you need to do, and you will save all that data in some sort of data structure. I'm gonna use a LinkedList of String for example's sake.
private class MyTask extends AsyncTask<Uri, Integer, List<String>> {
#Override
protected List<String> doInBackground(Uri... params) {
List<String> myList = new LinkedList<String>();
// load up the list with data you are trying to get
myList.add("foo");
myList.add("bar");
// whatever you return here will be passed in as a parameter to the onPostExecute()
return myList;
}
#Override
protected void onPostExecute(List<String> result) {
setContentView(R.layout.main2);
// set layout elements with data that from the result
TextView myTextView = (TextView) findViewById(R.id.some_label);
myTextView.setText(result.get(0));
// or just call some function you defined in your activity instead
}
}
So basically, have 2 different layout file and use the splash_screen layout, and use the async task the load the data and save it in some data structure you define, and use that data structure to load your UI elements in onPostExecute() after using setContentView() to change back to your main layout.
One special note:
With the above code, it will show the splash screen again and reload all the data again if you rotate the screen. If you want to avoid that, you can use the onSaveInstanceState() and save whatever data you want in the outBundle and read that data back in onCreate's savedInstanceState bundle and load the UI elements back up. This will require a separate thread (or you can just search about it) if you wanted to know more about handling rotation.
One of the solution to solve your problem I can think about is to use one activity for displaying the splash screen and your content. Since you can call setContentView() method at any time (not only in onCreate() method) just define all the views you want in separate XML files and pass the relevant id to setContentView() when it's time to switch.
You could also use one layout with your views and splash screens and hide / unhide attributes. When your data is loading setVisibility to your splash screen to Visible while your root view remain unvisible. When finish loading - do in the opposite way.
I just tried a stupid approach and it crashed my app... Basically I have an activity that has three tabs (containing three activities). Each of the tabs gets its input from an xml file downloaded off the net. Everything is fine but when I start my app, it has download the xml file and there's a "wait" time for this.
I managed to get around this by adding a splash screen. It looks beautiful but the problem is when I click on the second tab, it still has to get the list off the net, so it looks ugly now... It waits before displaying the list. So what I did was design an AsyncTask that just downloads the xml file. In my main activity, I spawn two tasks initially and send the URL and Intent as parameters. And inside the acitivities that start inside the tabs, I use a wait(). Inside the AsyncTask, after it is done with the download, I notify the Intent using notify(). This crashed! Of course, I didn't expect it to work but just wanted to try :) Writing it so that I can either get a feedback as to why this failed or to prevent others from wasting their time on this...
Now, I am sure many face the problem of a "wait" time inside the tabs. How do I solve this? I am thinking of either dimming the screen and then displaying a series of toasts or display a progress indicator inside the tabs or pre-fetching the xml files... I don't have a clue on how these can be achieved... Any thoughts?
Credit: To Mark. Thanks!
Problem: Display a Progress Indicator when your application is busy doing some work
Approach:
public class Approach extends ListActivity {
ProgressDialog myProgressDialog = null;
ListView myList = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myList = getListView();
myProgressDialog = ProgressDialog.show(getParent(),
"Please wait...", "Doing extreme calculations...", true);
//Do some calculations
myProgressDialog.dismiss();
}
}
There are a few challenges (like updating some UI elements). You might want to spawn a different thread to do your calculations if needed.
Also, if you are interested in this, you might be interested in Matt's approach too: android-showing-indeterminate-progress-bar-in-tabhost-activity
ProgressDialog.
Or, make the tabs have android:visibility="gone" until such time as the data is ready, then make them visible. In the interim, show some sort of loading graphic (perhaps with a RotateAnimation applied).