Uploaded image for project: 'Titanium SDK/CLI'
  1. Titanium SDK/CLI
  2. TIMOB-26890

Android: Switch between TabGroup tabs 1 and 4 sometimes crashes the app as of 8.0.0

    Details

    • Story Points:
      7
    • Sprint:
      2019 Sprint 8, 2019 Sprint 9, 2019 Sprint 10

      Description

      Summary:
      As of Titanium 8.0.0, a TabGroup with 4 or more tabs can sometimes crash when switching between tab 1 and 4. This depends on the content within the tab.

      This is a regression.

      Steps to reproduce:

      1. Build and run the below code on Android.
      2. Tap on "Tab 5".
      3. Tap on "Tab 1".
      4. Notice the app crashes/hangs.

      function createDayView(e) {
      	var dayView = Titanium.UI.createView({
      		height: '80dip',
      		width: '66dip',
      		top: '0dip',
      		backgroundColor: '#FFF'
      	});
      	var boxView = Titanium.UI.createView({
      		height: '56dip',
      		width: '56dip',
      		top: '10dip',
      		right: '10dip',
      		touchEnabled: false
      	});
      	dayView.add(boxView);
      	var date = new Date(e.date);
      	var backgroundView = Ti.UI.createView({
      		height: '56dip',
      		width: '56dip',
      		borderRadius: '14dip',
      		backgroundColor: '#AAA',
      		touchEnabled: false
      	});
      	boxView.add(backgroundView);
      	var verticalView = Ti.UI.createView({
      		height: Ti.UI.SIZE,
      		width: Ti.UI.SIZE,
      		layout: 'vertical'
      	});
      	boxView.add(verticalView);
      	verticalView.add(Titanium.UI.createLabel({
      		height: Ti.UI.SIZE,
      		width: Ti.UI.SIZE,
      		text: date.getDate(),
      		ellipsize: Titanium.UI.TEXT_ELLIPSIZE_TRUNCATE_END,
      		textAlign: Titanium.UI.TEXT_ALIGNMENT_CENTER,
      		verticalAlign: Titanium.UI.TEXT_VERTICAL_ALIGNMENT_CENTER,
      		font: { fontSize: '24sp', fontWeight: 'bold', fontFamily: 'Tahoma' },
      		color: '#FFF',
      		touchEnabled: false
      	}));
      	verticalView.add(Titanium.UI.createLabel({
      		height: Ti.UI.SIZE,
      		width: Ti.UI.SIZE,
      		text: 'OK',
      		horizontalWrap: false,
      		ellipsize: Titanium.UI.TEXT_ELLIPSIZE_TRUNCATE_END,
      		textAlign: Titanium.UI.TEXT_ALIGNMENT_CENTER,
      		verticalAlign: Titanium.UI.TEXT_VERTICAL_ALIGNMENT_CENTER,
      		font: { fontSize: '10sp', fontWeight: 'bold', fontFamily: 'Tahoma' },
      		color: '#FFF',
      		touchEnabled: false
      	}));
      	return (dayView);
      }
       
      function createMenuView(e) {
      	var menuView = Titanium.UI.createView({
      		height: '80dip',
      		top: '0dip',
      		backgroundColor: '#FFF'
      	});
      	var scrollView = Titanium.UI.createScrollView({
      		scrollType: 'horizontal',
      		layout: 'horizontal'
      	});
      	menuView.add(scrollView);
      	var dayViewData = [];
      	for (var i = 0; i < 12; i++) {
      		var date = new Date();
      		date.setDate(date.getDate() + i);
      		var dayView = createDayView({
      			date: date
      		});
      		dayViewData.push(dayView);
      		scrollView.add(dayView);
      	}
      	return (menuView);
      }
       
      function createTabView(win) {
      	var containerView = Titanium.UI.createView({
      		top: '80dip',
      		bottom: '0dip',
      		backgroundColor: '#FFF'
      	});
      	win.add(containerView);
      	var tableData = [];
      	var menuView = createMenuView();
      	win.add(menuView);
      	var tableView = Titanium.UI.createTableView({
      		backgroundColor: '#FFF',
      		separatorStyle: Titanium.UI.TABLE_VIEW_SEPARATOR_STYLE_SINGLE_LINE,
      		tableSeparatorInsets: { left: '0dip', right: '0dip' }
      	});
      	var section1 = Ti.UI.createTableViewSection();
      	var section2 = Ti.UI.createTableViewSection();
      	tableData.push(section1);
      	tableData.push(section2);
      	tableView.data = tableData;
      	containerView.add(tableView);
      	var iconView = Titanium.UI.createView({
      		height: '60dip',
      		width: '60dip',
      		right: '25dip',
      		bottom: '25dip',
      		zIndex: 100000
      	});
      	iconView.add(Titanium.UI.createImageView({
      		height: '60dip',
      		width: '60dip',
      		touchEnabled: false
      	}));
      	win.add(iconView);
      };
       
      var window1 = Ti.UI.createWindow();
      var window2 = Ti.UI.createWindow();
      var window3 = Ti.UI.createWindow();
      var window4 = Ti.UI.createWindow();
      var window5 = Ti.UI.createWindow();
      var tab1 = Ti.UI.createTab({
      	title: 'Tab 1',
      	window: window1
      });
      var tab2 = Ti.UI.createTab({
      	title: 'Tab 2',
      	window: window2
      });
      var tab3 = Ti.UI.createTab({
      	title: 'Tab 3',
      	window: window3
      });
      var tab4 = Ti.UI.createTab({
      	title: 'Tab 4',
      	window: window4
      });
      var tab5 = Ti.UI.createTab({
      	title: 'Tab 5',
      	window: window5
      });
      var tabGroup = Titanium.UI.createTabGroup({
      	tabs: [tab1, tab2, tab3, tab4, tab5]
      });
      tabGroup.addEventListener('open', function () {
      	createTabView(window1);
      	createTabView(window2);
      	createTabView(window3);
      	createTabView(window4);
      	createTabView(window5);
      });
      tabGroup.open();
      

      Result:

      [ERROR] :  TiExceptionHandler: (main) [15051,20921] android.view.AbsSavedState$1 cannot be cast to android.widget.AbsListView$SavedState
      [ERROR] :  TiExceptionHandler:
      [ERROR] :  TiExceptionHandler:     android.widget.AbsListView.onRestoreInstanceState(AbsListView.java:1879)
      [ERROR] :  TiExceptionHandler:     android.view.View.dispatchRestoreInstanceState(View.java:15633)
      [ERROR] :  TiExceptionHandler:     android.view.ViewGroup.dispatchThawSelfOnly(ViewGroup.java:3288)
      [ERROR] :  TiExceptionHandler:     android.widget.AdapterView.dispatchRestoreInstanceState(AdapterView.java:817)
      [ERROR] :  TiExceptionHandler:     android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3274)
      [ERROR] :  TiExceptionHandler:     android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3274)
      [ERROR] :  TiExceptionHandler:     android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3274)
      [ERROR] :  TiExceptionHandler:     android.view.View.restoreHierarchyState(View.java:15611)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.Fragment.restoreViewState(Fragment.java:415)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1454)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:792)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2596)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2383)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2338)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.FragmentManagerImpl.execSingleAction(FragmentManager.java:2215)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.BackStackRecord.commitNowAllowingStateLoss(BackStackRecord.java:649)
      [ERROR] :  TiExceptionHandler:     android.support.v4.app.FragmentPagerAdapter.finishUpdate(FragmentPagerAdapter.java:145)
      [ERROR] :  TiExceptionHandler:     android.support.v4.view.ViewPager.populate(ViewPager.java:1238)
      [ERROR] :  TiExceptionHandler:     android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:663)
      [ERROR] :  TiExceptionHandler:     android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:625)
      [ERROR] :  TiExceptionHandler:     android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:617)
      [ERROR] :  TiExceptionHandler:     ti.modules.titanium.ui.widget.tabgroup.TiUIAbstractTabGroup.selectTab(TiUIAbstractTabGroup.java:312)
      [ERROR] :  TiExceptionHandler:     ti.modules.titanium.ui.widget.tabgroup.TiUIBottomNavigationTabGroup.onMenuItemClick(TiUIBottomNavigationTabGroup.java:302)
      [ERROR] :  TiExceptionHandler:     android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:167)
      [ERROR] :  TiExceptionHandler:     android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:973)
      [ERROR] :  TiExceptionHandler:     android.support.design.internal.BottomNavigationMenuView$1.onClick(BottomNavigationMenuView.java:95)
      [ERROR] :  TiExceptionHandler:     android.view.View.performClick(View.java:5609)
      [ERROR] :  TiExceptionHandler:     android.view.View$PerformClick.run(View.java:22259)
      [ERROR] :  TiExceptionHandler:     android.os.Handler.handleCallback(Handler.java:751)
      [ERROR] :  TiExceptionHandler:     android.os.Handler.dispatchMessage(Handler.java:95)
      [ERROR] :  TiExceptionHandler:     android.os.Looper.loop(Looper.java:154)
      [ERROR] :  TiExceptionHandler:     android.app.ActivityThread.main(ActivityThread.java:6077)
      [ERROR] :  TiExceptionHandler:     java.lang.reflect.Method.invoke(Native Method)
      [ERROR] :  TiExceptionHandler:     com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
      [ERROR] :  TiExceptionHandler:     com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
      

      Cause:
      This crash happens due to view ID collision as described by TIMOB-17089 when a tab is being "restored" by the Android OS. Note that the view ID collision has been an issue before 8.0.0. The reason this tab selection crash regression is happening in 8.0.0 is due to the code refactoring where tabs are now implemented via a Java ViewPager whose default offscreen page limit is set to 1. This means the 8.0.0 TabGroup is only keep the tab on the left and the right of the current tab in memory. All other tabs are destroyed or are not created in memory until you tap on a tab farther in the group. Previously created destroyed tabs that are later "restored" cause the crash shown above.

      Recommended Solution:
      Resolve the TIMOB-17089 issue by assigned unique IDs to all views to avoid collision.
      Also, we should bump up the ViewPager limit to a higher value to avoid the tab destroy/restore behavior so that they won't lose their current state.

        Attachments

          Activity

            People

            • Assignee:
              jquick Joshua Quick
              Reporter:
              andreas.pingas Andreas Pingas
              Reviewer:
              Yordan Banev
              Tester:
              Lokesh Choudhary
            • Watchers:
              3 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Backbone Issue Sync

                • Titanium SDK/CLI <> Titanium Mobile
                  Synced with:
                  TIMOB-21087
                  Sync status:
                  ERROR
                  Last received:
                  Last sent:

                  Git Source Code