android - How to show dialog after activity finishes - android

Say we have two activities, Activity1 and Activity2.
In Activity1's onClick() method, we have a call to start Activity 2 if a certain button is pressed:
Intent myIntent = new Intent(Activity1.this, Activity2.class);
Activity1.this.startActivity(myIntent);
After finish() is called in Activity2, and Activity1 is resumed, I need a dialog to show in Activity1, as soon as it is resumed.
Before, I simply called showDialog(id) in the same block of Activity1's onClick() method:
public void onClick(View v) {
if(v == addHole){
//...
Intent myIntent = new Intent(Activity1.this, Activity2.class);
Activity1.this.startActivity(myIntent);
showDialog(END_DIALOG_ID);
}
}
The issue is, after Activity1 resumes, the dialog corresponding to END_DIALOG_ID is not visible, but the screen is darkened and unresponsive (as if the dialog were present), until the back key is pressed.
I have tried putting the showDialog() call in Activity1's onResume() and onRestart() methods, but these both crash the program.
I have also tried creating an AsyncTask method in Activity2, with the showDialog() call in its onPostExecute(), but the dialog is not visible in Activity2.
private class ShowDialogTask extends AsyncTask<Void, Void, Integer> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
protected Integer doInBackground(Void... id) {
//do nothing
return END_DIALOG_ID;
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
protected void onPostExecute(Integer id) {
super.onPostExecute(id);
showDialog(id);
}
}
I am now trying to implement this by calling
Activity1.this.startActivityForResult(myIntent, END_DIALOG_REQUEST);
with corresponding setResult() and onActivityResult() methods from Activity1, but it seems that there should be a better practice for implementing this. All I need is to have a dialog shown upon Activity2 finishing.
Thanks for any help you can provide.

Like you suggest, call startActivityForResult when starting Activity2. Then, override onActivityResult and check for RESULT_OK, and show your dialog box then. That's a perfectly acceptable practice for doing what you're looking to do.

