Espresso tests cause "ClassNotFoundException when unmarshalling" (REPRODUCED on basic app) - android

It seems that this unmarshalling exception always gets thrown when an activity is recreated with saved instance state during espresso tests. I have reproduced it with a very basic android app. Here are the steps:
Create an android app with two activities which each have one button. The button on the first activity opens the second activity. The button on the second activity closes the current activity.
Add an espresso test which simply opens the first activity, clicks the button (to open the second activity), then clicks the button on the second activity (to finish the second activity and go back to the first activity).
On your emulator, be sure to enable "Don't Keep Activities" on your emulator.
In my real app, it varies based on the activity which class will be "unknown" to cause the unmarshalling. In this specific example, it's apparently the toolbar. I have found that by removing specific entries ("androidx.lifecycle.BundlableSavedStateRegistry.key" and "android:viewHierarchyState") from the saved instance state that it will workaround this exception during espresso tests, but of course then things don't get restored correctly. And I'll reiterate that this is only a problem while running espresso tests. When performing the same exact test steps manually, everything unmarshalls correctly and there are no exceptions.
Changing sdk versions doesn't seem to help either.
That's it.
Here's all the gory code:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// // This will cause the exception to not be thrown during espresso tests
// if(savedInstanceState != null) {
// savedInstanceState.remove("androidx.lifecycle.BundlableSavedStateRegistry.key");
// savedInstanceState.remove("android:viewHierarchyState");
// }
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.open_button).setOnClickListener(v -> startActivity(new Intent(MainActivity.this, ChildActivity.class)));
}
}
public class ChildActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_child);
findViewById(R.id.close_button).setOnClickListener(v -> finish());
}
}
#LargeTest
#RunWith(AndroidJUnit4.class)
public class MainActivityTest {
#Rule
public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<>(MainActivity.class);
#Test
public void test() {
onView(withId(R.id.open_button)).perform(click());
onView(withId(R.id.close_button)).perform(click());
onView(withId(R.id.open_button)).check(matches(isDisplayed()));
}
}
androidx.test.espresso.PerformException: Error performing 'single click - At Coordinates: 115, 272 and precision: 16, 16' on view 'view.getId() is <2131231192/com.example.myapplication:id/close_button>'.
at androidx.test.espresso.PerformException$Builder.build(PerformException.java:1)
at androidx.test.espresso.base.PerformExceptionHandler.handleSafely(PerformExceptionHandler.java:8)
at androidx.test.espresso.base.PerformExceptionHandler.handleSafely(PerformExceptionHandler.java:9)
at androidx.test.espresso.base.DefaultFailureHandler$TypedFailureHandler.handle(DefaultFailureHandler.java:4)
at androidx.test.espresso.base.DefaultFailureHandler.handle(DefaultFailureHandler.java:5)
at androidx.test.espresso.ViewInteraction.waitForAndHandleInteractionResults(ViewInteraction.java:8)
at androidx.test.espresso.ViewInteraction.desugaredPerform(ViewInteraction.java:11)
at androidx.test.espresso.ViewInteraction.perform(ViewInteraction.java:8)
at com.example.myapplication.MainActivityTest.mainActivityTest(MainActivityTest.java:31)
... 32 trimmed
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: androidx.appcompat.widget.Toolbar$SavedState
at android.os.Parcel.readParcelableCreator(Parcel.java:2839)
at android.os.Parcel.readParcelable(Parcel.java:2765)
at android.os.Parcel.readValue(Parcel.java:2668)
at android.os.Parcel.readSparseArrayInternal(Parcel.java:3118)
at android.os.Parcel.readSparseArray(Parcel.java:2351)
at android.os.Parcel.readValue(Parcel.java:2725)
at android.os.Parcel.readArrayMapInternal(Parcel.java:3037)
at android.os.BaseBundle.initializeFromParcelLocked(BaseBundle.java:288)
at android.os.BaseBundle.unparcel(BaseBundle.java:232)
at android.os.Bundle.getSparseParcelableArray(Bundle.java:1010)
at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2133)
at android.app.Activity.onRestoreInstanceState(Activity.java:1135)
at android.app.Activity.performRestoreInstanceState(Activity.java:1090)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1317)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:2953)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at androidx.test.espresso.base.Interrogator.loopAndInterrogate(Interrogator.java:14)
at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:8)
at androidx.test.espresso.base.UiControllerImpl.loopUntil(UiControllerImpl.java:1)
at androidx.test.espresso.base.UiControllerImpl.injectMotionEvent(UiControllerImpl.java:6)
at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:7)
at androidx.test.espresso.action.MotionEvents.sendUp(MotionEvents.java:1)
at androidx.test.espresso.action.Tap.sendSingleTap(Tap.java:5)
at androidx.test.espresso.action.Tap.sendSingleTap$bridge(Unknown Source:0)
at androidx.test.espresso.action.Tap$1.sendTap(Tap.java:3)
at androidx.test.espresso.action.GeneralClickAction.perform(GeneralClickAction.java:6)
at androidx.test.espresso.ViewInteraction$SingleExecutionViewAction.perform(ViewInteraction.java:2)
at androidx.test.espresso.ViewInteraction.doPerform(ViewInteraction.java:25)
at androidx.test.espresso.ViewInteraction.doPerform$bridge(Unknown Source:0)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:2)
at androidx.test.espresso.ViewInteraction$1.call(ViewInteraction.java:1)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

