I'm experiencing a crash in my Titanium 2.0.2 GA mobile app on Android where reopening a Tab Group is crashing with a NullPointerException.
The symptoms are very much similar to these Jira's I've found but I've tested the test code in those Jiras and it does not crash, however, my code crashes every single time I test it.
- http://developer.appcelerator.com/question/131195/reopen-tabgroup-causes-activity-reference-is-invalid-removing-from-activity-stack-nullpointerexception
- https://jira.appcelerator.org/browse/TIMOB-7573
- https://jira.appcelerator.org/browse/TIMOB-7240
- https://jira.appcelerator.org/browse/TIMOB-7572
I have confirmed that the above jiras, using the test code provided, are indeed fixed.
My application is a collection of modules (messages, contacts, maps and calendar), and each module is configured into an array. The app then creates the modules from the array and stores the returned window objects in another array which is processed when constructing the home screen of icons. Clicking on an icon on the home screen opens the window of that module.
When you click on the Contacts module - the only one with a tabgroup, the application opens the tabgroup, but if you go back to the home screen and then click on Contacts again, it crashes.
The exception is as follows:
[DEBUG][AndroidRuntime( 345)] Shutting down VM
[WARN][dalvikvm( 345)] threadid=1: thread exiting with uncaught exception (group=0x4001d800)
[ERROR][TiApplication( 345)] (main) [1551,84953] Sending event: exception on thread: main msg:java.lang.NullPointerException; Titanium 2.0.2,2012/05/30 10:21,2ff31a3
[ERROR][TiApplication( 345)] java.lang.NullPointerException
[ERROR][TiApplication( 345)] at android.content.ComponentName.<init>(ComponentName.java:75)
[ERROR][TiApplication( 345)] at android.content.Intent.<init>(Intent.java:2678)
[ERROR][TiApplication( 345)] at ti.modules.titanium.ui.TabGroupProxy.handleOpen(TabGroupProxy.java:293)
[ERROR][TiApplication( 345)] at org.appcelerator.titanium.proxy.TiWindowProxy.handleMessage(TiWindowProxy.java:100)
[ERROR][TiApplication( 345)] at ti.modules.titanium.ui.TabGroupProxy.handleMessage(TabGroupProxy.java:104)
[ERROR][TiApplication( 345)] at android.os.Handler.dispatchMessage(Handler.java:95)
[ERROR][TiApplication( 345)] at android.os.Looper.loop(Looper.java:123)
[ERROR][TiApplication( 345)] at android.app.ActivityThread.main(ActivityThread.java:4627)
[ERROR][TiApplication( 345)] at java.lang.reflect.Method.invokeNative(Native Method)
[ERROR][TiApplication( 345)] at java.lang.reflect.Method.invoke(Method.java:521)
[ERROR][TiApplication( 345)] at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
[ERROR][TiApplication( 345)] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
[ERROR][TiApplication( 345)] at dalvik.system.NativeStart.main(Native Method)
[ERROR][AndroidRuntime( 345)] FATAL EXCEPTION: main
[ERROR][AndroidRuntime( 345)] java.lang.NullPointerException
[ERROR][AndroidRuntime( 345)] at android.content.ComponentName.<init>(ComponentName.java:75)
[ERROR][AndroidRuntime( 345)] at android.content.Intent.<init>(Intent.java:2678)
[ERROR][AndroidRuntime( 345)] at ti.modules.titanium.ui.TabGroupProxy.handleOpen(TabGroupProxy.java:293)
[ERROR][AndroidRuntime( 345)] at org.appcelerator.titanium.proxy.TiWindowProxy.handleMessage(TiWindowProxy.java:100)
[ERROR][AndroidRuntime( 345)] at ti.modules.titanium.ui.TabGroupProxy.handleMessage(TabGroupProxy.java:104)
[ERROR][AndroidRuntime( 345)] at android.os.Handler.dispatchMessage(Handler.java:95)
[ERROR][AndroidRuntime( 345)] at android.os.Looper.loop(Looper.java:123)
[ERROR][AndroidRuntime( 345)] at android.app.ActivityThread.main(ActivityThread.java:4627)
[ERROR][AndroidRuntime( 345)] at java.lang.reflect.Method.invokeNative(Native Method)
[ERROR][AndroidRuntime( 345)] at java.lang.reflect.Method.invoke(Method.java:521)
[ERROR][AndroidRuntime( 345)] at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
[ERROR][AndroidRuntime( 345)] at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
[ERROR][AndroidRuntime( 345)] at dalvik.system.NativeStart.main(Native Method)
[WARN][ActivityManager( 62)] Force finishing activity au.edu.csu.mobile/org.appcelerator.titanium.TiActivity
[WARN][ActivityManager( 62)] Activity pause timeout for HistoryRecord{45126978 au.edu.csu.mobile/org.appcelerator.titanium.TiActivity}
[INFO][Process ( 345)] Sending signal. PID: 345 SIG: 9
[INFO][ActivityManager( 62)] Process au.edu.csu.mobile (pid 345) has died.
[INFO][WindowManager( 62)] WIN DEATH: Window{4512a1f8 au.edu.csu.mobile/org.appcelerator.titanium.TiActivity paused=false}
[WARN][ActivityManager( 62)] Activity destroy timeout for HistoryRecord{45126978 au.edu.csu.mobile/org.appcelerator.titanium.TiActivity}
I think I've narrowed it down to either a bug still in Ti's code or how I'm calling my TabGroup window as I've reduced my code to the barest, simplest tabgroup one can imagine...
What I experience is that with a basic button to close the tabgroup, it doesn't crash. I can close and reopen the tabgroup to my heart's content. Hoever, if I use the Android back button, then reopen the tabgroup, that's when it crashes.
Additionally, I notice that (you can see in the code below) that the background colour of the tabgroup is set to blue, but the second time I reopen it (after using the close button of course) the background changes to black...
The code that sets up each module is here:
csu.module.moduleManager = {};
// Loop through the list of modules and include the
// main modulename.js file if enabled.
var modules = csu.modules;
var config = csu.config;
for (var i = 0; i < modules.length; i++) {
var _module = modules[i];
if (_module.enabled) {
Ti.include(config.moduleBasePath + _module.name + "/" + _module.name + '.js');
csu.module.moduleManager[_module.name] = createModuleWindow(_module.name);
Ti.API.debug('Adding module: [' + _module.name + ']');
}
}
function createModuleWindow(moduleName) {
var window = csu.module[moduleName].createMainWindow();
return window;
};
The aforementioned createMainWindow() function is here:
csu.module.contacts = {};
csu.module.contacts.createMainWindow = function() {
var tabGroup = Ti.UI.createTabGroup();
var tabWin = Ti.UI.createWindow({
exitOnClose: false,
title: 'blue',
backgroundColor: 'blue'
});
var tabButton = Ti.UI.createButton({
title: 'close'
});
var tab = Ti.UI.createTab({
title: 'blue',
window: tabWin
});
tabWin.add(tabButton);
tabButton.addEventListener('click', function(e){
tabGroup.close();
});
var label = Ti.UI.createLabel({
text: 'I am a tabgroup',
top: 20
});
tabWin.add(label);
tabGroup.addTab(tab);
tabGroup.name = 'Contacts_Main_Window';
return tabGroup;
};
And the code to open the tabgroup is here:
menuItem.addEventListener('click', function(e) {
var moduleWindow = csu.module.moduleManager[module.name];
Ti.API.debug(moduleWindow);
// If the module requires login and we're not logged in, stop them!
if (module.verifyLogin && csu.app.session.isValidSession()) {
moduleWindow.open();
} else if (!module.verifyLogin) {
Ti.API.debug('Opening Module Window: ' + module.name);
Ti.API.debug('Module Name: ' + moduleWindow.name);
moduleWindow.open();
Ti.API.debug('Module Opened');
} else {
// Not logged in...
var loginAlert = Ti.UI.createAlertDialog({
buttonNames: ['OK', 'Settings'],
cancel: 0,
message: 'You must be logged in to access this section.'
});
loginAlert.addEventListener('click', function (e) {
if (e.cancel === e.index || e.cancel === true) {
return;
}
switch (e.index) {
case 1:
var settingsWindow = csu.ui.createLoginDetailsPane();
settingsWindow.open({modal: true});
break;
default:
break;
}
});
loginAlert.show();
}
});
Some screenshots to illustrate the scenario:
- Home screen: http://i.imgur.com/Wcu7gl.png
- After clicking on Contacts: http://i.imgur.com/Db2HDl.png
- After clicking close and then Contacts again: http://i.imgur.com/QKfryl.png
- Crash after pressing the Back button and then Contacts again: http://i.imgur.com/weGcll.png
I've checked that the window object does still exist when reopening it, I've confirmed that custom parameters still exist, beyond that, I'm stuck.
This problem has been driving me up the wall for the last 3 days and I really need to resolve it. If anyone has any insights into:
- What might be causing this,
- How to resolve it
- Or at least; What the Component and Intent parts of the error stack trace might refer to
Would be massively appreciated.
Update: I've tried the workaround that was suggested but I still get the crash. The simple tabgroup test I have only has one tab in it. When I add a second tab, go to that tab and then press the hardware back button before returning to Contacts, it crashes.
Titanium.UI.currentWindow.addEventListener('android:back',function(e) { Ti.API.info('back button pressed'); currentWindow.close(); });
– Muhammad Zeeshan