[TIMOB-3208] Android: Heavyweight Windows Opened by Menu Items Often Cause Null Pointer Exceptions
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | High |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2011-12-28T11:19:53.000+0000 |
Affected Version/s | Release 1.7.2 |
Fix Version/s | Release 1.7.0, Sprint 2011-10, Sprint 2011-32, Release 1.8.0, Sprint 2011-52, Release 2.0.0, Release 1.8.1 |
Components | Android |
Labels | module_window, qe-testadded, regression, release-1.7.0, reported-1.6.0 |
Reporter | Dawson Toth |
Assignee | Allen Yeung |
Created | 2011-04-15T03:39:29.000+0000 |
Updated | 2012-02-03T15:50:33.000+0000 |
Description
Problem
If you open a heavyweight window when the user touches a menu item, you can easily cause a null pointer exception to be thrown and the app to crash.
Theory
Based on the error message, we're hitting the NPE in our call to create a new intent on line 528 of TiUIWindow.java. Because TiActivity.class is not null, activity must be null. This is set on line 185 of the same file, and passed to the createIntent method. My theory is that there is an interim period between when the menu closes and the window regains focus during which proxy.getTiContext().getActivity() will return null, and cause the NPE.
Sample Code
Drop the following in an app.js:
// open a heavyweight window
var win = Titanium.UI.createWindow({ backgroundColor: '#fff', fullscreen: true });
win.add(Ti.UI.createLabel({ text: 'Press your hardware menu button!' }));
win.open();
var modal = Ti.UI.createWindow();
// keep a pointer to the current activity
var currentActivity = win.activity;
// and make an options menu
currentActivity.onCreateOptionsMenu = function(e) {
// this menu item will effectively work off "proxy.getTiContext().getActivity()",
// which can be null if the timing is just right when the menu closes and the modal opens
e.menu.add({ title: 'Crashes Often' }).addEventListener('click', function() {
// simulate some heavy activity to get the modal ready; having this busy
// wait present is what makes this bug reproducible
for (var i = new Date().getTime() + 1000; i > new Date().getTime();) {
}
modal.open({ modal: true });
setTimeout(function() {
modal.close();
}, 50);
});
// but we explicitly set the activity in this one to our outer activity (the main activity)
// so it will never be null
var intent = Ti.Android.createIntent({
url: 'loginModalWindow.js'
});
intent.putExtra('modal', true);
e.menu.add({ title: 'Never Crashes' }).addEventListener("click", function() {
// simulate some heavy activity; it won't make a difference
for (var i = new Date().getTime() + 1000; i > new Date().getTime();) {
}
// start the activity; it will fire off without a problem
currentActivity.startActivity(intent);
});
};
now drop the following into your tiapp.xml:
<android xmlns:android="http://schemas.android.com/apk/res/android">
<activities>
<activity url="loginModalWindow.js" android:theme="@android:style/Theme.Dialog" />
</activities>
</android>
and finally drop the following in loginModalWindow.js:
var win = Ti.UI.currentWindow;
win.modal = true;
setTimeout(function() {
win.close();
}, 50)
This sample app has two menu items, named "Crashes Often" and "Never Crashes". They both launch modal windows that close themselves right away. The difference is the method they use. "Crashes Often" uses the Titanium way of opening a modal window. "Never Crashes" uses a more native Android way to open the modal, and is not reliant on Titanium automagically determining the current activity that should launch our modal activity. Note that you may need to try "Crashes Often" a couple of times before the app will freeze, crash, and you'll see the NPE in your logs.
Exception
E/TiUncaughtHandler(11317): (main) [1236,5053] Sending event: exception on thread: main msg:java.lang.NullPointerException; Titanium 1.7.0,2011/02/18 18:13,16c2c7
E/TiUncaughtHandler(11317): java.lang.NullPointerException
E/TiUncaughtHandler(11317): at android.content.ComponentName.<init>(ComponentName.java:75)
E/TiUncaughtHandler(11317): at android.content.Intent.<init>(Intent.java:2652)
E/TiUncaughtHandler(11317): at ti.modules.titanium.ui.TiUIWindow.createIntent(TiUIWindow.java:528)
E/TiUncaughtHandler(11317): at ti.modules.titanium.ui.TiUIWindow.createNewActivity(TiUIWindow.java:186)
E/TiUncaughtHandler(11317): at ti.modules.titanium.ui.TiUIWindow.<init>(TiUIWindow.java:110)
E/TiUncaughtHandler(11317): at ti.modules.titanium.ui.WindowProxy.handleOpen(WindowProxy.java:98)
E/TiUncaughtHandler(11317): at org.appcelerator.titanium.proxy.TiWindowProxy.handleMessage(TiWindowProxy.java:67)
E/TiUncaughtHandler(11317): at ti.modules.titanium.ui.WindowProxy.handleMessage(WindowProxy.java:85)
E/TiUncaughtHandler(11317): at android.os.Handler.dispatchMessage(Handler.java:95)
E/TiUncaughtHandler(11317): at android.os.Looper.loop(Looper.java:123)
E/TiUncaughtHandler(11317): at android.app.ActivityThread.main(ActivityThread.java:4370)
E/TiUncaughtHandler(11317): at java.lang.reflect.Method.invokeNative(Native Method)
E/TiUncaughtHandler(11317): at java.lang.reflect.Method.invoke(Method.java:521)
E/TiUncaughtHandler(11317): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:862)
E/TiUncaughtHandler(11317): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
E/TiUncaughtHandler(11317): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(11317): Uncaught handler: thread main exiting due to uncaught exception
Tested On
Titanium SDK version: 1.7.0 (02/18/11 18:13 316c2c7)
BROKEN on Android EPIC 4G 2.1
Associated Helpdesk Ticket
http://developer.appcelerator.com/helpdesk/view/75121">http://developer.appcelerator.com/helpdesk/view/75121
Attachments
File | Date | Size |
---|---|---|
app.zip | 2011-08-10T12:22:33.000+0000 | 1205 |
resources.zip | 2011-04-15T03:39:29.000+0000 | 825520 |
Workaround
Incidentally... there is a workaround that I did not explicitly mention. If you take a more manual approach to launching modal windows, the NPE can be avoided entirely. Take a look at the "Never Crashes" menu item and at what it is doing.
Spawned #3337 in order to address issue with HW windows not setting the opened state correctly when closing. This applies to non modal windows.
Upon further investigation, #3337 has been set to invalid and fix will be attached to this ticket.
(from [56ff1de427f78c3e5e69b719a005e1deb102d611]) [#3208 state:fixed-in-qa] Make sure opened flag is set correctly when closing HW window
opened window was not being set correctly upon HW window close. This was resulting in the incorrect conext being associated with the window proxy and would sometimes resul in an exception when opening the window a second time.
https://github.com/appcelerator/titanium_mobile/commit/56ff1de427f78c3e5e69b719a005e1deb102d611"> https://github.com/appcelerator/titanium_mobile/commit/56ff1de427f7...
Please use attached resources directory (modified version of original test) to verify fix. While this fix resolves the reported exception, it should be noted that there is a delay when closing a modal window that is opened from the options menu. The onDestroy logic does not actually fire for the window until the menu is opened again or there are activity changes such as orientation change.
During testing this behavior was seen to not have any negative side effects but still documenting here in case this info needs to be referenced in the future.
Tested with Titanium SDK version: 1.7.0 (03/23/11 09:50 87caf1e...) on
Emulator 2.1
Nexus S 2.3.2
No longer catching exception
Works as expected as on 1.7.1 version=1.7.1 timestamp=06/21/11 14:28 githash=1293a6d However pressing 'Never Crashes' causes a force close on 1.7.2 and the latest 1.8.0 build: "The application BugTest (process com.appcelerator.bugtest) has stopped unexpectedly. Please try again." version=1.7.2 timestamp=07/21/11 09:36 githash=97c3689 version=1.8.0 timestamp=07/25/11 14:29 githash=65a5393... Tested on a Droid running Android 2.2.2
Please look into this.
Issue reported is a duplicated of #4643. Please refer to #4643 for the work around. In short, the theme specified does not support the features that the custom activity is trying to utilize. In order to address this situation, navBarHidden should be set to true when launching the new activity. I have provided an example of the work around.
work around attached for the the problem introduced by setting theme on custom activity
Tested with 1.8.0.1.RC3 with rhino/v8, not able to invoke the menu, spoke to Allen, he said he will try to reproduce first before kicking it back. May be an issue with the test case
Closing bug. Verified fix on: SDK build: 1.9.0.v20120112153134 Runtime: V8, Rhino Titanium Studio, build: 1.0.8.201201122152 Device: Droid 3 (2.3.4)