Android splashscreen without creating a new activity - android

Edit
After moving my loading / creation code to an Async Task (see below) - I still have the initial problems that I had with my original splashscreen.
Those being that:
1) On starting the Async task in onCreate, everything is loaded but my Dialog can only be shown when onStart() is called which makes the whole process kind of pointless as there is a long pause with a blank screen, then after everything has loaded, the 'loading' dialog flashes up for a split second before disappearing.
2) I can't move object loading / creation etc to onStart because a) it will be run again even when the app is resumed after being sent to the background which I don't want to happen, and b) when when calling restoring the savedInstanceState in onCreate() I'll get a nullPointerException because i'm restoring properties of objects that won't have yet been created.
Would really appreciate if someone could advise how to get around these problems and create a simple splashscreen. Thanks!
Background
My app uses only one activity and I would like to keep it that way if possible.
I've struggled with this for over a week so really hope someone can help me out.
All I want to do is use a splashscreen with a simple 'loading' message displayed on the screen while my resources load (and objects are created etc.) There are a couple of points:
Conditions
1) The splashscreen should not have it's own activity - everything needs to be contained in a single-activity
2) The splashscreen should not use an XML layout (I have created a Splashscreen class which uses View to display a loading PNG)
3) My app is openGL ES 2.0 so the textures need to be loaded on the OpenGL Thread (creation of objects etc that don't use GL calls are OK to go on another thread if necessary).
What I've attempted so far
What I did so far was to create a dialog and display it in my onStart() method with:
Dialog.show();
then let everything load in my onSurfaceCreated method before getting rid of it with:
Dialog.dismiss();
However I needed to change this for varioius reasons so now I create my objects from a call within my onCreate() method and just let the textures load in my GL Renderer's onSurfaceCreated method.
However, this means that because the dialogue isn't displayed until after onCreate, I still get a delay (blank screen) while everything is created before the splash-screen is shown, this then stays on the screen until the textures have loaded. There are other issues with this too which can wait for another day!
So my approach is obviouly very wrong. I read every tutorial I could and every splash-screen related question I could find on SO and Gamedev.SE but I still can't find an explanation (that makes sense to me), of how this can be achieved.
I'm hope someone here can explain.

You should be able to use AsyncTask to load resources in the background and then just dismiss your splash
Here's an AsyncTask that I use to load data from a remote db. This displays a loading progress circle until the task is complete but should be easily re-purposed to display your splash
AsyncTask that runs in the background
private class SyncList extends AsyncTask<Void, ULjException, Void> {
private static final String TAG = "SyncList";
private final class ViewHolder {
LinearLayout progress;
LinearLayout list;
}
private ViewHolder m;
/**
* Setup everything
*/
protected void onPreExecute() {
Log.d(TAG, "Preparing ASyncTask");
m = new ViewHolder();
m.progress = (LinearLayout) findViewById(R.id.linlaHeaderProgress);
m.list = (LinearLayout) findViewById(R.id.listContainer);
m.list.setVisibility(View.INVISIBLE); //Set the ListView that contains data invisible
m.progress.setVisibility(View.VISIBLE); //Set the loading circle visible you can sub in Dialog.show() here
}
/**
* Async execution performs the loading
*/
#Override
protected Void doInBackground(Void... arg0) {
try {
Log.d(TAG, "Syncing list in background");
dba.open(ListActivity.this);
dba.sync();
} catch (ULjException e) {
publishProgress(e);
}
return null;
}
/**
* Display exception toast on the UI thread
*/
protected void onProgressUpdate(ULjException... values) {
Log.e(TAG, values[0].getMessage());
Toast.makeText(ListActivity.this, "Sync failed", Toast.LENGTH_LONG).show();
}
/**
* Finish up
*/
protected void onPostExecute(Void result) {
Log.d(TAG, "ASyncTask completed, cleaning up and posting data");
fillData();
m.list.setVisibility(View.VISIBLE); //Show the list with data in it
m.progress.setVisibility(View.INVISIBLE); //Hide the loading circle sub in Dialog.dismiss()
}
}
Calling the Task
protected void onStart() {
super.onStart();
// Init the dba
dba = DBAccessor.getInstance();
new SyncList().execute();
}
It should be noted that the AsyncTask is an inner class of the Activity its related to here
Edit
onCreate Method
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
Dialog.show();
//This launches a new thread meaning execution will continue PAST this call
//to onStart and your loading will be done concurrently
//Make sure to not try to access anything that you're waiting to be loaded in onStart or onResume let your game start from onPostExectue
new AsyncTask.execute();
}
doInBackground
protected Void doInBackground(Void... arg0) {
Load all resources here
}
onPostExecute
protected void onPostExecute(Void result) {
Dialog.dismiss();
Call a method that starts your game logic using your newly loaded resources
}