From what I can tell, you're supposed to instead use ActivityScenario and
then test the different activities' states using ActivityScenario.moveToState or ActivityScenario.recreate.

Related

Rock,Paper,Scissors progress of the game in app

so I want to create a Rock Paper Scissors app that should work like this:
In the first screen you enter the names of the 2 players.
In the second screen there are the names of the players and the score of each player near it.The progress is that you need to click a button,and after you click it,in each player's side,random image(rock paper or scissors) will appear and the winner will get a point,or nobody will if its a draw.
NOTE:This might not be an error in the code based on what i've seen when I tried to search for the message i'm getting while debugging so you may want to look at it first.
I would appreciate some comments on the code though.
I checked if the names that i'm passing from the first activity to the second one before I had started to work on the button and the app worked fine.
But after I wrote the code for the OnClickListener, the app just crashes instantly after the first activity when I run it. It is my first time working with images like that so i'm not sure that if used it properly. I have created some functions so the code will be more readable without knowing exactly what i'm doing though because i'm pretty new to android.
first activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startBtn;
startBtn = findViewById(R.id.startBtn);
startBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
EditText p2Name=findViewById(R.id.p2EditText);
EditText p1Name=findViewById(R.id.p1EditText);
String name1=p2Name.getText().toString();
String name2=p1Name.getText().toString();
Intent intent1 = new Intent(MainActivity.this, Game.class);
intent1.putExtra("name1",name1);
intent1.putExtra("name2",name2);
MainActivity.this.startActivity(intent1);
}
});
}
second activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
String p1Name;
String p2Name;
if (getIntent().hasExtra("name1")) {
p1Name = getIntent().getStringExtra("name1");
TextView p1NView = findViewById(R.id.p1);
p1NView.setText(p1Name);
}
if (getIntent().hasExtra("name2")) {
p2Name = getIntent().getStringExtra("name2");
TextView p2NView = findViewById(R.id.p2);
p2NView.setText(p2Name);
}
Button NRound=findViewById(R.id.newRoundBtn);
NRound.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
TextView score1=findViewById(R.id.score1View);
TextView score2=findViewById(R.id.score2View);
int p1Score = Integer.parseInt(score1.getText().toString());
int p2Score = Integer.parseInt(score2.getText().toString());
String[] RPS = new String[]{"rock", "paper", "scissors"};
Random r = new Random();
String p1Hand;
String p2Hand;
p1Hand = RPS[r.nextInt(3)];
p2Hand = RPS[r.nextInt(3)];
showImages(p1Hand,p2Hand);
String result=findWinner(p1Hand,p2Hand);
switch (result){
case "player1":
p1Score++;
score1.setText(String.valueOf(p1Score));
case "player2":
p2Score++;
score2.setText(String.valueOf(p2Score));
if(score1.getText().equals('3') || score2.getText().equals('3')){
Intent end=new Intent(Game.this,EndScreen.class);
Game.this.startActivity(end);
}
}
}
});
update();
}
public static String findWinner(String hand1,String hand2){
if(hand1.equals(hand2)){
return "draw";
}
String both=hand1.concat(hand2);
if(both.equals("rockscissor") || both.equals("paperrock")||both.equals("scissorspaper")){
return "player1";
}else{
return "player2";
}
}
public void showImages(String hand1,String hand2){
ImageView rock1=findViewById(R.id.rock1);
ImageView paper1=findViewById(R.id.paper1);
ImageView scissors1=findViewById(R.id.scissors1);
ImageView rock2=findViewById(R.id.rock2);
ImageView paper2=findViewById(R.id.paper2);
ImageView scissors2=findViewById(R.id.scissors2);
switch (hand1){
case "rock":
rock1.setVisibility(View.VISIBLE);
case "paper":
paper1.setVisibility(View.VISIBLE);
case "scissors":
scissors1.setVisibility(View.VISIBLE);
}
switch (hand2){
case "rock":
rock2.setVisibility(View.VISIBLE);
case "paper":
paper2.setVisibility(View.VISIBLE);
case "scissors":
scissors2.setVisibility(View.VISIBLE);
}
}
public void update(){
ImageView[] images=new ImageView[6];
for (ImageView image:images)
{
if(image.getVisibility()==View.VISIBLE){
image.setVisibility(View.GONE);
}
}
}
Logs
Edit:now after I have posted and seen the logs I understood what was the problem. I forgot to initialize the imageView array in a function and I was just looping over null array,trying to get its visibility.And Now it does not crash.
I didn't know about this so thanks anyways for telling me to post it.(thought errors should show up in the console or something).
Now I am dealing with another problem,I will try to solve it on my own though.
Process: com.example.rockpaperscissors, PID: 11607
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.rockpaperscissors/com.example.rockpaperscissors.Game}: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.widget.ImageView.getVisibility()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3260)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3396)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2009)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7319)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:934)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int android.widget.ImageView.getVisibility()' on a null object reference
at com.example.rockpaperscissors.Game.update(Game.java:71)
at com.example.rockpaperscissors.Game.onCreate(Game.java:65)
at android.app.Activity.performCreate(Activity.java:7783)
at android.app.Activity.performCreate(Activity.java:7772)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1299)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3235)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3396) 
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) 
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2009) 
at android.os.Handler.dispatchMessage(Handler.java:107) 
at android.os.Looper.loop(Looper.java:214) 
at android.app.ActivityThread.main(ActivityThread.java:7319) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:934) 
I tried to debug the app and at the last line of the first activity,the message that shows up is: source code does not match the byte code.
First try to clean the project and rebuild it.
In order for us to help you with the crash, we need to see the logs. Please add that as well.
About your code:
MainActivity.class
Try validating user input before passing it to the next activity.
Game.class (assuming this is also an activity so try renaming it to GameActivity.class)
When you are retrieving data from the intent, it's best practice to create a constant and make it public that you can use in the main activity as well (if you make a typo, it's hard to find the problem)
Something like: public static final String NAME_ONE_KEY = "name1";
I don't see the need of using a string array, identify those actions by integer and comment it so you and others who read your code can understand it.
It seems like you have a lot of child elements in your game layout file
Add the images into the drawable folder, create one image view for each player and update the image source.
Finally, save the state so you don't loose the data on device rotation.