You can use onResume method (if u'r not looking anything from activity2)
#Override
public void onResume(){
super.onResume();
//do something
}

I have to return to the root activity - MainActivity, potentially closing several activities, and then show dialog. So I have chosen alternative way.
MyDialog {
public static synchronized void planToShowDialog(String info) {
if (info != null) {
saveInfoToPreferences(info);
}
}
public static synchronized void showDialogIfNecessary(Context context) {
String info = readInfoFromPreferences();
if (info != null) {
saveInfoToPreferences(null); // Show dialog once for given info.
new MyDialog(context, info).show();
}
}
private static String readInfoFromPreferences() {
//...
}
private static void saveInfoToPreferences(String info) {
//...
}
}
I call MyDialog.showDialogIfNecessary() from MainActivity.onPostResume() method.

Related

Call different activity method from AsyncTask

I have a MainActivity, SecondaryActivity and an AsyncTask class.
MainActivity has a method called doSomething()
I call the AsyncTask from MainActivity like this:
new asyncTask(MainActivity.this).execute();
Which means I can reference the MainActivity in my onPostExecute
#Override
protected void onPostExecute(Boolean result){
super.onPostExecute(result);
# activity is defined as this.activity
activity.doSomething();
}
How can I call the AsyncTask from my SecondaryActivity in a similar manner, because I'd need a reference to my MainActivity to access its methods?
EDIT: I would want the doSomething() to be called at all times. So even if it's from SecondActivity, once it finishes its background operation -> doSomething()
The method I'm calling refreshes the screen of MainActivity to show data changes. Secondary activity only calls the AsyncTask when it is being paused/stopped/destroyed but currently the Asynctask finishes after MainActivity has started and so the changes aren't visible.
I think your AsyncTask in nested in the MainActivity currently. Its better you put it in separate class. Whatever parameter is required by it pass it in its constructor. Let both your activity implement the same interface. Something like this
class MainActivity/SecondaryActivity implements DoSomethingListener {
void doSomething() {
}
}
Also pass your activity reference to AsyncTask in the constructor.
Finally onPostExecute since you have reference to either MainActivity or Secondary Activity. Call activity.doSomething.
I guess you want to update something in MainActivity based on the result of the AsyncTask called from SecondaryActivity. In that case, I'd suggest calling SecondaryActivity with startActivityForResult. Then in your onPostExecute, call setResult to set a flag or some data.
Finally, in MainActivity override onActivityResult to call doSomething when the request code corresponds to SecondaryActivity.
So your requirement is to have a single instance of MainActivity. Data in MainActivity may be updated from within MainActivity or from SecondaryActivity. In either case the data to be updated is obtained using an AsyncTask.
My Suggestion
Add the following to the MainActivity in manifest, (More about android:launchMode here.)
android:launchMode="singleInstance"
When you are done interacting with SecondaryActivity, do this,
Intent intent = new Intent(this, MainActivity.class);
intent.putExtra("DATA1", "your_data1");
intent.putExtra("DATA2", "your_data2");
startActivity(intent);
finish();
Then in your MainActivity,
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
String data1 = intent.getStringExtra("DATA1");
String data2 = intent.getStringExtra("DATA2");
}
Then call AsyncTask in MainActivity using data1 and data2.
NOTE: This is one way to approach your problem. There are other approaches such as startActivityForResult() depending on your requirement.
UPDATE
If you want to cancel your AsyncTask, call asyncTask.cancel(true);
However, this will not ensure your HttpRequest is aborted, as the cancel will take effect after the request has completed. The work-around for this is a bit hackish. After calling cancel(), contineously check if isCancelled() is true, then do httpRequest.abort() This will only be the fastest way to finish your async task. Need not necessarily mean the request gets aborted.
Try this in your UpsertTask class.
private Context mContext;
public UpsertTask(Context context){
mContext = context;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
if(mContext instanceof MainActivity){
((MainActivity) mContext).doSomething();
}
else if(mContext instanceof SecondActivity){
((SecondActivity) mContext).doSomethingElse();
}
}
#Override
protected void onPostExecute(Boolean result){
super.onPostExecute(result);
# activity is defined as this.activity
if (activity != null) {
if(activity instanceof MainActivity) {
activity.doSomething();
} else if(activity instanceof SecondaryActivity) {
activity.doSomethingElse();
}
}
}
I think that would work. (if you understand your question correctly).
One way this could be done is by using an event bus. This is a way of passing messages/data between activities. You can post to the bus and then activities can listen for the message if they register.
EventBus class (seperate)
public class EventBus extends Bus {
private static final EventBus bus = new EventBus();
public static Bus getInstance() { return bus; }
private EventBus() {}
}
MainActivity class
...
#Override
protected void onResume() {
super.onResume();
EventBus.getInstance().register(this);
}
#Override
protected void onPause() {
super.onPause();
EventBus.getInstance().unregister(this);
}
#Subscribe
public void asyncDone(String message) {
foo(message)
}
AsyncTask class
...
#Override
protected void onPostExecute(Boolean result){
super.onPostExecute(result);
EventBus.getInstance().post("My data")
Thanks to #theheartbreakpug from Reddit for giving me this solution.

Update data in local SQLite then return to parent Activity solution

I am learning Android. I am creating an application that have 2 activities: List Activity list all records from a local SQLite and Edit activity will create/update record.
On Edit activity, I have a button. When the button clicked, I will process creating/updating the record then returning back to parent activity (List activity).
On Button click. I have 2 solutions to process Create/Update:
1. Process Create/Update in UI thread ( Not using AsyncTask )
This solution is fine but I may have to show 'Processing' dialog.
2. Use AsyncTask -- so creating/updating happens in background Thread. Here is my code in Edit Activity:
---- NOTE that Edit activity use Fragment so getActivity() method will return the instance of EditActivity
saveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
AsyncTask<Phrase, Integer, Integer> asyncTask = new AsyncTask<Phrase, Integer, Integer>() {
#Override
protected Integer doInBackground(Phrase... params) {
Phrase phrase = params[0];
if (phrase._id > 0) {
PhraseDao.update(DbManager.openWrite(getActivity()), phrase);
} else {
PhraseDao.insert(DbManager.openWrite(getActivity()), phrase);
}
return null;
}
#Override
protected void onPostExecute(Integer result) {
Intent intent = new Intent();
getActivity().setResult(Activity.RESULT_OK, intent);
// Close Edit Activity then Go back to List activity
getActivity().finish();
// MY QUESTION: What happens if the EditActivity (getActivity) already destroyed?
// How can I handle destroyed activity here
}
};
asyncTask.execute(a_phrase);
}
});
I don't know how to handle 'onPostExecute' method in the case Edit Activity ( accessed by getActivity()) Already destroyed.
Anyone have any ideas? Thank you!
Add a null check to see if activity exists i.e
if(getActivity() != null){
Intent intent = new Intent();
getActivity().setResult(Activity.RESULT_OK, intent);
getActivity().finish();
}

