I have a menu button, that when I click it, it sends some data to the cloud. While it is sending the data, I display a progress dialog. Every thing goes swimmingly and seems to be fine and I can press the button as many times as I want and the data is properly sent to the cloud.
But when I go to exit the activity I get a error saying that there was a window leak:
com.android.internal.policy.impl.PhoneWindow$DecorView{4321fd38 V.E..... R......D 0,0-1026,288} that was originally added here
the "here" is referring to when I insatiate my progress dialog.
Here is my code:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
progressDialog = ProgressDialog.show(CruiseDetailRollCallActivity.this, "", "Loading...", true);//set up & show progress dialog
switch(item.getItemId()){
case R.id.menu_roll_call_opt_in:
//saveing something into Parse -- (a database online, check Parse.com if you want more info, but just treat this like I am saving something into the cloud)
currentUser.put("somethingBoolean", false);
currentUser.saveInBackground(new SaveCallback(){
#Override
public void done(ParseException e) { //once data has been put into the cloud
progressDialog.dismiss();//dismiss the dialog
supportInvalidateOptionsMenu();//refreshes the options menu
}
});
return true;
default:
return super.onOptionsItemSelected(item);
}
}
I should mention that this is not crashing my application but merely displaying the error. I have no clue why it is happening, and I feel like I shouldn't just ignore it.
EDIT: found my mistake. It was failing because works was because I was hitting back on the action bar and the progress dialog would be created but never dismissed since it was only being dismissed inside the done code.
My guess is that you're leaking the activity into the currentUser object, when you pass the CruiseDetailRollCallActivity into currentUser.saveInBackground(new SaveCallback().... The SaveCallback class that you just created now has a strong reference to the Activity. It will never be garbage collected, even though you exit the method. You should use a WeakReference and then it can be garbage collected.
WeakReference<CruiseDetailRollCallActivity> weakRef = new WeakReference<CruiseDetailRollCallActivity>(CruiseDetailRollCallActivity.this)
Then, pass the weakRef in to the ProgressDialog constructor:
progressDialog = ProgressDialog.show(weakRef.get(), "", "Loading...", true);
Whenever you're passing around the Context in Android, check to see if you need a WeakReference so it can be garbage collected. It's easy to leak the entire application.
It was failing because works was because I was hitting back on the action bar and the progress dialog would be created but never dismissed since it was only being dismissed inside the done code.
I moved
progressDialog = ProgressDialog.show(CruiseDetailRollCallActivity.this, "", "Loading...", true);//set up & show progress dialog
into
case R.id.menu_roll_call_opt_in:
//saveing something into Parse -- (a database online, check Parse.com if you want more info, but just treat this like I am saving something into the cloud)
currentUser.put("somethingBoolean", false);
currentUser.saveInBackground(new SaveCallback(){
#Override
public void done(ParseException e) { //once data has been put into the cloud
progressDialog.dismiss();//dismiss the dialog
supportInvalidateOptionsMenu();//refreshes the options menu
}
});
return true;
so looks something like this
case R.id.menu_roll_call_opt_in:
progressDialog = ProgressDialog.show(CruiseDetailRollCallActivity.this, "", "Loading...", true);//set up & show progress dialog
//saveing something into Parse -- (a database online, check Parse.com if you want more info, but just treat this like I am saving something into the cloud)
currentUser.put("somethingBoolean", false);
currentUser.saveInBackground(new SaveCallback(){
#Override
public void done(ParseException e) { //once data has been put into the cloud
progressDialog.dismiss();//dismiss the dialog
supportInvalidateOptionsMenu();//refreshes the options menu
}
});
return true;
Related
I have an activity that calls a second java class. I want after the second class is called to show a progressbar and then return to normal activity execution. I found some other threads but i couldn't make the progressbar to stop.
There's a full example over here.
Quote:
Declare your progress dialog:
ProgressDialog progress;
When you're ready to start the progress dialog:
progress = ProgressDialog.show(this, "dialog title",
"dialog message", true);
and to make it go away when you're done:
progress.dismiss();
Here's a little thread example for you:
// Note: declare ProgressDialog progress as a field in your class.
progress = ProgressDialog.show(this, "dialog title",
"dialog message", true);
new Thread(new Runnable() {
#Override
public void run()
{
// do the thing that takes a long time
runOnUiThread(new Runnable() {
#Override
public void run()
{
progress.dismiss();
}
});
}
}).start();
ProgressDialog is deprecated, so you might want to use a ProgressBar.
I've found this post about deleting one of them.
Well, I think this is rather ridiculous, but here is how I fixed it.
In my xml for the ProgressBar, I added android:visibility="gone"
to hide it by default. Then, in my code, I first told it to display
(View.VISIBLE) before it tried getting the server list, then I told
it to hide (View.GONE) after it was done. This worked (I could see
the progress indicator while the data loaded, then it went away). So
I suppose I couldn't get it to hide in the code because the code is
not what forced it to be visible to begin with... That seems like a
bug to me.
Its very Simple:
to show a Progress
ProgressDialog dialog = ProgressDialog.show(getContext(), "Title", "Message");
and to stop it:
dialog.dismiss();
I am building a very basic vocabulary application. The feature I am trying to implement right now is a go to feature, that is taking the user to a specific vocab term. i am doing this by prompting the user with a dialog fragment that asks the user for a page number. (dialog fragment will get triggered via a callback, button press)
This is my code for doing so
public class GoToDialog extends DialogFragment{
submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String pgn = pageNumber.getText().toString();
if(!isNumeric(pgn) || pgn.isEmpty()) {
Toast.makeText(getActivity(), "Please enter a valid number", Toast.LENGTH_SHORT).show();
} else {
int pagina = Integer.parseInt(pgn);
if(pagina <= 0 || pagina > total) {
Toast.makeText(getActivity(), String.format("Please enter a valid " +
"term number between 0 and %d", total), Toast.LENGTH_SHORT).show();
} else {
getDialog().dismiss();
getFragmentManager().executePendingTransactions();
communicator.onDialogMessage(pagina);
}
}
}
});
Here are screenshots when I run my application
Screenshot2(right after screenshot 1)
In terms of functionality The dialog loads up fine and is able to take the user to the right location. However in that example of taking the user from term 7 to term 5, the user is taken to the
right term but the dialog doesn't close as it should from getDialog().dismiss(). I know dismiss is being called because I walked through the code and communicator.onDialogMessage(pagina) returns the right term number to the activity. The dialog does close when I select another term number to go to. Does anyone see the issue? This doesn't make sense to me at all.
To close a dialog, dismiss is the correct method to use
- How to correctly dismiss a DialogFragment?
I also tried what a user suggested in Correct way to remove a DialogFragment: dismiss() or transaction.remove()?, which is to call executePendingTransactions().
If anyone's having a similar issue, the issue with my application was my OnTouchListener.
When I set up on OnTouchListener to trigger the DialogFragment, here was my original code for doing so
goTo - TextView
private void setUpGoToTouchListener() {
goTo.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
FragmentManager fm = MainActivity.this.getFragmentManager();
GoToDialog dialog = new GoToDialog();
Bundle bundle = new Bundle();
bundle.putInt("Size", defMan.getTotalCount());
dialog.setArguments(bundle);
dialog.show(fm, "Manager");
return true;
}
});
}
When the gesture(a touch) on the TextView occurs, two MotionEvents will be generated, the press, ACTION_DOWN - first finger has touched the screen, and the release, ACTION_UP - the last of the fingers has stopped touching the screen. Because two motion events occurred, two dialog fragments were created. Thats why dismiss had to be called twice in my situation to get rid of both dialog fragments. I fixed this by having a conditional test for event.getAction()
In my app, I am showing a dialog during a long-running background process which is modal.
This dialog is dismissed when android returns from the background task.
final ProgressDialog progressDialog = ProgressDialog.show(activity, "", "Doing something long running", true, true);
new Thread(new Runnable() {
public void run() {
someLongRunningCode(); // (not using AsyncTask!)
activity.runOnUiThread(new Runnable() {
public void run() {
progressDialog.dismiss();
}
});
}
}).start();
Now, however, when the user rotates his device while the background process is still running the activity is recreated and thus, the progressdialog gets detached from the activity.
When the long running process is done, android obviously tries to hide the (now detached) progress dialog which then results in an exception: java.lang.IllegalArgumentException: View not attached to window manager.
Is there any way to check if it is safe to dismiss the dialog?
Edit: Or better still, is there any way to attach the existing dialog to the newly created activity?
A new, cloned dialog would be fine aswell.
Just catch the exception and ignore it. Android's decision to restart the activity on rotation makes threads have a few odd exceptions like that. But the end result you want is no dialog box displayed. If you just catch the exception, no dialog box is displayed. So you're good.
If you aren't using a separate layout/drawables for landscape and portrait, you can just override configChange in your manifest for the activity so it doesn't destroy the activity (it will still correctly rotate and resize everything for you). That way the same dialog will be up and you shouldn't get the exception. The other option would require a lot of work around onSaveInstanceState and onRestoreInstanceState, and you'd need to be very careful of timing issues if the thread actually finishes during that time. The whole recreate activity on rotation idea doesn't work well with multithreading.
if (progressDialog != null) {
progressDialog.dismiss();
}
I've faced this issue before and solved using the below code.
private boolean isDialogViewAttachedToWindowManager() {
if (dialog.getWindow() == null) return false;
View decorView = progressDialog.getWindow().getDecorView();
return decorView != null && decorView.getParent() != null;
}
I am writing the app and I have the following scenario:
First i show an Acitivty and I show a ProgressDialog while loading data from a Server. I do the loading task in a Thread because I found this to be a solution so that the Dialog is shown correctly
showDialog(DIALOG_LOAD);
loadListThread = new Thread(new Runnable(){
#Override
public void run(){
service.execRequest();
loadingFinishedHandler.sendEmptyMessage(0);
MyActivity.this.dismissDialog(MyActivity.DIALOG_LOAD);
}
});
loadListThread.start();
After the Thread is finished I notify a Handler. Again this is a solution I found for the problem to access Views in the activity without being in the Thread anymore
loadingFinishedHandler = new Handler(){
#Override
public void handleMessage(Message msg){
showList();
}
};
After loading is finished, I check whether I received data correctly. If not, i want to show an error dialog:
showDialog(DIALOG_ERROR);
I call the dialogs with the onCreateDialog method
public Dialog onCreateDialog(int dialogId){
switch(dialogId){
case DIALOG_LOAD:
String message = getString(R.string.load);
ProgressDialog loadDialog = new ProgressDialog(this);
loadDialog.setMessage(message);
loadDialog.setCancelable(false);
return loadDialog;
case DIALOG_ERROR:
String message = getString(R.string.error);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(errorMessage);
builder.setNeutralButton(getString(R.string.ok), null);
return builder.create();
}
return null;
}
And here is the problem: If I change the Orientation of my phone while data is loading and an error dialog shall be created afterwards, I get this message
Activity [...] has leaked window [...] that was originally added here
I understand why this is happening. The View has been re-created, when i rotated my phone and showDialog() is obviously trying to access the old one. But I do not know how to fix it.
Does anyone has an idea?
Edit: solved it :-)
I found out that I can prevent my Activity from restarting on orientation change. I read about it in this article
Handling Runtime Changes
I declared in the AndroidManifest by adding
android:configChanges="orientation"
that i handle orientation changes by myself. These changes can now be handled with this method
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
}
But for my case it was not necessary to add any code!
public Dialog onCreateDialog(int dialogId){
if(isFinishing()){
return null;)}...
I need a popup dialog to be shown when i get a message from different thread but the dialog should be not dependent on Activity i.e, it should display the dialog wherever the screen focus is.
Can it be done? Because the dialog is handled per Activity, I thought of using a service but again it would be one more thread added and I want to avoid that.
Any other options available?
If you're trying to ask how to show a dialog when your activity is not the focused activity on the user's phone then try using Notifications instead. Popping up a dialog over a different application interrupts the user when they may be doing something else. From the Android UI guidelines:
Use the notification system — don't
use dialog boxes in place of
notifications
If your background service needs to
notify a user, use the standard
notification system — don't use a
dialog or toast to notify them. A
dialog or toast would immediately take
focus and interrupt the user, taking
focus away from what they were doing:
the user could be in the middle of
typing text the moment the dialog
appears and could accidentally act on
the dialog. Users are used to dealing
with notifications and can pull down
the notification shade at their
convenience to respond to your
message.
A guide to create notifications is here: http://developer.android.com/guide/topics/ui/notifiers/notifications.html
Alternative solution :
AlertDialog dialog;
//add this to your code
dialog = builder.create();
Window window = dialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.token = mInputView.getWindowToken();
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
window.setAttributes(lp);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
//end addons
alert.show();
if I understand you correctly you could use a base class for all of your activities
public abstract class BaseActivity extends Activity{
protected static BaseActivity current_context = null;
#override
protected void onPause(){
current_context = null;
super.onPause();
}
#override
protected void onResume(){
current_context = this;
super.onResume();
}
public static void showDialog(/*your parameters*/){
//show nothing, if no activity has focus
if(current_context == null)return;
current_context.runOnUiThread(new Runnable() {
#override
public void run(){
AlertDialog.Builder builder =
new AlertDialog.Builder(current_context);
//your dialog initialization
builder.show();
}
});
}
}
in your thread show your dialog with BaseActivity.showDialog(..) But this approach doesn't work if you want to show your dialog on top of any activity of the target device.