Dialogs from custom classes crash in Android O

I often need user interaction (dialogs) from custom classes that aren't subclasses of Activity or Fragment. Here's an example of how I'm currently doing this. I open the dialog from the fragment manager of the fragment that created the custom class, and I use a small inner class as the target so I can get the dialog result. This keeps all the related code in one place, as opposed to putting onActivityResult in the parent fragment:
public class DocumentViewer extends RelativeLayout {
public void deleteAnnotations() {
DialogFragment dialog = new DialogFragment();
Bundle args = new Bundle();
args.putString("title", this.app.getString(R.string.DELETE_ANNOTATIONS));
args.putString("message", this.app.getString(R.string.CONFIRM_DELETE_ANNOTATIONS));
args.putString("button1Text", this.app.getString(R.string.BUTTON_DELETE));
args.putString("button2Text", this.app.getString(R.string.BUTTON_CANCEL));
dialog.setArguments(args);
DocumentViewerAlertListener listener = new DocumentViewerAlertListener();
listener.canvas = this.canvas;
dialog.setTargetFragment(listener, R.id.confirmDelete);
dialog.show(this.fragment.getFragmentManager(), "confirmDelete");
}
public static class DocumentViewerAlertListener extends ALFragment {
public ALCanvas canvas;
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if ((requestCode == R.id.confirmDelete)&&(resultCode == 1)) {
this.canvas.clearItems();
}
}
}
}
Unfortunately, in Android O, the dialog.show line crashes with this stack trace:
09-20 14:06:12.852 24301-24301/com.bizname.appname E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bizname.appname, PID: 24301
java.lang.IllegalStateException: Fragment ALAlert{1ace4cd #8 confirmDelete} declared target fragment DocumentViewerAlertListener{7e21182} that does not belong to this FragmentManager!
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1209)
at android.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1549)
at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1611)
at android.app.BackStackRecord.executeOps(BackStackRecord.java:807)
at android.app.FragmentManagerImpl.executeOps(FragmentManager.java:2394)
at android.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2189)
at android.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2142)
at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2049)
at android.app.FragmentManagerImpl$1.run(FragmentManager.java:718)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
It makes sense that the fragment manager I'm using doesn't know about this inner class, but what's the alternative? I tried using the inner class as the fragment manager...
dialog.show(listener.getFragmentManager(), "confirmDelete");
...and there was no crash, but the dialog didn't appear, perhaps because the listener fragment is never displayed.
I'm open to either a quick fix for my current approach, or a different approach that accomplishes the same thing (but would prefer a quick fix!).
While writing out the question, I had an idea based on "the listener fragment is never displayed." I added this line before showing the dialog:
this.fragment.getFragmentManager().beginTransaction().add(listener, "confirmDelete").commit();
I assume this lets the fragment manager know about my listener fragment, so it doesn't throw the exception.
This seems like an easy and logical fix, but I'm still open to other approaches.