ProgressDialog, activity's onCreate() and AsyncTask

This question has to do with an AsyncTask that needs to kill a ProgressDialog. While the task is running, the activity gets destroyed and recreated (phone rotation, for example). A new dialog gets made with the help of onSaveInstanceState() but the AsyncTask, spawned by the previously destroyed activity, can't see it.
Picture, if you will... (quick mockup code for example's sake).
public class Bob extends Activity {
private ProgressDialog m_d;
public void onCreate(Bundle b) {
m_d = new ProgressDialog(this);
// ...
if (b != null) {
if (b.getBoolean("dialog") == true)
m_d.show();
}
// ...
}
public void onSaveInstanceState(Bundle b) {
super.onSaveInstanceState(b);
b.putBoolean("dialog", (m_d.isShowing()));
}
public void onDestroy() {
if (m_d.isShowing()) {
m_d.dismiss();
m_d = null;
}
//...
}
}
The AsyncTask onPreExecute() does m_d.show() and onPostExecute() does m_d.hide().
The problem is when my activity gets recreated (say, on phone rotation), the AsyncTask seems to have the old m_d. It is null, because that got killed in onDestroy().
It doesn't have the new m_d, created when the activity got recreated. So now I have a ProgressDialog and the guy who was supposed to kill it in onPostExecute() can't see it.
Now what? I need the old AsyncTask to somehow signal the new ProgressDialog to go away.
private OnItemSelectedListener onSpinnerSelection = new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
// ...
new Switcharoo().execute();
}
}
private class Switcharoo extends AsyncTask<Void, Void, Void> {
protected void onPreExecute() {
m_d.show();
// ...
}
protected void onPostExecute() {
if (m_d != null) m_d.hide();
// ...
}
protected Void doInBackground(Void... arg0) {
// ...
}
}
If Android doesn't kill my activity while the task is running, it's fine. The ProgressDialog pops up and goes away like I expect.
If Android decides to restart my activity for any reason while the task is running, m_d will be null in onPostExecute() even though I recreated it in onCreate() - it has the old m_d, not the new one.
There are two problems: the first is that the AsyncTask is bound to the activity that has created it. I mean, the instance of the activity.
If you want it to survive between activities rotations you have to store it somewhere else (check this for example). In any case, you need to have a reference to it from the new activity (another thing you could do is to use a model fragment, i.e. a fragment with no UI that you set as setRetainInstance(true)).
Once you have the reference to the async task from the newly created activity, nothing prevents you to have m_d local to the async task and a setter method that updates it with the new dialog.
Note also that it would be a good practice to have weak references pointing to the activity and the dialog itself in order to allow garbage collection. Otherwise the dialog (and probably the activity itself) would not be freed until the execution of the task itself ended.

How to handle AsyncTask onPostExecute when paused to avoid IllegalStateException

