When my app launches a file scraper that iterates recursively through the sdcard is kicked off. As you can imagine, this takes a fair amount of time. On my epad transformer it can take 30 to 40 seconds to blitz through the 32gb of storage.
This activity is done in two fragments that live on the main layout, launched from the main activity.
Obviously with load times like that I need to do something to alert the user. However, from what I've read I can't launch the main activity in the background behind a load screen. I've tried to drop a progress bar on and also failed.
So, any suggestions?
EDIT
I'm just reading up on using an aSyncTask within a fragment. I would love to use a progressbar, either within the slow list fragment, or on a splashscreen, or just over the main view
Do something like this
LoadTask load;
public class LoadTask extends AsyncTask<Void, Integer, Boolean> {
int prog = 0;
#Override
protected Boolean doInBackground(Void... params) {
//All your loading code goes in here
publishProgress(prog++); //Use this to update the progress bar
publishProgress(-1) //Use this to open other app
return false;
}
protected void onProgressUpdate(Integer... progress) {
if(progress[i] == -1)
//open other app here
for(int i = 0; i < progress; i++){
//set progress here with progress[i];
}
}
}
then to start the async task use
load = new LoadTask();
load.execute();
Related
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
}
Iv'e got an Android app that is using a list activity to display a list of items pulled from the internet. I First use an AsyncTask to load the list and that Async task finishes it calls a different async task to start loading the thumbnail pictures that go along with the list items. The problem I am having is that the user has access to a refresh button that they can press at any time and when it is pressed, the whole list of items is delete and the loading starts over. The Async task that loads the thumbnails could potentially still be running if this happens and may try to add a thumbnail to a non existing list item then. Iv'e tried synchronizing on the list, using a Boolean which after researching I realized would not work. I have also tried using a static atomic boolean to check if refresh has been hit to cancel the thumbnail loader. Any ideas?
public class LoadItems extends AsyncTask<Void, Void, Boolean> {
private Activity activity;
private static boolean loading = false;
public static final AtomicBoolean refreshing = new AtomicBoolean(false);
private static final String TAG = "LoadItems";
private int start;
private List<ListItem> items;
public LoadItems(Activity activity) {
this.activity = activity;
}
#Override
protected void onPreExecute() {
loading = true;
start = ItemViewer.itemList.size();
}
#Override
protected Boolean doInBackground(Void... arg0) {
items = WebFunctions.getMoreItems(activity);
return (items != null);
}
protected void onPostExecute(Boolean success) {
if (success) {
for (ListItem item: items) {
ItemViewer.itemList.add(item);
Log.d(TAG, "added item " + item.getTitle());
}
LoadThumbnails thumbnailLoader = new LoadThumbnails();
thumbnailLoader.execute(start, ItemViewer.itemList.size());
}
loading = false;
}
public void protectedExecute() {
if (!loading)
execute();
}
public void refresh() {
if (!refreshing.getAndSet(true)) {
WebFunctions.reset();
ItemViewer.itemList.removeAllItems();
execute();
}
}
}
public class LoadThumbnails extends AsyncTask<Integer, Void, Drawable> {
private int position;
private int end;
#Override
protected Drawable doInBackground(Integer... params) {
position = params[0];
end = params[1];
Drawable thumbnail = null;
synchronized(ItemViewer.itemList) {
if (LoadItems.refreshing.get())
cancel(true);
String url = ItemViewer.itemList.get(position).getThumbnailUrl();
if (!url.isEmpty())
thumbnail = WebFunctions.loadDrawableFromUrl(ItemViewer.activity, url);
}
return thumbnail;
}
protected void onPostExecute(Drawable d) {
synchronized (ItemViewer.itemList) {
if (LoadItems.refreshing.get())
cancel(true);
if (d != null)
ItemViewer.itemList.setThumbnail(position, d);
position++;
if (position < end) {
LoadThumbnails lt = new LoadThumbnails();
lt.execute(position, end);
}
}
}
}
This is pretty simple to solve. Whenever the user hits the refresh button, make sure you call cancel() on the last async tasks you have created before you create new tasks. For example,
private void onRefreshClick(View v) {
if(mLastLoadItemTask != null) mLastLoadItemTask.cancel(true);
if(mLastLoadThumbnailTask != null) mLastLoadThumbnailTask.cancel(true);
mLastLoadItemTask = new LoadItems(...);
mLastLoadItemTask.execute();
}
Then, in the onPostExecute of each of your async tasks, first check to see if they were cancelled by calling isCancelled(). If they were cancelled, make sure the onPostExecute method does no work by just returning. For example,
protected void onPostExecute(...) {
if(isCancelled()) return;
//Adding items to list
//Or start load thumbnail task
}
As you can see that should prevent any unintentional or stale updates because the onPostExecute methods and your cancel calls will all happen on the main therad. The last thing I would suggest is to alter your loadThumbs task to be able to stop doing work as soon as possibly by checking isCancelled() whenever it makes sense to do so.
The following steps might help:
cache the results, whatever you have previously pulled from the net should be saved and quickly restored back when your application is launched. this way you avoid long delays and empty screens on application startup, which, in turn, stops the user from pressing 'reload'
make a boolean variable reload_in_progress, set it to true when you start pulling data from the net, and set it to false when all thumbnails are ready. 'reload' click handler should ignore clicks when reload_in_progress is true.
show some king of progress bar to the user, so (s)he knows it's already reloading and does not push reload again.
almost forgot, never update data shown to the user "live", this leads to wonderful situations, when the user clicks on item while it's changing and doing something completely different from what (s)he expected. long updates should keep its data to themselves and quickly swap old data for the new one only when everything is ready.
I have four Tab at the top of my apps
The content of the fourth tab is that it will get data from sql server and then display in listview
since the amount of data retrieved is quite big, it takes 2-3 sec
The problem is that:
After I click the fourth tab, it has no response, then after 2-3sec, it displays the content
As I know it is loading the data from database, I will not continue to click
However, when users click it and no response, he may click and click and click
How to show something to user so that they know it is loading data??
You should use a CrusorLoader. This will display a loading circle while still making the UI active. Note that even if you're using lower versions of the android API, you can still access the CursorLoader class via the Android Support Package. For more information on loaders, checkout
new SomeTask(0).execute(); // write this line in your 4th tab onCreate()
/** Inner class for implementing progress bar before fetching data **/
private class SomeTask extends AsyncTask<Void, Void, Integer>
{
private ProgressDialog Dialog = new ProgressDialog(yourActivityClass.this);
#Override
protected void onPreExecute()
{
Dialog.setMessage("loading...");
Dialog.show();
}
#Override
protected Integer doInBackground(Void... params)
{
//Task for doing something
// get data from sql server and then display in listview
return 0;
}
#Override
protected void onPostExecute(Integer result)
{
if(result==0)
{
//do some thing if your list completed
}
// after completed finished the progressbar
Dialog.dismiss();
}
}
When a long-running process is started, you'll want to indicate that something is happening so the user knows to wait. You want a progress dialog.
Here is an example:
http://www.androidpeople.com/android-progress-dialog-example
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.
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