Related

Understanding the UI thread

I am a beginner to Android and I have some confusions regarding Android UI Thread. Now, I know that no thread apart from the one that created the UI can modify it.
Great.
Here is the Activity from my first Android app which slightly confuses me.
public class NasaDailyImage extends Activity{
public ProgressDialog modalDialog = null;
//------------------------------------------------------------------------------
#Override
protected void onCreate(Bundle savedInstanceState){
//Instantiate progress dialog, skipping details.
Button b = //get reference to button
b.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
modalDialog.show(); // show modal
Toast.makeText(getApplicationContext(), "Getting feeds", 500).show();
new AsyncRetriever().execute(new IotdHandler()); // Get the feeds !!
}
});
}
//------------------------------------------------------------------------------
public synchronized void resetDisplay(boolean parseErrorOccured,
boolean imageErrorOccured,
IotdHandler newFeeds){
if(parseErrorOccured || imageErrorOccured){
// make a Toast
// do not update display
}else{
// make a Toast
// update display
// based on new feed
}
}
//------------------------------------------------------------------------------
class AsyncRetriever extends AsyncTask<IotdHandler,Void,IotdHandler>{
#Override
protected IotdHandler doInBackground(IotdHandler... arg0) {
IotdHandler handler = arg0[0];
handler.processFeed(); // get the RSS feed data !
return handler;
}
//------------------------------------------------------------------------------
#Override
protected void onPostExecute(IotdHandler fromInBackground){
resetDisplay( // call to update the display
fromInBackground.errorOccured,
fromInBackground.imageError,
fromInBackground);
}
//------------------------------------------------------------------------------
}
1. onCreate is on the UI thread so I can do whatever I want but onClick is not. Why can I make a ProgressDialog and a Toast in that method? Why no error there?
2. The AsyncTask is subclass of the the NasaDailyImage. This means it can access all the methods of NasaDailyImage including resetDisplay() which updates the display. resetDisplay() is called in the onPostExecute which runs on a different thread from UI. So, why can I update the display there and yet get no errors ?
onClick() is indeed on the UI thread. Most of what happens in an Activity happens on the UI thread.
onPostExecte() (and its counterpart onPreExecute()) runs on the UI thread as well. The AsyncTask.onPostExecte() documentation clearly states this. AsyncTask was deliberately designed such that developers could update the UI before and after they do background work.
In general, your code will be running on the UI thread unless you explicitly tell it otherwise. Once you create AsyncTasks, Runnables, or Threads, you need to ensure you understand where your code is executing. In an Activity, it is typically safe to assume you are on the UI thread.
You are extending AsyncTask class , where async task class is calling its sequential method automatically. First onPreExecute then doBackground and finally onPost. If you want to change any ui change you can use onProgressUpdate method.
To use your activity class simple call activityclass.this.resetDisplay(). Because inner class scope sometimes failed to integrate except global varible.
Thanks

Android unloads data file during inactivity

