As I've asken on another question HERE it seems that the PackageManager.getInstalledPackages() doesn't play nice with Threading. As CommonsWare stated HERE:
Perhaps the PackageManager needs to be invoked on the main application
thread, for some reason
So having it on a thread gets undesired behavior, entering and exiting a few times in my Activity makes the list of displayed apps sometimes with items, sometimes empty. Having everything in the UI Thread works like a dream, loads fine everytime. The thing is, the user expects some sort of feedback and I need to provide one. As I start the activity, the screen remains black for 3-4-5-6 seconds (depending on the device and apps installed). How can I provide some sort of feedback ? I am thinking of a ProgressDialog but I don't know how can I start it. Thank you.
As discovered, the loop to work through the applications takes awhile (which can be done in a separate thread), compared to the call to PackageManager.getInstalledPackages() (which has to be done on the UI thread).
Use Async to do background work and show indicator while loading data.
in you onCreate(). call new AsyncCommonDataFetcher(this).execute();
public class AsyncCommonDataFetcher extends AsyncTask<Void, Void, Void> {
Context mContext = null;
private ProgressDialog mProgressIndicator = null;
public AsyncCommonDataFetcher(Context ctx) {
mContext = ctx;
}
protected void onPreExecute() {
mProgressIndicator = ProgressDialog.show(((Activity) mContext), null,
"Please wait", true);
}
#Override
protected Void doInBackground(Void... voids) {
// Do what ever work you like to do. It will do this in backgound and show user a indicator to wait.
}
#Override
protected void onPostExecute(Void voidInstance) {
try {
if (mProgressIndicator != null) {
mProgressIndicator.hide();
mProgressIndicator.dismiss();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
Try the following for ProgressDialog in the onCreate() of your activity
this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
ProgressDialog LoadingDialog = ProgressDialog.show(this, "", "Loading..", true);
And then dismiss it when the process causing the delay is over
LoadingDialog.dismiss();
Related
I am writing my first Android application and am having some trouble with dismissing the ProgressDialog in onPostExecute. I've read through numerous similar questions here on SO,but none of those solutions seemed to fix my particular issue.
The dialog shows up and DOES dismiss, but it isn't doing it the way I want it to. The animation freezes when Filter.apply() is started and does not dismiss until after Filter.apply has completed. Filter.apply() is another time intensive process that is called from various places. This method will have its own ProgressDialog.
What have I done incorrectly?
Here is my AsyncTask
public class JSONFetchFMTask extends AsyncTask<Void, Void, Void> {
private Activity activity;
private ProgressDialog dialog;
public JSONFetchFMTask() {}
#Override
protected void onPreExecute() {
Context context = FM.getAppContext();
Activity activity = ((FM)context.getApplicationContext()).getCurrentActivity();
dialog = ProgressDialog.show(activity, "", "Downloading Data", true);
}
#Override
protected Void doInBackground(Void... params) {
// Process intensive code was here
return null;
}
#Override
protected void onPostExecute(Void result) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
Filter.apply();
}
}
And here is my FM class:
public class FM extends Application {
private static Context context;
private Activity currentActivity;
public void onCreate(){
super.onCreate();
FM.context = getApplicationContext();
}
public static Context getAppContext() {
return FM.context;
}
public Activity getCurrentActivity(){
return this.currentActivity;
}
public void setCurrentActivity(Activity currentActivity) {
this.currentActivity = currentActivity;
}
}
If Filter.apply() is time intensive, you shouldn't run it on the main thread. That's probably why the animation freezes up. You can confirm this if you see a log message that says something like:
"Skipped X frames. Application may be doing too much work on the main thread."
Offload the time intensive aspects of Filter.apply() onto another thread. If you want, you can just use another AsycnTask to do this.
The onPostExecute() method is a hook to the UI thread that AsyncTask objects use to show any results that may be needed. If Filter.apply() is a time intensive process, this will definitely freeze the UI thread and Filter.apply() is now running on the UI thread because it was explicitly asked to run here.
Try to delegate the Filter.apply() onto it's own thread to perform CPU intensive tasks.
I need to process some data when the user click the button in one activity, so the screen looks like the app stops for 2-3 seconds. It isn't a lot but I want to give the user information that everything is ok and IMO the best way will be the progressbar which is visible only when data are processed.
I found the code of ProgressBar and it looks like this:
<ProgressBar
android:id="#+id/loadingdata_progress"
style="?android:attr/progressBarStyle"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_alignBottom="#+id/fin2_note"
android:layout_centerHorizontal="true"
android:indeterminate="true"
android:visibility="invisible" />
and inserted it on the middle of my layout.
And to try if the progressbar works, I put this code
loadingimage= (ProgressBar) findViewById(R.id.loadingdata_progress);
loadingimage.setVisibility(View.VISIBLE);
into onCreate method and everything looks fine.
Then I recreated the code to show this progressbar only if the data is processed.
After click the user invoke this method
public void fin2_clickOnFinalization(View v)
{
loadingimage= (ProgressBar) findViewById(R.id.loadingdata_progress);
loadingimage.setVisibility(View.VISIBLE);
// code where data is processing
loadingimage.setVisibility(View.INVISIBLE);
}
and nothing appear on the screen. I don't know where is the mistake. If I found the progress bar by id, It's strange for me that I can control it in onCreate method but in onclick method it's out of my control.
Your UI thread cannot show progress bar cause it is busy due to your data processing. Try to use this kind of code :
public void fin2_clickOnFinalization(View v) {
new YourAsyncTask().execute();
}
private class YourAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... args) {
// code where data is processing
return null;
}
#Override
protected void onPostExecute(Void result) {
loadingimage.setVisibility(View.INVISIBLE);
super.onPostExecute(result);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
loadingimage.setVisibility(View.VISIBLE);
}
}
EDIT:
AsyncTask let you run code in separate thread and make app more responsive, just put time-consuming code inside doInBackground.
You're not giving the UI time to refresh. Your "data processing" code is running on the UI thread, blocking any visible changes. By the time the system gets control to refresh the display, you've already set it back to invisible.
To fix this, move your processing code to a separate thread or AsyncTask. Then you can set the progress bar to visible, start the task, and have it turn itself invisible once it's done.
I'd recommend AsyncTask for this purpose about 90% of the time on Android, since it comes stock with useful callbacks. The developer guide for it(in the Javadoc linked above) is pretty explicit, and outlines all the steps you need to take.
AsyncTask is too heavily-weighted for such task.
A better much solution
Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
#Override
public void run() {
loadingimage.setVisibility(View.VISIBLE);
}
});
Or even simpler (does essentially the same thing as solution above)
runOnUiThread(new Runnable() {
#Override
public void run() {
loadingimage.setVisibility(View.VISIBLE);
}
});
You can try to create a global ProgressDialog not in the layout but in your activity like:
public class MyActivity {
ProgressDialog progress = null;
protected void onCreate(...) {
progressDialog = new ProgressDialog(this);
progressDialog.setCancelable(false);
progressDialog.setTitle("Progress");
}
public void fin2_clickOnFinalization(View v)
{
progress.show();
// code where data is processing
progress.dismiss();
}
}
Hope i it helps
I have an AsyncTask which does a lot of JSON calculations.
public class InitializationTask extends AsyncTask<Void, Void, InitializationResult> {
private ProcessController processController = new ProcessController();
private ProgressDialog progressDialog;
private MainActivity mainActivity;
public InitializationTask(MainActivity mainActivity) {
this.mainActivity = mainActivity;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(mainActivity);
progressDialog.setMessage("Die Daten werden aufbereitet.\nBitte warten...");
progressDialog.setIndeterminate(true); //means that the "loading amount" is not measured.
progressDialog.setCancelable(false);
progressDialog.show();
};
#Override
protected InitializationResult doInBackground(Void... params) {
return processController.initializeData();
}
#Override
protected void onPostExecute(InitializationResult result) {
super.onPostExecute(result);
progressDialog.dismiss();
if (result.isValid()) {
mainActivity.finalizeSetup();
}
else {
AlertDialog.Builder dialog = new AlertDialog.Builder(mainActivity);
dialog.setTitle("Initialisierungsfehler");
dialog.setMessage(result.getReason());
dialog.setPositiveButton("Ok",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
mainActivity.finish();
}
});
dialog.show();
}
}
}
processController.initializeData() runs for about 20 seconds. All this works. It even works when I install the application, and pressing home button while application is initializing. The AsyncTask is working in the background. When I restart the application from Android device again after the AsyncTask has been finished, the application still works.
But the application cannot handle this use case: When I deploy the application (or start it when no data is initialized), so that it really takes about 20sec to initialize the data and when I hit home to close the application while initialization (the AsyncTask) runs in the background and start the application again, it leads to unexpected behavior as RuntimExceptions and so on. It seems that the device wants to load the application twice, but none of them can start successfully. How to deal with that?
I thought about checking if there is a running AsyncTask in MainActivity to avoid starting it again:
private InitializationTask initializationTask = new InitializationTask(this);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (!AsyncTask.Status.RUNNING.equals(initializationTask.getStatus())) {
initializationTask.execute((Void[])null);
}
initializeMap();
}
Unfortunately this does do nothing. Moreover I'm not sure if such use case is possible at all, because when I start the same application twice, they cannot "share" an AsyncTask. Should I somehow avoid starting the application twice or something? I'm not sure what options do I have on this? Any ideas?
I usually do it a bit more bluntly. I set my AsyncTask reference to null when I'm not using it. When the onClick fires, I check if it's not null, which means I started it. If it is null, I create and execute a new AsyncTask right there. It is plenty fast and it's clean enough. One bonus of this approach is that an AsyncTask can only be executed once anyway, so it fits in well with that. At the end of onPostExecute, you can set the reference back to null again if you intend to stay in the same Activity.
While you're perfecting your AsyncTask flow, make sure that it survives orientation changes as well.
I found the solution: It's actually not an AsyncTask issue. The problem was that my parse method of JSONParser (that does the most of the work) which is invoked in processController.initializeData(), was not synchronized.
I have a ListActivity which launches another Activity based on the list selection. This second Activity needs to load a fair bit of data from the internet and as such there is a noticeable delay between when the user clicks on an item and when the Activity displays.
This is a problem because I currently have no way to indicate to the user that their click is being processed (even just changing the colour of the selected list item would be sufficient but I can't find a good way to do that). Ideally I'd be able to display an indeterminate ProgressDialog while the second Activity is loading.
I've tried a few different approaches for this but nothing seems to work as desired.
I've tried the following:
Retrieving the serializable data (not all of it but some part) in an AsyncTask in the first Activity and passing it as an extra to the second. This didn't really work well as a ProgressDialog I created in onPreExecute() didn't display immediately (it seems delayed by the processing done in doInBackground() for some reason.)
Here is the code for that:
AsyncTask<String, Void, String> read = new AsyncTask<String, Void, String>() {
Dialog progress;
#Override
protected void onPreExecute() {
progress = ProgressDialog.show(SearchActivity.this,
"Loading data", "Please wait...");
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
DatasetReader reader = new DatasetReader();
reader.setFundID(params[0]);
reader.addDatsets(FundProfile.datasets);
reader.populate();
return reader.toString();
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progress.dismiss();
}
};
read.execute(selectedItem.getUniqueID());
try {
action = new Intent(SearchActivity.this, FundProfile.class);
action.putExtra("data", read.get());
} catch(Exception ex) {
ex.printStackTrace();
}
In the second Activity's onCreate() method (this does not work at all):
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarVisibility(true);
Here is the onCreate() method for the second approach:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setTitleColor(Color.WHITE);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setProgressBarVisibility(true);
try {
setContentView(R.layout.fund_profile);
// init some data
setProgressBarVisibility(false);
} catch(Exception ex) {
FundProfile.this.finish();
}
}
If you have long operations you should not be doing them in onCreate in any case as this will freeze the UI (whether or not the activity is displayed). The UI set by onCreate will not appear and the UI will be unresponsive until after the onCreate call finishes.
It seems you can start your second activity and display a progress bar (or requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);), then start an ASyncTask which will be responsible for updating your UI once data has been retrieved.
Adam,
It sounds like you are looking for the Indeterminate Progress bar: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/view/ProgressBar2.html
You can display this while you are loading your second Activity then set the visibility to false once the second Activity has loaded its data.
Move creating the Intent -- and really anything you need to do after the AsyncTask completes -- into onPostExecute:
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
progress.dismiss();
Intent action = new Intent(SearchActivity.this, FundProfile.class);
action.putExtra("data", result);
// ... do more here
}
The problem is that AsyncTask.get() blocks until the task is completed. So in the code above, the UI thread is blocked and the ProgressDialog is never given a chance to appear until the task completes.
The documentation at http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#getWritableDatabase%28%29 states:
Database upgrade may take a long time,
you should not call this method
[getWritableDatabase] from the
application main thread, including
from ContentProvider.onCreate().
This begs the question: for best practice, where should getWritableDatabase be called from?
My feeling is that, perhaps, it should be called once upon application launch with a callback to mark the database as ready. Is this correct?
For small and agile databases I imagine this isn't much of an issue.
Otherwise, I'd use an always-wonderful AsyncTask, called from onCreate.
It can be called from anywhere, but it should not be called from the UI thread because you don't know how long the process will take (especially with the different file systems in use). Even if you know the database should be small, you don't know about the file system (can it perform more than one job at a time? are there are thousand other jobs waiting in line already?). You can use an AsyncTask or a Thread to call getWriteableDatabase.
It seems that the intended use of the open helper framework, is to open the db on activity start, and close it when the Activity is destroyed.
In an AsyncTask from within onCreate()...
new StartupTask().execute();
The AsyncTask Thread.sleep() below is just to give enough time to show the dialog so that you can see it work. Obviously take that out when you're done playing. ;)
private class StartupTask extends AsyncTask
{
private ProgressDialog progressDialog;
#Override
protected Object doInBackground(final Object... objects)
{
openHelperRef.getWritableDatabase();
try
{
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return null;
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
runOnUiThread(new Runnable()
{
public void run()
{
progressDialog = ProgressDialog.show(
MyActivity.this, "Title",
"Opening/Upgrading the database, please wait", true);
}
});
}
#Override
protected void onPostExecute(Object object)
{
super.onPostExecute(object);
progressDialog.dismiss();
}
}
in onDestroy()...
openHelper.close();