My Android Xamarin Forms App is exhibiting a 10 to 30 second lag when creating a ViewModel/View and pushing the View to the modal stack at runtime.
The application is receiving a deep link successfully, but then experiences a considerable performance lag when trying to show the desired view.
The same application executing on IOS does not have this lag.
Looking at the logs and code, the lag happens at the line I've marked with '-->' below, after MT2 but before MT3 log entries.
I have been experimenting with Device.BeginInvokeOnMainThread to see if it makes the problem better or worse, but have seen no effect for this issue.
Any idea what the perf hangup might be?
Code
protected override async void OnAppLinkRequestReceived(Uri uri)
{
//Aside: I would rather use System.Web.HttpUtility to parse this query string, but it seems I can't in an Xam PCL.
//Pattern:
//my.app.deeplink://<command>?<params>
//Examples:
// Deep Linking Web Navigation:
// my.app.deeplink://webnav?url=/ns/products/product/?id=1234¶m1=value1¶m2=value2
// Deep Linking Native Navigation:
// my.app.deeplink://view?name=SettingsPageView¶m1=value1¶m2=value2
try
{
m_oLogging.LogInfo("OnAppLinkRequestReceived: " + uri.ToString());
NavigationManager.DeepLinkCommand oDeepCmd = NavigationManager.ParseDeepLink(uri);
switch (oDeepCmd)
{
case NavigationManager.DeepLinkCommand.invalid:
m_oLogging.LogInfo("Deep Link deemed invalid. Doing nothing.");
break;
case NavigationManager.DeepLinkCommand.webnav:
//Query expected to be like: ?url=/ns/products/product/?id=1234¶m1=value1¶m2=value2
string szUri = uri.Query;
Uri oParsedURI = WebNavigationManager.URLS.ParseDeepLinkUri(szUri);
string szURL = oParsedURI.ToString();
m_oLogging.LogInfo(string.Format("Proceeding with webnav to [{0}].", szURL));
App oApp = ((App)Application.Current);
WebNavigationManager.Instance.NavigateToURL(szURL);
break;
case NavigationManager.DeepLinkCommand.view:
//Query like: ?name=SettingsPageView¶m1=value1¶m2=value2
string szView = uri.Query.Split('=')[1].Split('&')[0];
string szViewTypeName = string.Empty;
switch (szView)
{
case "SettingsPageView":
szViewTypeName = typeof(SettingsPageView).Name;
break;
default:
//Try to dynamically present the named view.
szViewTypeName = "my.app.namespace." + szView;
break;
}
if (ViewIsAlreadyLoaded(szViewTypeName))
{
//I'm already at that page, so chill!
m_oLogging.LogInfo(string.Format("View [{0}] is already loaded in one of the stacks. No need to load it again or duplicate it.", szViewTypeName));
}
else
{
m_oLogging.LogInfo(string.Format("Proceeding with presenting view [{0}].", szViewTypeName));
switch (szView)
{
case "SettingsPageView":
//There is nothing modal right now, or there is, but the top of the stack isn't the settings page.
m_oLogging.LogInfo(string.Format("Pushing known view modally: '{0}'", szViewTypeName));
Device.BeginInvokeOnMainThread(() =>
{
m_oLogging.LogInfo(string.Format("Pushing known view modally (MT1): '{0}'", szViewTypeName));
IViewModel oSPVM = new SettingsPageViewModel();
m_oLogging.LogInfo(string.Format("Pushing known view modally (MT2): '{0}'", szViewTypeName));
--> BaseView oSPV = new SettingsPageView(oSPVM);
m_oLogging.LogInfo(string.Format("Pushing known view modally (MT3): '{0}'", szViewTypeName));
MainPage.Navigation.PushModalAsync(oSPV, false);
m_oLogging.LogInfo(string.Format("Pushed known view modally (MT4): '{0}'", szViewTypeName));
});
m_oLogging.LogInfo(string.Format("Pushed known view modally: '{0}'", szViewTypeName));
break;
default:
//Try to dynamically present the named view.
var vViewModel = Activator.CreateInstance(Type.GetType(szViewTypeName + "Model"));
if (vViewModel != null)
{
var vView = Activator.CreateInstance(Type.GetType(szViewTypeName), vViewModel);
if (vView != null)
{
m_oLogging.LogInfo(string.Format("Pushing dynamic view modally: '{0}'", szViewTypeName));
Device.BeginInvokeOnMainThread(async() => await MainPage.Navigation.PushModalAsync(vView as Page, false));
m_oLogging.LogInfo(string.Format("Pushed dynamic view modally: '{0}'", szViewTypeName));
}
}
break;
}
}
break;
}
}
catch (Exception oExc)
{
m_oLogging.LogError(oExc.Message);
}
base.OnAppLinkRequestReceived(uri);
}
Runtime Debugging content
08-01 15:14:59.023 I/MyApp (29249): OnAppLinkRequestReceived: my.package.name://view/?name=SettingsPageView&Action=Clicked
08-01 15:14:59.030 I/MyApp (29249): Proceeding with presenting view [SettingsPageView].
08-01 15:14:59.031 I/MyApp (29249): Pushing known view modally: 'SettingsPageView'
08-01 15:14:59.031 I/MyApp (29249): Pushed known view modally: 'SettingsPageView'
Thread started: #17
08-01 15:14:59.079 D/Mono (29249): [0xb7190920] worker starting
08-01 15:14:59.153 D/ViewRootImpl@3c0476fMainActivity: Relayout returned: oldFrame=[0,0][1080,2220] newFrame=[0,0][1080,2220] result=0x7 surface={isValid=true -1281830912} surfaceGenerationChanged=true
08-01 15:14:59.153 D/ViewRootImpl@3c0476fMainActivity: mHardwareRenderer.initialize() mSurface={isValid=true -1281830912} hwInitialized=true
08-01 15:14:59.162 I/MyApp (29249): Pushing known view modally (MT1): 'SettingsPageView'
Resolved pending breakpoint at 'SettingsPageViewModel.cs:25,1' to bool app.namespace.name.SettingsPageViewModel.get_PreferencePushService () [0x00001].
Resolved pending breakpoint at 'SettingsPageViewModel.cs:38,1' to bool app.namespace.name.SettingsPageViewModel.get_PreferencePushDelivery () [0x00001].
Resolved pending breakpoint at 'SettingsPageViewModel.cs:51,1' to bool app.namespace.name.SettingsPageViewModel.get_PreferencePushPromotion () [0x00001].
08-01 15:14:59.340 I/MyApp (29249): Pushing known view modally (MT2): 'SettingsPageView'
08-01 15:14:59.488 D/Mono (29249): DllImport searching in: '__Internal' ('(null)').
08-01 15:14:59.488 D/Mono (29249): Searching for 'java_interop_jnienv_call_static_object_method'.
08-01 15:14:59.488 D/Mono (29249): Probing 'java_interop_jnienv_call_static_object_method'.
08-01 15:14:59.488 D/Mono (29249): Found as 'java_interop_jnienv_call_static_object_method'. #############################[ 3 seconds of lag ] 08-01 15:15:02.826 D/TcpOptimizer(29249): [my.package.name] Full closed: sid=125, tcpi_state=8 #############################[ 1 second of lag ] 08-01 15:15:03.830 D/TcpOptimizer(29249): [my.package.name] Full closed: sid=130, tcpi_state=8
Thread finished: #15
The thread 'Unknown' (0xf) has exited with code 0 (0x0). #############################[ 26 seconds of lag ] 08-01 15:15:29.177 I/System.out(29249): (HTTPLog)-Static: isSBSettingEnabled false
08-01 15:15:29.177 I/System.out(29249): (HTTPLog)-Static: isSBSettingEnabled false
08-01 15:15:29.179 I/MyApp (29249): CustomerID: (known) e3ecf5c1-3920-4cc9-ad08-0c1b2145db2b ( <-- this code runs while data is being read from the viewmodel for the view ) 08-01 15:15:29.186 I/System.out(29249): (HTTPLog)-Static: isSBSettingEnabled false 08-01 15:15:29.187 I/System.out(29249): (HTTPLog)-Static: isSBSettingEnabled false 08-01 15:15:29.188 I/System.out(29249): (HTTPLog)-Static: isSBSettingEnabled false 08-01 15:15:29.188 I/System.out(29249): (HTTPLog)-Static: isSBSettingEnabled false Thread started: <Thread Pool> #18
08-01 15:15:29.285 D/Mono (29249): Assembly Loader probing location: '/Users/builder/data/lanes/4009/3a62f1ea/source/monodroid/builds/install/mono-armv7/lib/app.namespace.name.resources.dll'.
08-01 15:15:29.285 D/Mono (29249): Assembly Loader probing location: '/Users/builder/data/lanes/4009/3a62f1ea/source/monodroid/builds/install/mono-armv7/lib/app.namespace.name.resources.exe'.
08-01 15:15:29.291 D/Mono (29249): Assembly Loader probing location: '/storage/emulated/0/Android/data/my.package.name/files/.override/en-US/app.namespace.name.resources.dll'.
08-01 15:15:29.364 I/MyApp (29249): Pushing known view modally (MT3): 'SettingsPageView'
08-01 15:15:29.428 W/art (29249): JNI RegisterNativeMethods: attempt to register 0 native methods for md5b60ffeb829f638581ab2bb9b1a7f4f3f.LabelRenderer
08-01 15:15:29.646 W/art (29249): JNI RegisterNativeMethods: attempt to register 0 native methods for md5b6ed58bdd2dc5ff75bde29ef5e42d6e8.ExtendedSwitchRenderer
08-01 15:15:30.001 I/MyApp (29249): Pushed known view modally (MT4): 'SettingsPageView'
08-01 15:15:30.002 I/Choreographer(29249): Skipped 1851 frames! The application may be doing too much work on its main thread.
08-01 15:15:30.111 D/ScrollView(29249): onsize change changed
08-01 15:15:30.189 D/ViewRootImpl@3c0476fMainActivity: MSG_WINDOW_FOCUS_CHANGED 1
08-01 15:15:30.189 D/ViewRootImpl@3c0476fMainActivity: mHardwareRenderer.initializeIfNeeded()#2 mSurface={isValid=true -1281830912}
08-01 15:15:30.229 V/InputMethodManager(29249): Starting input: tba=android.view.inputmethod.EditorInfo@cf0b2ac nm : my.package.name ic=null
08-01 15:15:30.230 I/InputMethodManager(29249): [IMM] startInputInner - mService.startInputOrWindowGainedFocus
08-01 15:15:30.231 D/InputTransport(29249): Input channel constructed: fd=160
08-01 15:15:30.233 D/InputMethodManager(29249): HSI from window - flag : 0 Pid : 29249
08-01 15:15:30.252 W/IInputConnectionWrapper(29249): reportFullscreenMode on inexistent InputConnection
08-01 15:15:38.190 D/Mono (29249): [0xb7190920] worker finishing
Thread finished: #17
The thread 'Unknown' (0x11) has exited with code 0 (0x0).
08-01 15:15:41.995 D/TcpOptimizer(29249): [my.package.name] Full closed: sid=171, tcpi_state=8
08-01 15:15:49.902 D/ViewRootImpl@3c0476fMainActivity: MSG_RESIZED_REPORT: frame=Rect(0, 0 - 1080, 2220) ci=Rect(0, 72 - 0, 144) vi=Rect(0, 72 - 0, 144) or=1
08-01 15:15:49.972 D/ViewRootImpl@3c0476fMainActivity: Relayout returned: oldFrame=[0,0][1080,2220] newFrame=[0,0][1080,2220] result=0x1 surface={isValid=true -1281830912} surfaceGenerationChanged=false
08-01 15:15:51.378 D/ViewRootImpl@3c0476fMainActivity: MSG_WINDOW_FOCUS_CHANGED 0
08-01 15:15:54.111 D/ViewRootImpl@3c0476fMainActivity: MSG_WINDOW_FOCUS_CHANGED 1
08-01 15:15:54.111 D/ViewRootImpl@3c0476fMainActivity: mHardwareRenderer.initializeIfNeeded()#2 mSurface={isValid=true -1281830912}
08-01 15:16:09.240 D/Mono (29249): [0xbf27c920] worker finishing
Thread finished: #10
The thread 'Unknown' (0xa) has exited with code 0 (0x0).
Any assistance is appreciated!