I have a quiz app that loads a question database from a plist file. When the app is freshly loaded it runs a thread to load the question file.
After some inactivity, when you open the app again and try to run a new quiz, it loads no questions.
I imagine its some sort of garbage collector that the OS uses to clear up some memory, it's doing its job so thats ok.
But what would you recommended for reload the file? I'm thinking overriding savedInstanceState?
This is a splash screen and loader combination that is the first activity to run
public class SplashScreen extends Activity {
/** Called when the activity is first created. **/
ProgressDialog dialog;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new LoadViewTask().execute();
}
private class LoadViewTask extends AsyncTask<Void, Integer, Void>{
//Before running code in separate thread
#Override
protected void onPreExecute(){
dialog = new ProgressDialog(SplashScreen.this);
dialog.setMessage("Loading...");
dialog.setTitle("Loading File");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.show();
}
//The code to be executed in a background thread.
#Override
protected Void doInBackground(Void... params){
try{
//Get the current thread's token
synchronized (this){
QuestionsLoader.getInstance(SplashScreen.this);
}
}
catch (Exception e){
e.printStackTrace();
Log.d("TAG", "exception" + e);
}
return null;
}
//Update the progress
#Override
protected void onProgressUpdate(Integer... values){
}
//after executing the code in the thread
#Override
protected void onPostExecute(Void result){
dialog.dismiss();
Intent i = new Intent("com.project.AnActivity");
startActivity(i);
finish(); //Finish the splash screen
}
}
}
Is SplashScreen an Activity? If so, you should not attempt to retain a reference to it.
Android destroys activities to free memory when needed. It is indeterminate when this happens and you should not interfere with the mechanism. It doesn't use the garbage collector to do this (it destroys the Activity's process) so if you hold a reference to it, you will leak the entire activity which is not good. You see many apps on the Play Store with this particular bug, or a variant of it. Just rotate the device 5 or 10 times and the app will crash with out of memory.
Without seeing the code, it's impossible to know but usually the correct approach is to load external resources in onResume() or later such that they are available whether the Activity is a new instance (e.g. onCreate() has been called) or the same instance returning to the foreground.
If you need to persist some state regardless of whether the activity is destroyed or not, save it (usually to SharedPreferences) in onPause() and retrieve it in onResume().

Android : how to initialize an activity in background before displaying it

I have an activity in which I display an image that is stored on a website.
I am using the following code to get it from its url and display the activity :
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
setContentView(R.layout.ad_screen);
AsyncTask<String, Void, Integer> task = new AsyncTask<String, Void, Integer>()
{
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
#Override
protected Integer doInBackground(String... urls)
{
fetchAd();
return 0;
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
#Override
protected void onPostExecute(Integer result)
{
displayAd();
}
};
task.execute("");
}
That works very fine, but the behaviour is not the one I want : in this case, the activity is pushed on the screen (with a right to left animation) and then the AsyncTask begins. So the image is displayed on screen after a delay (which is normal).
But I would like to perform the request before the activity is pushed, so that the screen is displayed directly with its image without any delay.
Is there a way to have this behaviour ?
Thanks in advance.
You can download the image in the previous activity, create a bitmap, then pass it as an extra in the intent that launches this activity.
Use AsyncTask to load images in previous activity, and display the images in the current activity.
You could in the "parent" activity, the one that starts the new activity, do the fetching of the content, store it either in memory or some sort of fast access persistence, and after that start the activity.
Same principle could be to introduce a "loader" activity which would show a loader, and prefetch the data, when all data received it would start the activity that should display the data.

onPostExecute() gets called but it wont dismiss my dialog