Bundle not restored in Activity (appcompat 7:25.1 bug)

I have implemented simple drill-down pattern with several activities that pass data to display. But when I go back (either android back button or toolbar's back button) my app crashes in onCreate because parameters are gone.
Ok, there are hundreds similar questions. Please read on. I did investigation and even senior developer reviewed my code and he did not understood.
This is a log how it was called:
TestItemsActivity.onCreate()
TestItemsActivity.onStart()
TestItemsActivity.onResume()
TestItemsActivity.onPause()
RunTestActivity.onCreate()
RunTestActivity.onStart()
RunTestActivity.onResume()
TestItemsActivity.onSaveInstanceState()
TestItemsActivity.onStop()
RunTestActivity.onPause()
TestItemsActivity.onCreate()
The last sentence is important - why is onCreate called again? Activity lifecycle says that onResume is expected.
Let's look at the source code. I use appcompat 7:25.1.
TestItemsActivity
public class TestItemsActivity extends AppCompatActivity {
TestScript script;
protected void onCreate(Bundle state) {
log.debug("onCreate()");
super.onCreate(state);
setContentView(R.layout.act_script_items);
if (state != null) {
script = (TestScript) state.getSerializable(ScriptListActivity.KEY_SCRIPT);
} else {
script = (TestScript) getIntent().getSerializableExtra(ScriptListActivity.KEY_SCRIPT);
}
...
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Intent intent = new Intent(TestItemsActivity.this, RunTestActivity.class);
intent.putExtra(RunTestActivity.KEY_POSITION, position);
intent.putExtra(ScriptListActivity.KEY_SCRIPT, script);
TestItemsActivity.this.startActivity(intent);
}
});
}
protected void onSaveInstanceState(Bundle state) {
state.putSerializable(ScriptListActivity.KEY_SCRIPT, script);
super.onSaveInstanceState(state);
}
AppCompatActivity
public class RunTestActivity extends AppCompatActivity {
protected void onCreate(Bundle state) {
log.debug("onCreate()");
super.onCreate(state);
setContentView(R.layout.act_script_items);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbarCalc);
if (toolbar != null) {
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
moveTaskToBack(true);
}
});
}
Intent intent = getIntent();
script = (TestScript) intent.getSerializableExtra(ScriptListActivity.KEY_SCRIPT);
}
And now the most interesting source code is the manifest:
<activity
android:name=".activities.TestItemsActivity"
android:label="#string/action_academy"
android:parentActivityName=".activities.ScriptListActivity">
</activity>
<activity
android:name=".activities.RunTestActivity"
android:label="#string/action_academy"
android:parentActivityName=".activities.TestItemsActivity">
</activity>
Accidentally I found that if I remove parentActivityName than android's back button does not start new activity (onCreate) and my app does not crashes! Unfortunatelly toolbar's back button disappears.
Some SO questions I read:
Saving Android Activity state using Save Instance State
save the state when back button is pressed
How to save activity state after pressing back button?
Bundle savedInstanceState is always null
SavedInstantState created during onSaveInstanceState not restored in onCreate
Can somebody explains what is going there?
Complete stacktrace including parts that I removed from source code above. It fails in adapter because scripts instance is null. The activity failed to initialize it either from intent or bundle (which is null despite that onSaveInstanceState was called)
java.lang.RuntimeException: Unable to start activity ComponentInfo{lelisoft.com.lelimath.debug/lelisoft.com.lelimath.activities.TestItemsActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List lelisoft.com.lelimath.data.TestScript.getItems()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.util.List lelisoft.com.lelimath.data.TestScript.getItems()' on a null object reference
at lelisoft.com.lelimath.adapter.TestItemAdapter.<init>(TestItemAdapter.java:97)
at lelisoft.com.lelimath.activities.TestItemsActivity.onCreate(TestItemsActivity.java:55)
at android.app.Activity.performCreate(Activity.java:6679)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2618)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 
at android.app.ActivityThread.-wrap12(ActivityThread.java) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
at android.os.Handler.dispatchMessage(Handler.java:102) 
at android.os.Looper.loop(Looper.java:154) 
Update:
Workaround for this problem is to use a theme without a toolbar:
<activity
android:name=".activities.CampaignActivity"
android:label="#string/action_academy"
android:theme="#style/AppTheme.NoActionBar">
</activity>
Unfortunatelly I need the toolbar for one activity.
As you can see from the trace you provided, the activity was stopped and state was saved. Therefore later when activity is to be restored it will call onCreate().
TestItemsActivity.onSaveInstanceState()
TestItemsActivity.onStop()

