I know finish() method is used to finish current activity while starting a new activity. But probably finish() method is not working in my AsyncTask override method.
Okay, My current activity is LoginActivity in which I implemented AsyncTask and in one of my override methods of AsyncTask I am starting LoggedInActivity. In LoggedInActivity there are many fragments. This doesn't cause any problem I think so. When I press BACK button I get LoginActivity. I don't want that. Please take a look at my code:
public class LoginActivity extends Activity implements OnClickListener {
private void startLoggedInActivity()
{
Intent i = new Intent(this, LoggedInActivity.class);
startActivity(i);
finish();
}
private class FetchProfileTask extends AsyncTask<String, Integer, JSONObject>
{
protected JSONObject doInBackground(String... strings) {
bla....bla....
}
protected void onPreExecute() {
bla....bla....
}
protected void onPostExecute(JSONObject jsonObject) {
try {
startLoggedInActivity();
} catch (Exception e) {
Log.w("Exception", "FetchProfileTask - onPostExecute()");
} finally {
}
}
}
}
I will be pleased if anyone helps me.....
Add android:noHistory="true" to LoginActivity in your manifest.
What this does is mark the activity to not be added to your Activity stack after going to another activity. So when you press the back button, it will close your app rather than going back to the LoginActivity. With this approach, you no longer need the finish() call.
<activity
...
android:noHistory="true">
The back button will change to the last activity. If the activity is finished, Android will create it again(I'm not very sure).So I think you can rewrite the back button.
Activity activity; // Instance Variable
Inside On Create :
activity = this;
private void startLoggedInActivity()
{
Intent i = new Intent(this, LoggedInActivity.class);
startActivity(i);
activity.finish();
}
Related
I have the following activity path:
Main -> A0 -> A1 -> A2 -> B
or
Main -> B
On Main, the user can select to either show something on B, or to create something new with the A series.
After completing the A series, it goes to B.
When the user gets to B via the A route, I want the back button to go to Main. But when the user is in the A series, I want the back button to go to the previous A (or Main from the first A).
I tried using FLAG_ACTIVITY_NO_HISTORY when I create the intents, but that just makes everything go back to Main.
Is there a way to mark the activities for removal once I hit a threshold?
You could add a BroadcastReceiver in all activities you want to close (A0, A1, A2):
public class MyActivity extends Activity {
private FinishReceiver finishReceiver;
private static final String ACTION_FINISH =
"com.mypackage.MyActivity.ACTION_FINISH";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
finishReceiver= new FinishReceiver();
registerReceiver(finishReceiver, new IntentFilter(ACTION_FINISH));
}
#Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(finishReceiver);
}
private final class FinishReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_FINISH))
finish();
}
}
}
To close these activities trigger the following broadcast from Activity B.
sendBroadcast(new Intent(ACTION_FINISH));
Here is the github example project for the same.
Define the logic in this very way
In Series of Activity A define the logic in this very way
public static Activity A=null;
//in onCreate();
A=this;
//Same way in B
public static Activity B=null;
B=this
now wherever you want the back condition to be checked override the OnbackPressed.
#Override
public void onBackPressed()
{
if(Activity_A.A!=null)
{
finish();
return;
}
if(Activity_B.B!=null){
//your condition
finish();
}
}
You can fire a new intent for Main activity with SINGLE_TOP and CLEAR_TOP flags in onBackPressed of B. So no matter how you landed at B, pressing back takes you to Main with all history cleared. No need to mess with A series activities.
I am trying to wrap my head around what proper activity flow convention is.
I currently have:
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
//do stuff
//clicklisteners setup etc
Intent intent = new Intent(this, ExampleActivity.class);
//putExtras
startActivity(intent);
}
}
public class ExampleActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
//getExtras
//objectA state lives here
//do stuff
}
}
If the user presses back when on the ExampleActivity view, and then clicks another listener that takes them to ExampleActivity, I want to be able to access "objectA" state again. How do I implement this? I am not understanding onResume or onRestart...
are these the methods to call? or is there a better convention to navigate the app activities?
Android has a mechanism for having an activity pass results back to the prior activity that started it. The documentation for that is here.
Basically, you use startActivityForResult to start the second activity, the second activity uses setResult to set results, and the first activity receives those results in the onActivityResult callback when the second activity finishes.
If the user presses back when on the ExampleActivity view, the ExampleActivity is dead and user is back in the MainActivity, which calls "onResume".
When your are back from activity1 to activity2, activity2's onResume method is called.
With that being said, after the user closed ExampleActivity objectA is destroyed.
I have a main activity which use view pager to display 3 tabs. In each tab fragment, I may call two AsyncTask classes to fetch data from the my web service. When the background task failed to connect to the web service, I would like to handle the ConnectException by closing the main activity(that has the 3 tabs) and redirect to errorActivity. What is the best way to do this?
I have set launchMode="singleTop" in the manifest file.
Currently, what I do for each AsyncTask is:
private class FetchDataTask extends AsyncTask<Void, Void, ArrayList<Person>> {
#Override
protected ArrayList<Person> doInBackground(Void... params) {
try {
return new Fetcher().fetchPeople();
} catch (IOException e) {
Log.e(TAG, "Failed to fetchPeople");
cancel(true);
return null;
}
}
#Override
protected void onPostExecute(ArrayList<Person> people) {
mPeople = people;
….
}
#Override
protected void onCancelled() {
super.onCancelled();
Intent i = new Intent(getActivity(), ErrorActivity.class);
i.putExtra(ErrorFragment.EXTRA_ERROR_MSG, ErrorFragment.MSG_UNEXPECTED_ERROR);
startActivity(i);
getActivity().finish();
}
}
But the problem I am facing is having to open multiple ErrorActivity considering that the first two tabs will be called upon start up.
Create a function in your activity called onConnectionFailed() and in all fragments when service call fails call getActivity() typecaste it into your activity and call onConnectionFailed().
You can also create an interface with this function, make you activity implement that function and provide the body for it, and finally pass the activity as the object of that interface to the fragments.
inside onConnectionFailed() just call finish()
EDIT 1:
onWebServiceCallFailed(){
if(!error){
Intent i = new Intent(this,
i.putExtra(ErrorFragment.EXTRA_ERROR_MSG, ErrorFragment.MSG_UNEXPECTED_ERROR);
startActivity(i);
this.finish();
error = true;
}
}
And inside activity create a boolean error = false
I have a service that is, among other things, downloading images from internet. When this is done I want to show this image in a custom Activity that has a dialog theme. But I only want to use this pop up if the app is running, otherwise I just want to create a notification.
But I get an exception when I try to start an activity from my service and i feel that maybe this isn't the right way to do it?
It says:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
So my question is if this is the right way to do this by setting that flag or how should I get my downloaded image from my service to an activity. Can I in some way tell an activity to start a new activity from my service class?
I think using Broadcast Receiver is better option for you.
Add Below Method in Service and call this method when image Download Complete.
private void updateMyActivity(Context context) {
if(MainActivity.activityStatusFlag){
//update the activity if activityStatusFlag=true;
Intent intent = new Intent("mUpdateActivity");
context.sendBroadcast(intent);
}else{
//display notification if activityStatusFlag=false;
}
}
In Activity Add Following Code.
public class MainActivity extends Activity{
public static boolean activityStatusFlag= false;
//define this variable to check if activity is running or not.
#Override
protected void onResume() {
super.onResume();
activityStatusFlag = true;
this.getApplicationContext().
registerReceiver(mMessageReceiver,new IntentFilter("mUpdateActivity"));
}
#Override
protected void onPause() {
super.onPause();
activityStatusFlag = false;
this.getApplicationContext().unregisterReceiver(mMessageReceiver);
}
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Display Popup or update Activity
}
};
}
I have a rather commonly occurring situation in Android, which has to do with the previous asynctask updating the activity, whilst the activity has been lost because of a change in orientation.
I have an activity, Activity A.
Activity A implements OnDownloadCompleteListener {
public void sync()
{
new SyncAttestationInfoTask(this).execute();
}
#Override
public void onComplete()
{
loadAttestationInfo();
}
}
Here is my asynctask shortened:
package com.evento.mofa.backgroundtasks;
import java.util.ArrayList;
import java.util.List;
/**
* #author Ahmed
*
*/
public class SyncAttestationInfoTask extends AsyncTask<Void, Void,Void> {
/*TIP*/
//TO SPEED UP THIS OPERATION WE CAN USE THE EXECUTEONEXECUTOR .
private ProgressDialog pd;
private OnDownloadComplete parentActivityContext;
EntityConvert convert = new EntityConvert();
private AttestationDao aDao = new AttestationDao();
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
if (Locale.getDefault().getLanguage().equals("ar"))
{
/*EMPTY ALL THE TABLES THEN START PROCESSING*/
aDao.flushTables(Locale.getDefault().getLanguage());
syncAttestLocations(Webservices.ATTEST_LOCATION_AR,1);
syncDocumentTypes(Webservices.DOCUMENT_TYPES_AR,1);
syncAttestationInfo(Webservices.ATTESTATION_INFO_AR,1);
} else {
/*EMPTY ALL THE TABLES THEN START PROCESSING*/
aDao.flushTables(Locale.getDefault().getLanguage());
syncAttestLocations(Webservices.ATTEST_LOCATION,0);
syncDocumentTypes(Webservices.DOCUMENT_TYPES,0);
syncAttestationInfo(Webservices.ATTESTATION_INFO,0);
}
return null;
}
public SyncAttestationInfoTask(OnDownloadComplete context) {
parentActivityContext = context;
}
#Override
protected void onPreExecute() {
pd = new ProgressDialog((Context)parentActivityContext);
pd.setTitle("Loading...");
pd.setMessage("Updating Data.");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
}
#Override
protected void onPostExecute(Void result) {
pd.dismiss();
parentActivityContext.onComplete();
// findViewById(R.id.the_button).setEnabled(true);
}
}
There is something strange with my Activity.
I put a breakpoint on the onComplete callback inside my activity
I start a progress dialog inside the sync async task.
As soon as the progress dialog displays on the screen I landscape my device.
The dialog box vanishes, and pd.dismiss() raises a "View not attached" error (I understand that the activity that it was attached to no longer exists).
The above means that parentActivityContext().oncomplete should also throw the same error, however it does not.
I commented the pd.Dismiss(), and found out that the breakpoint on onComplete() is invoked? Isn't this strange given the fact that the reference to the activity has been lost at this point?
Please give me insight into this.
I would do this
Add this line to your Manifest.xml file, this will prevent of calling onCreate() when screen rotates.
<activity android:name=".yourActivity" android:configChanges="keyboardHidden|orientation">
Version above Android 3.2, you also need to add "screenSize":
<activity android:name=".yourActivity" android:configChanges="keyboardHidden|orientation|screenSize">
This will prevent activity from restarting on orientation change, and you should not have any problems (except maybe some layout fixes)
The previous Activty is being referenced by the AsyncTask via a strong reference.
AsyncTask constructed with Activity 1 (parentActivityContex =
Activity 1)
Activity 1 "destroyed" and Activity 2 comes into foreground
AsyncTask completes, calls parentActivityContext (Activity 1) onComplete
Activity 2 just sits there doing nothing
Activity 1 no longer has active references pointing to it, is collected
You could try doing your task in a Service and having whatever Activity is in the foreground receive a broadcast, or you could try having your AsyncTask reference a Fragment with setRetainInstance(true). Below is an example of the second case. Note that you might want to handle the case where the AsyncTask completes while the Fragment is detached from one Activity and not yet attached to the next Activity
public class ExampleDownloadFragment extends Fragment {
#Override
public void onCreate(final Bundle savedInstanceState) {
setRetainInstance(true);
new SyncAttestationInfoTask(this).execute();
}
public void onComplete() {
final Activity activity = getActivity();
if (activity != null && activity instanceof A) {
((A) activity).onComplete();
}
}
}