I have a class which extends AsyncTask, which is intended to serve as a generic task manager class for my application.
Problem:The strange behavior is that the progress dialog shows up, but is never dismissed.
I am sure that onPostExecute() gets called for every task instance, as any Log.d("","") statements fire if placed in here, even the Toast messages show up from within this method, but I am not able to dismiss the static dialog.
I understand that AsyncTask(s) have access to UI thread at only 2 places [onPreExecute() and onPostExecute()], so I think trying to dismiss the dialog in runOnUiThread() is unnecessary.
All calls to executeTask() are made from different onCreate() methods of different activities that need to fetch some data over network before populating some of their UI elements, and I always pass the current activity's context to the tasks.
As I do not switch activities until after the related tasks are completed, I believe the activity context objects are still valid (am I wrong to have assumed this???) I have never found any of them to be null while debugging.
Could this be a timing issue? I have also observed that most of the times DDMS shows all tasks get completed before the activity is displayed. If I use new Handler().postDelayed(runnable_which_calls_these_tasks,10); in the onCreate(), and add delaying code in foo_X(), the activities are displayed without any delay, but the dialog will just not dismiss().
I have read through quite a number of articles on this issue but am still not able to figure out exactly where am I going wrong. I do not want to define each task as private inner class Task1 extends AsyncTask<> in all of my activity classes and I would not want to (unless this is the only solution) load my application object with all activity references either as mentioned in this discussion: Is AsyncTask really conceptually flawed or am I just missing something?.
I have spent a week on this and am absolutely clueless :( It would be great if someone can guide me, and let me know what am I missing.
Following is the class definition: [I've removed some irrelevant application specific code for clarity]
public class NetworkTask extends AsyncTask<Void, Integer, Boolean> {
private Context UIcontext;
private int operationType;
private static ProgressDialog dialog;
private static int taskCount;
private NetworkTask(int operationType Context context){
this.UIcontext = context;
this.operationType = operationType;
if (taskCount++ == 0)
dialog = ProgressDialog.show(context,"","Loading...");
}
public static Boolean executeTask(int operationType, Context context) {
return new NetworkTask(operationType, context).execute().get();
}
#Override
protected void onPreExecute(){
super.onPreExecute();
if (taskCount == 1)
dialog.show();
}
#Override
protected Boolean doInBackground(Void... arg0) {
switch(operationType){
case TYPE_1:
foo1();
break;
case TYPE_2:
foo2();
break;
case TYPE_3:
foo3();
break;
case TYPE_4:
foo4();
break;
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
taskCount--;
if (dialog.isShowing() && taskCount == 0){
dialog.dismiss();
}else {
Toast.makeText(UIcontext, "Task#"+ operationType+", m done, but there are "+taskCount+" more", 5).show();
}
}
}
Edited:
Mystery solved - this one was giving me a bit of trouble. There were a few problems:
The main problem why dialog was not showing was NetworkTask.get(). This is a blocking call that waits for the whole NetworkTask to finish. During that time it blocks UI thread so nothing is drawn (also other UI elements are unresponsive). Remove get():
public static Boolean executeTask(int operationType, Context context){
new NetworkTask(operationType, context).execute();
return true; // return whatever
}
The show() on ProgressDialog is called twice. This shows two dialogs, one after another. Remove show() inside onPreExecute().
ProgressDialog is modal - it prevents changing UI until it is done. Toast.makeText() are called before dialog.dismiss() but since dialog is blocking drawing to screen, they get queued and are shown after dialog is dismissed.
super.onPostExecute(result) and super.onPreExecute() are redundant so can be removed.
I can post the whole working code if you have trouble with it.

Display the rotating progress before display the data

I'm displaying some data by using SQLite. When I click on one button data come from database. It takes some time. At that time the screen is black. At that time I want to display the rotating spinner before the data dispay. Any ideas?
Android provides a ProgressDialog for accomplishing what you want.
First i would like to suggest to have a look at AsyncTask page, so that you will come to know about the AsyncTask exactly.
Now, Implement AsyncTask as given below:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new performBackgroundTask().execute();
}
private class performBackgroundTask extends AsyncTask <Void, Void, Void>
{
private ProgressDialog Dialog = new ProgressDialog(main.this);
protected void onPreExecute()
{
Dialog.setMessage(getString("Please wait..."));
Dialog.show();
}
protected void onPostExecute(Void unused)
{
Dialog.dismiss();
// displaying all the fetched data
}
#Override
protected Void doInBackground(Void... params)
{
// implement long-running task here i.e. select query/fetch data from table
// fetch data from SQLite table/database
return null;
}
}
Enjoy !!!
You should not execute long running tasks in UI thread as this blocks the UI redraw and makes app look unresponsive.
Use AsyncTask to execute long running tasks in background, while still updating the screen.
You can look at the standard music picker as one example of how to do this:
https://android.googlesource.com/platform/packages/apps/Music/+/master/src/com/android/music/MusicPicker.java
In addition to the whole "queries must be done off the main UI thread," this shows an indeterminant progress while first loading its data, fading to the list once the data is available. The function to start the query is here:
https://android.googlesource.com/platform/packages/apps/Music/+/master/src/com/android/music/MusicPicker.java#581
And to do the switch is here:
https://android.googlesource.com/platform/packages/apps/Music/+/master/src/com/android/music/MusicPicker.java#569
The layout has the list view put in a frame layout with another container holding the progress indicator and label. The visibility of these is changed to control whether the list or progress indicator are shown:
https://android.googlesource.com/platform/packages/apps/Music/+/master/res/layout/music_picker.xml

Categories

Resources