Android activity and fragment lifecycle issue?

I have the following stack trace for a crash caused by an NPE:
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.daybreak.my.app/com.daybreak.my.app.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2430)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ViewSwitcher.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
at com.daybreak.my.app.TimesFragment.onLocationChange(TimesFragment.java:446)
at com.daybreak.my.app.MainActivity.onLocationChange(MainActivity.java:289)
at com.daybreak.my.app.MainActivity.onCreate(MainActivity.java:112)
at android.app.Activity.performCreate(Activity.java:6302)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2383)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.access$900(ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1358)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5456)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
The way I have setup my app is as follows:
MainActivity
public class MainActivity extends AppCompatActivity implements LocationChangeListener {
#Override
public void onCreate(Bundle savedInstanceState) {
//...
onLocationChange(LocationManager.getSavedLocation(this)); // Manually calling onLocationChange() method
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) return;
showFragment(new TimesFragment(), TimesFragment.TAG);
}
}
#Override
public void onLocationChange(Locatin location) {
if (location == null) return;
//...
// Call attached onLocationChange() if it implements LocationChangeListener
Fragment f = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (f instanceof LocationChangeListener)
((LocationChangeListener) f).onLocationChange(location);
}
}
TimesFragment
public class TimesFragment extends Fragment implements LocationChangeListener {
private ViewSwitcher viewSwitcher;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//...
viewSwitcher = (ViewSwitcher) view.findViewById(R.id.view_switcher);
}
#Override
public void onLocationChange(Location location) {
this.location = location;
viewSwitcher.setOnClickListener(null); //<-- NPE Cause here
updateContent();
}
}
MY EXPECTATION
From what I understand, the Activity.onCreate() will only be called after a fresh start or after the user navigates back to the app after the app has been killed (explicitly by the user or by memory management when other apps need memory). If this happens the fragments will also be destroyed and will need to be created, i.e., fragment's onCreateView() will be called. Therefore calling onLocationChange() from the MainActivity.onCreate() before attaching the fragment is safe as findFragmentById() within the onLocationChange() would not find any fragment.
REALITY
From the stack trace we can see that the call initiated from MainActivity.onCreate(). But what's puzzling for me is that at the time onLocationChange() is called from within MainActivity.onCreate(), findFragmentById() within the onLocationChange() finds the fragment in the view container and calls the fragments onLocationChange(). When this happens the viewSwitcher is NULL and causes the app to crash.
Obviously, fragment has already been added to the view container and the fragments onCreateView() has not been called yet.
THE QUESTION
I am not able to recreate this crash, and not sure of the lifecycle process that is causing this.
So can anyone tell me
how to reproduce this error and
the lifecycle process that is responsible for the flow that is causing the NPE?
This is being caused by device rotation. Can recreate the stack trace by rotating the device.
NOTE: This can happen even when app's orientation is locked (as in my case); if the user is in another app in an orientation that is different than the orientation your app is locked to and they switch back to the app, the orientation lifecycle for your app will be fired.
SOLUTION
Add f != null && f.isResumed() before calling methods from the fragment. isResumed() will return false if fragment hasn't been resumed after recreation.

