I have my Activity flows like this ACTIVITY A -> ACTIVITY B -> ACTIVITY C -> ACTIVITY D. When the user is on Activity D and click a button called exit, the application should go back to Activity B and finish the Activities C and D. How can I do that?
NOTE : ACTIVITY B and ACTIVITY D are the same class but different instance
In AndroidManifest.xml, set android:noHistory as true for Activity B, C and D. set it as false for Activity A (actually, the default is false).
Demo:
<activity android:name=".xx.xx.ActivityA"
android:noHistory="false"
android:screenOrientation="nosensor">
</activity>
<activity android:name=".xx.xx.ActivityB"
android:noHistory="true"
android:screenOrientation="nosensor">
</activity>
<activity android:name=".xx.xx.ActivityC"
android:noHistory="true"
android:screenOrientation="nosensor">
</activity>
<activity android:name=".xx.xx.ActivityD"
android:noHistory="true"
android:screenOrientation="nosensor">
</activity>
For the exit Button:
exitBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(),ActivityB.class);
startActivity(intent);
finish();
}
});
When you back from activity D and go to activity A then you need to use these code.
1. First create the instance of all activity using this code.
Activity A{
}
Activity B{
public static Activity activityB;
oncreate(){
activityB=this;
}
Activity C{
public static Activity activityC;
oncreate(){
activityC=this;
}
Activity D{
}
2. Please call this method on back press in Activity D
private finishActivity(){
Clear the instance of middle activity.
B.activityB.finish();
C.activityC.finish();
finish();
}
Quite complicated solution, but allows to fully manipulate backstack. Basically create your own "backstack" in you Application:
public class MyApplication extends Application {
private Set<Activity> runningActivities = new HashSet<>();
onCreate() {
// ...
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
#Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
runningActivities.add(activity);
}
#Override public void onActivityDestroyed(Activity activity) {
runningActivities.remove(activity);
}
});
}
public void killActivities(Set<Class<?>> activitiesToRemove) {
for (Activity stackActivity : runningActivities) {
if (activitiesToRemove.contains(stackActivity.getClass())) {
stackActivity.finish();
}
}
}
}
Now you can call something like this in you ActivityD:
((MyApplication)getApplication()).killActivities(ActivityD.class, ActivityC.class)
You can use Map instead of Set if class isn't only specifier necessary to determine which activity should be removed...
Please corret me if there is something wrong with my solution, but it seems like there should be no leaks and no boilerplate in your activities
Edit - Kotlin version:
class MyApplication : Application() {
private val runningActivities = mutableSetOf<Activity>()
override fun onCreate() {
// ...
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle) {
runningActivities.add(activity)
}
override fun onActivityDestroyed(activity: Activity) {
runningActivities.remove(activity)
}
})
}
fun killActivities(activitiesToRemove: Set<Class<Activity>>) {
runningActivities.forEach { activity ->
if (activitiesToRemove.contains(activity.javaClass)) {
activity.finish()
}
}
}
}
Just add this in your ActivityD.class
finishButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(),ActivityB.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
}
});
Hope it helps!
Best and full proof way of implementing this is create the Application class which extends android.app.Application and there just maintain your list of opened activity's instances.
Next is mention your created Application class name into <application> within the AndroidManifest.xml
And then whenever you want you can destroy the opened activities.
Related
Lets put we 3 activates that may follow the stack A->B->C
How can I close A and keep B->C, but only if B opens C?
C is not always opened, but if it gets opened only then A should be removed, leaving the stack as B->C. Otherwise will remain as A->B.
I don't see the possibility of using finish() in A, as it should be closed from B when opening C.
By the way A is a single instance with its own affinity.
Write an abstract BaseActivity class and implement a LocalBroadcastManager in it and register a BroadcastReceiver. Note that we have also introduced an abstract onFinishReceived method.
public abstract class BaseActivity extends AppCompatActivity {
private LocalBroadcastManager mLBM;
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
onFinishReceived(intent.getStringExtra("activity"));
}
}
protected void onCreate(#Nullable Bundle savedInstanceState) {
...
mLBM = LocalBroadcastManager.getInstance(this);
IntentFilter filter = new IntentFilter();
filter.addAction(...);
mLBM.registerReceiver(receiver, filter);
}
protected void onDestroy() {
mLBM.unregisterReceiver(receiver);
mLBM = null;
}
abstract void onFinishReceived(String activity);
}
Then, extend all A, B and C Activities from the BaseActivity and override the onFinishReceived() method.
public class A extends BaseActivity {
void onFinishReceived(String activity) {
if (activity.equals("A") { // or "B" or "C"
finish();
}
}
}
}
Now, whenever you want to finish a specific activity use,
LocalBroadcastManager.getInstance(context)
.sendBroadcast(new Intent()
.setAction(...)
.putExtra("activity", "A") // or "B" or "C"
);
LocalBroadcastManager is now deprecated. It's better if you can use something like RxJava. The idea is to use an asynchronous and event-based mechanism rather than going for a simple ugly solution like storing the Activity in a static variable.
I created a BaseActivity and in it I custom method finish
#Override
public void finish() {
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
super.finish();
}
Now I create a new activity B extends BaseActivity but I want when I finish Actiivty B I use new animation
overridePendingTransition(R.anim.transtate_out, R.anim.hold_actibity);
How I can replace animation when call finish in activity B?
Thank you for watching!
You can declare a var in BaseActivity like
int inAnimRes = R.anim.slide_in_right, outAnimRes = R.anim.slide_out_left;
#Override
public void finish() {
overridePendingTransition(inAnimRes , outAnimRes );
super.finish();
}
then set value to these two variables in B.
One way is to remove finish method of BaseActivity class and override this finish method in ActivityB class to add custom your animation there, like this:
#Override
public void finish() {
overridePendingTransition(R.anim.transtate_out, R.anim.hold_actibity);
super.finish();
}
I'm working in a app ,where I filter if screen is on or off and launch an activity when the screen is on, but sometime it launch the activity but the activity was started already , i would like to ask you if it was any way to know inside my app if an activity is already launched.
When i start the activity i added this code
intent11.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Thank you so much.
I think that you can set variable with activity life cycle
class IAMActivity extends Activity {
static boolean isStart = false;
public void onStart() {
super.onStart();
isStart = true;
}
public void onStop() {
super.onStop();
isStart = false;
}
}
Here is what you are looking for:
class MyActivity extends Activity {
static boolean alreadyLaunched = false;
#Override
public void onStart() {
super.onStart();
alreadyLaunched = true;
}
#Override
public void onStop() {
super.onStop();
alreadyLaunched = false;
}
}
If you want to restrict your activity to one instance, you can set the launchMode in the manifest file to either singleTask or singleInstance, depending on your requirements.
<activity
android:launchMode="singleTask"
...>
...
</activity>
With singleTask a new intent is delivered via onNewIntent() instead of onCreate().
See <activity> for more details.
I'm using this code to jump back in activity stack (mainly to move to home Activity):
Intent goTo = new Intent(this, HomeActivity.class);
goTo.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(goTo);
So I create new Intent and set "target" to HomeActivity which is in Activity stack so whole stack will be cleared from top to this HomeActivity.
Now I need slightly different use case. I have for example five Activities A-B-C-D-E (A started B etc.) on the stack. Now I need to jump from E to C or B depending of what user choose. The problem is that Activities A, B, C, D, E have same class. So I can't use example above because I don't know how to target that Activity.
So the question is if there is any way how to "tag activity" or manipulate with stack.
Thanks!
I haven't tried it myself, but I think the best option would be to refactor your app to use a stack of Fragments within a single Activity (since you can then more easily manage the backstack using the provided addToBackStack() and popBackStack() methods). Basically this involves moving most of the code in your Activity into a Fragment and then adding the backstack manipulation code in the Activity). You can look at the code for FragmentBreadCrumbs (with API 11+) or the code for HanselAndGretel (for use with the compatibility library) to see how this can be implemented.
However, if you want to continue using your current multi-Activity approach, the following is some code I came up with to illustrate how you can do this.
First, add several internal classes to alias your current Activity and put these classes into a sequence list (note also the simplistic getSequencedActivityIntent() method that I wrote, you can add more advanced logic if you need - maybe use a HashMap to associate each class in the sequence with an arbitrary tag value?):
public class MyActivity extends Activity {
public static class A extends MyActivity {}
public static class B extends MyActivity {}
public static class C extends MyActivity {}
public static class D extends MyActivity {}
public static class E extends MyActivity {}
public static class F extends MyActivity {}
public static class G extends MyActivity {}
public static class H extends MyActivity {}
public static class I extends MyActivity {}
public static class J extends MyActivity {}
private final static List<Class<?>> SEQUENCE = Arrays.asList(new Class<?>[] {
A.class, B.class, C.class, D.class, E.class,
F.class, G.class, H.class, I.class, J.class,
});
private Intent getSequencedActivityIntent(int step) {
final int current = SEQUENCE.indexOf(this.getClass());
if (current == -1) new Intent(this, SEQUENCE.get(0));
final int target = current + step;
if (target < 0 || target > SEQUENCE.size() - 1) return null;
return new Intent(this, SEQUENCE.get(target));
}
// the rest of your activity code
}
Don't forget to add their entries to your AndroidManifest.xml file too (singleTop is optional - it will prevent the Activity instance in the stack to be created again when brought back to the front):
<activity android:name=".MyActivity$A" android:launchMode="singleTop" />
<activity android:name=".MyActivity$B" android:launchMode="singleTop" />
<activity android:name=".MyActivity$C" android:launchMode="singleTop" />
<activity android:name=".MyActivity$D" android:launchMode="singleTop" />
<activity android:name=".MyActivity$E" android:launchMode="singleTop" />
<activity android:name=".MyActivity$F" android:launchMode="singleTop" />
<activity android:name=".MyActivity$G" android:launchMode="singleTop" />
<activity android:name=".MyActivity$H" android:launchMode="singleTop" />
<activity android:name=".MyActivity$I" android:launchMode="singleTop" />
<activity android:name=".MyActivity$J" android:launchMode="singleTop" />
Now, whenever you need to start a new "top" instance of your Activity, you can do something like:
final Intent intent = getSequencedActivityIntent(+1);
if (intent == null) return;
intent.putExtra("dataset", dataSet);
startActivity(intent);
And when you need to go back to one of the instance in the backstack you can do:
final Intent intent = getSequencedActivityIntent(- stepBack);
if (intent == null) return;
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
You can index your activities without having to worry about handling all the chain of onActivityResults using a super Activity that you extend in all your activities
Here is an implementation (I did not test it) but if you extend this SuperActivity in all your Activities, you can call fallBackToActivity( int ) to any activity using its index and each activity now has a getIndex(). You can use it to fallback to a relative index like getIndex()-3
package sherif.android.stack.overflow;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class SuperActivity extends Activity {
private static String EXTRA_INDEX = "SUPER_INDEX";
private static int RESULT_FALLBACK = 0x123456;
private int index;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getIntent()!=null) {
index = getIntent().getIntExtra(EXTRA_INDEX, -1) + 1;
}
}
protected final int getIndex() {
return index;
}
protected final void fallBackToActivity(int index) {
Intent intent = new Intent();
intent.putExtra(EXTRA_INDEX, index);
setResult(RESULT_FALLBACK, intent);
finish();
}
#Override
public void startActivityForResult(Intent intent, int requestCode) {
intent.putExtra(EXTRA_INDEX, getIndex());
super.startActivityForResult(intent, requestCode);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_FALLBACK) {
if(data.getIntExtra(EXTRA_INDEX, -1)!=getIndex()) {
setResult(RESULT_FALLBACK, data);
finish();
}
}
}
}
You can just keep a condition in your statement
if user chooses this item pass intent to B class
and if user chooses that item pass intent to C class
Add extra to your intent that will point the activity what to do.
For example
intent.putExtra("STATE", 1);
And get this value in onCreate of your activity.
getIntent().getExtras()
As I understand Android only targets the class of activity and not a particular instance of activity. So, I think you won't be able to do what you want by just adding some flags on Intent.
I think the easiest approach would be to implement it on your own by something like this
a) Create some singleton and have a member in it which points to instance of activity to which you want to return (as example activity B). Probably, you will have to store all activities in some list to be able to get instance of some previously launched activity.
b) Override onResume for all activities and in it do following check:
if (SomeSingleton.getTargetActivity() != null && this != SomeSingleton.getTargetActivity())
finish();
else
SomeSingleton.setTargetActivity(null);
c) As soon as you need to return from E do
SomeSingleton.setTargetActivity(B);
finish();
This will close top activity (which is E) and call onResume on activity D. It will check whether it's target. If it's not then it will close it and system will call onResume on activity C and so on.
If you want to use an abnormal approach, or some tricky once, you will have more problems later. I think you can
Define a abstract/non-abstract subclass of Activity and define everything you want. If other classes are exactly the same as the above class, so just subclass from it and do nothing more. But if the classes ( Activities ) may different from each other, you can provide abstract/non-abstract methods to define additional abilities.
So
You write a reusable codes for all activities,
You act normal so you will get good result
You can control everything specialized in your activites
You can control stack using manifest file
and more
for detailed information see below codes:
Parent Activity:
public abstract class AbstractActivity extends Activity {
AbstractActivity currentActivity;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
currentActivity = this;
someProtectedMethod();
commonMethod();
// etc...
/* event handling */
Button btn_first = (Button) findViewById(R.id.btn_first);
Button btn_second = (Button) findViewById(R.id.btn_second);
btn_first.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(currentActivity, FirstActivity.class);
currentActivity.startActivity(intent);
}
});
btn_second.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(currentActivity, SecondActivity.class);
currentActivity.startActivity(intent);
}
});
}
/** you must override it, so you can control specified things safe */
protected abstract void someProtectedMethod();
protected void commonMethod() {
Log.i("LOG", "Hello From " + getClass().getName());
}
#Override
protected void onResume() {
super.onResume();
//some statement that work in all activities to
Log.i("LOG", "On Resume: " + getClass().getName());
}
}
First Activity:
public class FirstActivity extends AbstractActivity {
#Override
protected void someProtectedMethod() {
Log.i("LOG", "Special Action From First Activity");
}
}
Second Activity:
public class SecondActivity extends AbstractActivity {
#Override
protected void someProtectedMethod() {
Log.i("LOG", "Special Action From Second Activity");
}
}
main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
<Button
android:id="#+id/btn_first"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="Open First Activity" />
<Button
android:id="#+id/btn_second"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="Open Second Activity" />
</LinearLayout>
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.activity_control"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="7" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name=".FirstActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondActivity"
android:label="#string/app_name" >
</activity>
</application>
</manifest>
The best and the easiest solution (so far) will be use Fragments and FragmentManager. Then tag each Fragment and use FragmentManager. Using only Activity can be very difficult to have almost the same result.
I have three activities ActivityA, ActivityB, ActivityC.
Suppose in ActivityA, there is some code like...
if(someCondition()){
gotoActivityB();
}
else{
gotoActivityC();
}
Now, If user goes to ActivityB, ActivityA should not be finished.
If he goes to ActivityC, it should be finished.
Adding noHistory in manifest file doesn't work.
Also, finish()in if condition doesn't work, As there are many activities after ActivityC in which ActivityA should be in background.
I don't want to call startActivity(context,ActivityA.class)in those activities onBackPressed() because, it will again execute code of onCreate() in ActivityA.
So, is there a way, where i can remove ActivityA from the stack when user presses back button in ActivityB?
may be something like this:?
ActivityB.this.finish();
ActivityA.finish(); //some code to finish ActivityA
Okay, here is one way you can accomplish your goal. You will need to pass around the Activity context to wherever you need it in order to call finish() on it. I used the Application class to do this. I only used two classes to do it for the sake of time, but it should work just fine for your purposes. Here is how I did it:
This is the first class. It is the Activity that we want to close from another Activity.
public class MainActivity extends Activity implements OnClickListener {
private Button button;
// application instance
private MainApplication mainApplication;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mainApplication = (MainApplication) getApplicationContext();
// set the Activity's context for later usage. Doing this determines which
// Activity can be closed from another Activity.
mainApplication.setActivityContext(this);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
Intent i = new Intent(this, SecondActivity.class);
startActivity(i);
break;
}
}
}
This is the Second Activity. Exiting out of it will also cause finish() to be called on the first class:
public class SecondActivity extends Activity {
private Activity activityContext;
private MainApplication mainApplication;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_activity_layout);
mainApplication = (MainApplication) getApplicationContext();
// get the Activity context you stored in the MainApplication class
// so you can call finish on it.
activityContext = mainApplication.getActivityContext();
}
#Override
protected void onPause() {
super.onPause();
// closes your defined Activity. If you press the back button you will find
// that you exit right out of the app as the other Activity gets popped off
// the stack.
activityContext.finish();
}
}
And the Application class:
public class MainApplication extends Application {
private Activity activityContext;
public Activity getActivityContext() {
return activityContext;
}
public void setActivityContext(Activity activityContext) {
this.activityContext = activityContext;
}
}
And of course make sure to declare your MainApplication class in the AndroidManifest:
<application
android:name=".MainApplication"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
This is a sort of hacky way to do this. There may be better ways. But regardless, you have to pass around the context of the Activity that you want to call finish() on. Then you can close it from anywhere.
Hi you can finish your activity in current activity itself based on the condition. or use StartActivityforResult based on the result you can finish your activity.
hope this will help you.
You can try this in another way, like i do.
Create a static instance variable of the activity in the beginning.
private static Activity1 thisAct = null; // Activity1 is name of class
Now initialize this variable in onCreate() method
thisAct = this;
Create a static method which will finish this activity
public static void finishActivity()
{
thisAct.finish();
}
While going to Activity C, clear the FLAG :
Intent cIntent = new Intent(view.getContext(), cActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(cIntent);