I appreciate the numerous postings regarding AsyncTask on a rotation change. I have the following problem when using the compatability lib and trying to dismiss a DialogFragment in onPostExecute.
I have a fragment which fires of an AsyncTask which displays a progress DialogFragment, then in onPostExecute dismisses the dialog and then potentially throws up another DialogFragment.
If when the progress dialog is being displayed I put the application into the background I get the following for my fragment:
1) onPause
2) onSaveInstanceState
3) onPostExecute in which I try to dismiss and invoke a dialog.
I get an IllegalStateException because I'm trying to effectively commit a transaction when the activity has saved its state and I understand this.
On a rotation I've assumed (perhaps incorrectly) that I wouldn't get an onPostExecute until the activity has been recreated. However, when putting the application into the background I assumed (definitely incorrectly) that the onPostExectute wouldn't get called while the fragment/activity was paused.
My question is, is my solution to simply detect in onPostExecute that the fragment/activity is paused and simply perform what I need to do in onResume instead? Seems somewhat ugly to me.
Thanks in advance, peter.
Edit 1
Need to support 2.1 and above
Edit 2
I have considered showing the dialog using FragmentTransaction:add and FragmentTransaction:commitAllowingStateLosshowever this isn't without its problems.
If you need to synchronize your task with the activity lifecycle, I believe that Loaders are exactly what you need. More specifically, you should use AsyncTaskLoader to do the job. So now instead of running an AsyncTask, you launch your loader, then wait for response in a listener. If the activity is paused, you won't get a callback, this part will be managed for you.
There is another way to handle this task: using a fragment which retains its instance. The general idea is that you create a fragment without UI and call setRetainInstance(true). It has a task which is being notified about the activity being available or not. If not, the task's thread suspends until an activity becomes available.
Another way of achieving what you require is to implement the PauseHandler class that I documented in this post.
Then in your onPostExecute method call sendMessage() to post your message into the handler.
When your application resumes the action will be handled.
Rather then using BroadcastReceiver, I prefer using bus libraries like guava, otto or eventbus. Their performance is much better then the broadcast receiver implementation.
I came up with a solution for this problem without any major workaround:
The basic idea how to maintain a progressdialog and a asynctask is described in this blogentry (of course I used the AsyncTaskComplex-Version). All credits go to the author of this blogentry, I only added a tiny thing:
Obviously I'm not using showDialog() anymore. Instead I stick with DialogFragments.
The second tweak is the importent one and also solves the problem with the IllegalStateException:
Instead of only telling the asynctask in onRetainCustomNonConfigurationInstance() that there is no more activity I also do it in onPause(). And instead of only telling the asynctask in onCreate() that there is a new activity I also do it in onResume().
And there you go, your AsyncTask will not try to inform your activity about his finish causing an IllegalStateException when the activity is not visible.
If you would like to see more code instead of words, leave a comment.
/edit:
Sourcecode to show my solution, which I think is a pretty decent one :)
public class MyActivity extends Activity {
private MyTask mTask;
#Override
protected void onCreate(Bundle pSavedInstanceState) {
super.onCreate(pSavedInstanceState);
setContentView(R.layout.editaccount);
Object retained = getLastCustomNonConfigurationInstance();
if ( retained instanceof NewContactFolderIdTask ) {
mTask = (MyTask) retained;
mTask.setActivity(this);
}
}
#Override
protected void onPause() {
if(mTask != null) {
mTask.setActivity(null);
}
super.onPause();
}
#Override
public Object onRetainCustomNonConfigurationInstance() {
if(mTask != null) {
mTask.setActivity(null);
return mTask;
}
return null;
}
#Override
protected void onResume() {
if(mTask != null) {
mTask.setActivity(this);
}
loadValues(); // or refreshListView or whatever you need to do
super.onResume();
}
public void onTaskCompleted() {
loadValues(); // or refreshListView or whatever you need to do
DialogFragment dialogFragment = (DialogFragment) getSupportFragmentManager().findFragmentByTag(PROGRESS_DIALOG_FRAGMENT);
if(dialogFragment != null) {
dialogFragment.dismiss();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// app icon in Action Bar clicked; go home
Intent intent = new Intent(this, OXClient.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
case R.id.menu_refresh:
mTask = new MyTask(this);
mTask.execute();
break;
}
return super.onOptionsItemSelected(item);
}
private class NewContactFolderIdTask extends AsyncTask<Boolean, Integer, Bundle> {
private MyActivity mActivity;
private boolean mCompleted;
private NewContactFolderIdTask(MyActivity pActivity) {
this.mActivity = pActivity;
}
public void setActivity(MyActivity pActivity) {
this.mActivity = pActivity;
if(mCompleted) {
notifiyActivityTaskCompleted();
}
}
private void notifiyActivityTaskCompleted() {
if(mActivity != null) {
mActivity.onTaskCompleted();
}
}
#Override
protected Bundle doInBackground(Boolean... pBoolean) {
// Do your stuff, return result
}
#Override
protected void onPreExecute() {
DialogFragment newFragment = ProgressDialogFragment.newInstance();
newFragment.show(getSupportFragmentManager(), PROGRESS_DIALOG_FRAGMENT);
}
#Override
protected void onPostExecute(Bundle pResult) {
mCompleted = true;
notifiyActivityTaskCompleted();
}
}
}
On How to handle Handler messages when activity/fragment is paused I offer another approach using a BroadcastReceiver.
I consider it cleaner more elegant and it offers the advantages that you can invoke code on your base fragment from everywhere within your app and by using sticky broadcasts your invocation can be "remembered" and executed after your fragment resumes.

How to force main Acivity to wait for subactivity in Android?

I am calling a subactivity from main activity. This subactivity should take few numbers from user (i'm using Edit text control to achieve this), save them to static variable in another class and terminate. I want main activity to wait for subactivity but both are just running simultaneously. Even doing sth like that doesn't help:
Thread t = new Thread(new Runnable(){
public void run(){
Log.v("==================", "run "+new Date());
startActivityForResult(new Intent(ctx,myCustomSubactivity.class),1);
} });
Log.v("==================", "calling run "+new Date());
t.start();
try {
t.join();
} catch (InterruptedException e) {Log.v("==================", "can't join");}
Log.v("==================", "back from activity "+new Date());
do you know how to force main activity to wait? Thread.wait() method is not supported in Android(program throws error).
May be I'm missing something but why don't just use startActivityForResult and onActivityResult mechanism? You could get result from you subactivity from intent it was resulted with.
Edit: BTW as far as I understand, if you will run Object.wait() from Activity code if will hold UI tread whitch can result in Application not responding error.
I agree with Nikolay this is definitely the android way to do this.
Start the subactivity with startActivityForResult in the sub activity use setResult to add an result code and an intent with all the numbers you need in the data bundle.
In your first activity overwrite onActivityResult and retrieve the numbers from the Intent.
If you use the static variable this seems easier in the first moment but it is very insecure and there are some cases this may not work. If your program is send to the background your activities will be saved but if the phone runs low on memory the system will close your program and after the user resumes it everything looks like the moment the user left it but the static variables will be recreated to their initialization value.
Try to get used to the way the android activity lifecycle works. Using this approach will result in fewer used memory and a much better user experience.
Check out the Notepad example, it covers exactly this situation. And as others have said, the Android way is to have your first activity start up your second activity (not sub-activity!) and asynchronously listen for a response (not pause or wait, no need for joining, etc.).
Well... you can do it like this (btw, there's not straight forward way):
Have a singleton class, let's call it Monitor:
public class Singleton
{
private Singleton() { }
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
}
public class ParentActivity extends Activity
{
private void startAndWait()
{
Intent i = new Intent();
// initialize i
startActivityForResult(i);
Singleton si = Singleton.getInstance();
synchronized(si)
{
si.wait();
}
//do remaining work
}
}
public class ChildActivity extends Activity
{
protected void onCreate(Bundle savedInstance)
{
//do all the work
Singleton si = Singleton.getInstance();
synchronized(si)
{
si.notify();
}
}
}
I'm not here to judge if it's a good pattern or not but if you really need an activity to wait for a sub-activity, you can try this approach:
define an object (lock) over which the two activities get synchronized; this can (should) also work as the object to exchange data between those two activities and thus should be defined as static
in parent activity, start an async task (as the UI main thread cannot be in waiting state)
in the async task, start your sub-activity
the async task waits on the lock till it gets notified
the sub-activity does whatever it needs and notifies the waiting thread when it finishes
I did a similar thing in my app and IMHO had a good reason for this (not to bother a user with login screen upon app start or resume, the app tries to re-use credentials stored in a secured place and only in case it fails, it shows this login screen. So yes, basically any activity in my app can get "paused" and waits till the user provides correct credentials in the login activity upon which the login screen finishes and the app continues exactly where it got paused (in the parent activity).
In the code it would be something like this:
ParentActivity:
public class ParentActivity extends Activity {
private static final String TAG = ParentActivity.class.getSimpleName();
public static class Lock {
private boolean condition;
public boolean conditionMet() {
return condition;
}
public void setCondition(boolean condition) {
this.condition = condition;
}
}
public static final Lock LOCK = new Lock();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.parent_layout);
// do whatever logic you need and anytime you need to stat sub-activity
new ParentAsyncTask().execute(false);
}
private class ParentAsyncTask extends AsyncTask<Boolean, Void, Boolean> {
#Override
protected Boolean doInBackground(Boolean... params) {
// do what you need and if you decide to stop this activity and wait for the sub-activity, do this
Intent i = new Intent(ParentActivity.this, ChildActivity.class);
startActivity(i);
synchronized (LOCK) {
while (!LOCK.conditionMet()) {
try {
LOCK.wait();
} catch (InterruptedException e) {
Log.e(TAG, "Exception when waiting for condition", e);
return false;
}
}
}
return true;
}
}
}
ChildActivity:
public class ChildActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.child_layout);
// do whatever you need in child activity, but once you want to finish, do this and continue in parent activity
synchronized (ParentActivity.LOCK) {
ParentActivity.LOCK.setCondition(true);
ParentActivity.LOCK.notifyAll();
}
finish();
// if you need the stuff to run in background, use AsyncTask again, just please note that you need to
// start the async task using executeOnExecutor method as you need more executors (one is already occupied), like this:
// new ChildAsyncTask().executeOnExecutor(ChildAsyncTask.THREAD_POOL_EXECUTOR, false);
}
}

Categories

Resources