App is crashing after Home is pressed. Unable to pause - Derived class did not call super.onSaveInstanceState()

When I press the Home button, the app should be paused, save all state and work fine.
Instead I get this error:
java.lang.RuntimeException: Unable to
pause activity
{be.test.tester/be.test.tester.DataScreen}:
java.lang.IllegalStateException:
Derived class did not call
super.onSaveInstanceState() at
android.app.ActivityThread.performPauseActivity(ActivityThread.java:3641)
at
android.app.ActivityThread.performPauseActivity(ActivityThread.java:3598)
at
android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3574)
at
android.app.ActivityThread.access$2500(ActivityThread.java:136)
at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:2186)
at
android.os.Handler.dispatchMessage(Handler.java:99)
at
android.os.Looper.loop(Looper.java:143)
at
android.app.ActivityThread.main(ActivityThread.java:5068)
at
java.lang.reflect.Method.invokeNative(Native
Method) at
java.lang.reflect.Method.invoke(Method.java:521)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:858)
at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at
dalvik.system.NativeStart.main(Native
Method)
Caused by:
java.lang.IllegalStateException:
Derived class did not call
super.onSaveInstanceState() at
android.view.View.dispatchSaveInstanceState(View.java:6087)
at
android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:1207)
at
android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:1207)
at
android.view.View.saveHierarchyState(View.java:6068)
at
com.android.internal.policy.impl.PhoneWindow.saveHierarchyState(PhoneWindow.java:1475)
at
android.app.Activity.onSaveInstanceState(Activity.java:1106)
at
android.app.Activity.performSaveInstanceState(Activity.java:1056)
at
android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1289)
at
android.app.ActivityThread.performPauseActivity(ActivityThread.java:3623)
... 12 more
My activity reacts on touch:
public class DataScreen extends Activity implements OnGestureListener{
I'm getting some extra's from the intent:
totUsage = Integer.parseInt(getIntent().getStringExtra("extraTotUsage"));
limit = Integer.parseInt(getIntent().getStringExtra("extraLimit"));
Bundle bundle = getIntent().getExtras();
mylist = (ArrayList<HashMap<String, String>>) bundle.get("extraMyList");
A custom view is showing data (canvas). When you scroll on screen, data changes in custom view (set, get method) and redraws itself.
I don't really manage the onSaveInstanceState here, don't really know if I have to.
My app is onTop of the stack, because of:
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
I don't understand the error.
You should override onSaveInstanceState and call its super method like shown below. At least it helped me with the same problem.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// your stuff or nothing
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// your stuff or nothing
}
I've just been dealing with this problem. In my app the main activity allows a second activity to be called to display info/instructions etc, it also has a surface view that draws the runtime element of the app.
The main activity has a variable to hold the surface view, if that variable hasn't been set before the info/instructions activity is called I get this error. I simply moved the surface view variable assignment into the main activity onCreate and all is well.
It is also possible that one uses a custom preference (e.g. custom dialog preference) that does not call "super.onSavedInstanceState()" which leads to the same error.

Categories

Resources