I'm testing a destroy/restart sequence to make sure a counter retains its original value (before it was being incorrectly incremented on the restart). I put a fix in and it worked, when I test manually. But the unit test always passes, whether I include the fix or not. As you can see in the code below, I'm getting the counter value, then restarting, getting the counter value again and comparing them. What could be the problem?
public void testNumCorrectEqualAfterDestroy() {
mCorrect = (TextView) mActivity.findViewById(R.id.correct);
int before = Integer.parseInt(mCorrect.getText().toString());
mActivity.finish();
mActivity = this.getActivity();
mCorrect = (TextView) mActivity.findViewById(R.id.correct);
int after = Integer.parseInt(mCorrect.getText().toString());
Assert.assertEquals(before, after);
}
I think finish() will not cycle your activity through the "appropriate" states. The way I've tested this lifecycle case before is like so:
...
//TODO: do not use getActivity, instead use the startActivity() method
//and pass a value in the Bundle parameter
...
getInstrumentation().callActivityOnStart(mActivity);
getInstrumentation().callActivityOnResume(mActivity);
//TODO: asssert that the value is the expected one (based on what you fed in the bundle)
Bundle newBundle = new Bundle();
getInstrumentation().callActivityOnSaveInstanceState(mActivity, newBundle);
getInstrumentation().callActivityOnPause(mActivity);
getInstrumentation().callActivityOnStop(mActivity);
getInstrumentation().callActivityOnDestroy(mActivity);
//initialize activity with the saved bundle
getInstrumentation().callActivityOnCreate(mActivity, newBundle);
getInstrumentation().callActivityOnResume(mActivity);
//TODO: assert that the value is the expected one
ActivityInstrumentationTestCase2.getActivity() starts the Activity the first time you call it, and then it simply returns that Activity in each subsequent call in the test case. Thus, you are still looking at the Activity that you finished.
After you finish the first Activity, you need to start a new one from the test. You can use InstrumentationTestCase.launchActivity(), for example.
As another example, I've written a test that pushes a button in ActivityA that launches ActivityB for-result; the test then immediately kills ActivityA (via an orientation change, but finish() would work, too), and then the test gets a handle to the new ActivityA that the system creates when ActivityB is done and sends its result. The trick there was to have the test add an Instrumentation.ActivityMonitor and then have that monitor wait for the system to start the new ActivityA and give the test a handle to it.
Related
I want to develop an application that, at the beginning, checks a variable's value and, basing on this value, starts the activity A or the activity B, something like this:
protectec void onCreate(...){
boolean b = checkVariable();
if(b){
startActivityA();
} else {
startActivityb();
}
}
What I'm doing
This is the method I have currently implemented:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_limitation);
varChecker = new VarChecker(this);
if(varChecker.getRemainingUses() <= 0){
limitReached();
} else if(varChecker.isFirstTime()){
firstTime();
} else {
startMainActivity();
}
}
This is an activity that must be shown the first time the application is executed. Else, depending on the getRemainingUses() result, it must start the activity A or the activity B automatically.
The question
Is there any way to do what I want, without the need of create a new Activity, specially to avoid the super.onCreate(savedInstanceState) and the setContentView(R.layout.activity_limitation)?
Is there any way to do what I want, without the need of creating a new
Activity, specially to avoid the super.onCreate(savedInstanceState)
and the setContentView(R.layout.activity_limitation)?
I had a similar problem some time ago. As far as I know, It's not possible to avoid calling super.onCreate(savedInstance) and setContentView(R.layout.activity_limitation). It's design of the Android activity.
I see the following possible solutions:
if you want to choose an appropriate activity by e.g. clicking a
button, then you just need to create a new Intent basing on a
variable and there's no problem.
if you want to choose an activity in
a different moment of the flow - e.g. during the application start you can:
create a single activity for two options, use fragments and switch between them
create "dummy" activity with an empty layout, create an intent and switch to an appropriate activity basing on a variable and finish "dummy" activity. Please note that this solution is a kind of workaround and it's worth to spend some time to figure out something better, but I'm not sure if there is a recommended solution for such use cases.
I am storing some data in static varibles in Activity1 and accessing in Activity3,and Activity 5. ie..
Activity1---> Activity2--->Activ3
.....................|
......................Activity4.-----> Activ5
This works fine if we close the application completely, from Activity1 (ie if the user is at Activ5 if he clicks back button then -->Activ4-->Activ2-->Activ1-->Exit)
But the user is exiting app at Activ3,4,5 by clicking Mobile exit button(Not the application exit), Now after few hrs the user is reopening application then , It(app) gets started from Activi3 or 4 or 5. (ie where ever app was closed).
Now, Since i am using some data(which i stored in static varibles in Activ1.)
I am getting null values. Why this is happining. How to avoid this types of errors.
I have used sharedpref to avoid this.Is this the only solution ?
Restore the state of activity when it is recreated, so that the values passed can be retrieved at a later time.
e.g. for an integer that was passed through intent do as following: -
//this will save the value if an activity is killed in background.
#Override
protected void onSaveInstanceState(Bundle outState)
{
getIntent().putExtra("count", getIntent().getStringExtra("count"));
super.onSaveInstanceState(outState);
}
//In restore instance state, retrieve the stored values. The following work can also be done //in oncreate, as when an activity is killed in background, onCreate method is also called.
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
if(savedInstanceState == null)
return;
int count = getIntent().getIntExtra("count", 0);
super.onRestoreInstanceState(savedInstanceState);
}
You need to add onSaveInstanceState methods to your earlier activities, and check the bundle received by the onCreate methods. Check out the Activity Lifecycle for details.
You should not store values in static members, the activity context gets released, thus you losing your static values. The preferred way of passing values between activities is using Bundles along with the Intents.
you can create new class and extend application ,and in that store all data that you want, its very useful but remember if you do that you must add name of your application in manifest file
background
I have at least 2 activities on an app i've developing : a splash activity and a main activity. the splash activity calls the main activity.
on some cases (for now let's assume it's the first run only of the app) the splash activity adds a boolean extra (using intent.putExtra) to the intent to be true, and the main activity reads it using :
getIntent().getBooleanExtra(...,false);
the problem
i only wish to see the flag as true when i open the main activity after the splash activity.
This is why i've tried to just call (in the onCreate, right after i get the flag) :
getIntent().removeExtra(...);
another approach (acccording to this website) would be:
final Intent newIntent = new Intent();
setIntent(newIntent);
and another approach could be:
getIntent().putExtra(..., false);
none of those work: for some reason, on some cases, the flag is still returned as true.
as an example, i can press the home button (when the main activity was in the foreground), and then i start a heavy app (like the cut-the-rope game or a benchmark app), and then return to the app using the launcher.
in this case (which doesn't always occur), the splash activity isn't shown, but instead the main activity is shown. it calls onCreate again, while the flag itself is still set to true .
the question
why does it occur? how come the intent doesn't get reset?
is there a way to overcome this in an elegant way?
is it safe to just ignore the flag when the "savedInstanceState" is not null ?
It sounds like your VM is being torn down and re-started. I have observed on some devices that when the VM is re-created, instead of entering the launch activity, the last running activity is reconstructed from the saved state. It sounds like you are attempting to save state in the activity intent, so that is probably where your problems are coming from.
An important key to avoiding problems like this is to remember that the savedInstanceState bundle passed to onCreate is always null when the activity is being created new, in all other cases such as reconstructing after rotation or even when the VM has been torn down (as is likely your case) the savedInstance bundle passed to onCreate will be non-null.. my advice would be to use the intent to initialize your local state (the initial value of your flag) when the savedInstanceState is null, then never try to update your own intent, save your state in the savedInstance bundle..
E.G.
class Someactivity extends Activity {
private static final STATE_FLAG = "flagState";
private boolean someFlag;
protected void onCreate (Bundle savedInstanceState) {
final Intent intent = getIntent();
if(savedInstanceState==null) {
// Brand new, set from intent
someFlag = intent.getBooleanExtra(STATE_FLAG, false);
} else {
// Restored, set from saved instance
someFlag = savedInstanceState.getBoolean(STATE_FLAG, false);
}
}
protected void onSaveInstanceState (Bundle outState) {
// Save any updates to our activity state
outState.putBoolean(STATE_FLAG, someFlag);
}
}
I have an application where whenever I exit the application via the home hardware button, it should return to the last state the application is in. However, when I launch the application again, the application shows a white screen with only my header bar. And when I click on the header bar's button, the application crashes with the IllegalStateException where the application cannot find the method for the button clicked.
I am currently implementing with Sherlocks Fragment, where the header bar is an action bar. I'm also using HTC Rhyme, Version 2.3 (Gingerbread). The following is the codes for the addition of fragments into my main app.
Codes to add the fragments within the onCreate method in the activity:
FragmentTransaction trans = getSupportFragmentManager().beginTransaction();
Bundle bMain = getIntent().getExtras();
String statusCheck = "";
if (bMain != null) {
statusCheck = bMain.getString("statusCheck");
}
if (statusCheck.equals("web")) {
MyWebViewFragment webfrag = new MyWebViewFragment();
trans.add(R.id.container,webfrag, "WebViewFragment");
} else if(statusCheck.equals("traveloguelist")) {
MyTravelogueListFragment travelfrag = new MyTravelogueListFragment();
trans.add(R.id.container,travelfrag, "TravelogueListFragment");
}
trans.commit();
This is the codes when I change a fragment:
MyTravelogueListFragment travelfrag = new MyTravelogueListFragment();
getSupportFragmentManager().beginTransaction().replace(R.id.container, travelfrag).addToBackStack(null).commit();
[Edit]
I realized after much reading and running that the main issuei have is that upon resuming the application, the activity is actually created again. Thus, some of the parameters i passed in does not get registered, resulting in the wrong display. I THINK this is the error that is causing that to happen:
Previously focused view reported id "myresID" during save, but can't be found during restore.
However, I don't know how you force the application to remember the previous state of the fragment? Or is there any other way around this problem?
I'm still very stuck with this problem. Will really appreciate it if someone can help me!
After much trial and error and many readings, I finally found a way to sort of solve my problem.
From what I understand, this problem will occur due to the Activity's life cycle. The comment by Tseng in this forum was quite comprehensive:
http://forum.unity3d.com/threads/127794-Android-Apps-crashing-on-resume
It seems that during the time when other applications are invoked when a certain activity is onPause/onStop, Android might free up some of its memory the activity is currently holding on to if there is insufficient memory required. In this case, all the current objects or variable the paused activity is having will be destroyed. Thus, when the activity is back on focus, the onCreate is actually invoked again. Thus, the activity will have no idea which fragment I am currently require.
However, I realized that it will always call the saveInstanceState which is essentially a bundle object. So I did the following:
onSaveInstanceState method
#Override
public void onSaveInstanceState(Bundle bundle) {
//activityFrag is a string object that tells me which fragment i am in currently
bundle.putString("statusCheck", activityFrag);
}
onCreate method
if (savedInstanceState != null) {
getSupportFragmentManager().popBackStack(null, getSupportFragmentManager().POP_BACK_STACK_INCLUSIVE);
//return;
statusCheck = savedInstanceState.getString("statusCheck");
} else {
statusCheck = b.getString("statusCheck");
}
What I have done is to remove all the fragments I have stacked thus far to remove any issues where there is missing information needed. So this is like starting anew again. The status check just determine which fragment the user has last visited.
After much testing, it seems like it does solve my problem. though I wouldn't say it is perfect. One of the main downfall I have is that whenever I change my fragment, I have to update and change my statusCheck to make sure the correct fragment will be called. However, I have to admit this way is a little unorthodox and might not be very correct.
If any of you have any better ideas, please feel free to share!
You can try to implement following:
Use putFragment to save all fragments, currently located in FragmentManager, into bundle in onSaveInstanceState;
And then you can use getFragment to get all previously stored fragments back from bundle in onRestoreInstanceState.
Also... you'll probably need some HashMap that will help to determine the hierarchy of the fragments (in case you have containers and contained fragments) to be saved into bundle as well.
Also... when restoring from bundle you'll need to know keys for all fragment you've put there earlier. Probably, the easiest way is simply to organize an array of keys and put them into bundle when saving the fragment into instance.
This way your saving and restoring will be complete and centralized.
I am calling an activity from within itself - basically i've a list of new storys and two filter buttons that when clicked restart the activity with an intent passed that changes the news stories.
When i run the app it works, but for a second i get the old activity UI while the app reads from the new xml feed and then the UI updates. Is there any way to stop this from happening and get the activity to restart cold.
here's the code I am currently attaching to the onclicklistener
public void openFootballNews(View v) {
Intent i = new Intent(this, News_Landing.class); // News_landing class is the class this code is in
Bundle bundle = new Bundle();
bundle.putString("code", "football"); // this, if set, changes the xml feed to read
i.putExtras(bundle);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
this.onCreate(null); //this has halved the time the old UI is on the screen for but I cant get rid of it completely
startActivity(i);
}
any help would be great, thanks!
Starting an activity from itself doesn't make much sense (unless your aim is to do something esoterically recursive ;) ). Also, I may be mistaken, but I believe activities are kept in a stack so that as you flip between news stories, you're piling up one nearly-identical activity after another. I'd similarly think calling onCreate() by hand is bad form.
Would need to see all of your code, but my guess is that you are reading your feed and creating your list inside onCreate(), and that your best bet is to refactor that into a openNews(String sport) method, which you call once in onCreate() and again in your listener(s).