Skip to content

Commit 0d34dda

Browse files
authored
Merge pull request #87 from winnerspiros/fix-android-startup-crash-8348821197718379664
fix(android): Prevent startup crash by robustly loading rulesets and native libs
2 parents 7eabf41 + 3adad3d commit 0d34dda

1 file changed

Lines changed: 53 additions & 28 deletions

File tree

osu.Android/OsuGameActivity.cs

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414
namespace osu.Android
1515
{
1616
[global::Android.App.Activity(ConfigurationChanges = global::Android.Content.PM.ConfigChanges.Orientation | global::Android.Content.PM.ConfigChanges.ScreenSize | global::Android.Content.PM.ConfigChanges.UiMode, Exported = true, LaunchMode = global::Android.Content.PM.LaunchMode.SingleInstance, MainLauncher = true)]
17-
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
18-
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
19-
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataPathPattern = ".*\\\\.osr", DataHost = "*", DataMimeType = "*/*")]
17+
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataPathPattern = ".*\\.osz", DataHost = "*", DataMimeType = "*/*")]
18+
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataPathPattern = ".*\\.osk", DataHost = "*", DataMimeType = "*/*")]
19+
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataPathPattern = ".*\\.osr", DataHost = "*", DataMimeType = "*/*")]
2020
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
2121
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
2222
[global::Android.App.IntentFilter(new[] { "android.intent.action.VIEW" }, Categories = new[] { "android.intent.category.DEFAULT" }, DataScheme = "content", DataMimeType = "application/x-osu-replay")]
@@ -159,20 +159,29 @@ protected override void OnCreate(global::Android.OS.Bundle? savedInstanceState)
159159
// reference: https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)
160160
handleIntent(Intent);
161161

162-
Debug.Assert(Window != null);
163-
164-
Window.AddFlags(global::Android.Views.WindowManagerFlags.Fullscreen);
165-
Window.AddFlags(global::Android.Views.WindowManagerFlags.KeepScreenOn);
166-
167-
Debug.Assert(WindowManager?.DefaultDisplay != null);
168-
Debug.Assert(Resources?.DisplayMetrics != null);
162+
if (Window != null)
163+
{
164+
Window.AddFlags(global::Android.Views.WindowManagerFlags.Fullscreen);
165+
Window.AddFlags(global::Android.Views.WindowManagerFlags.KeepScreenOn);
166+
}
167+
else
168+
{
169+
global::Android.Util.Log.Warn("OsuGameActivity", "Window is null in OnCreate, flags not set.");
170+
}
169171

170-
global::Android.Graphics.Point displaySize = new global::Android.Graphics.Point();
172+
if (WindowManager?.DefaultDisplay != null && Resources?.DisplayMetrics != null)
173+
{
174+
global::Android.Graphics.Point displaySize = new global::Android.Graphics.Point();
171175
#pragma warning disable CA1422 // GetSize is deprecated
172-
WindowManager.DefaultDisplay.GetSize(displaySize);
176+
WindowManager.DefaultDisplay.GetSize(displaySize);
173177
#pragma warning restore CA1422
174-
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
175-
IsTablet = smallestWidthDp >= 600f;
178+
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
179+
IsTablet = smallestWidthDp >= 600f;
180+
}
181+
else
182+
{
183+
global::Android.Util.Log.Warn("OsuGameActivity", "WindowManager.DefaultDisplay or Resources.DisplayMetrics is null in OnCreate.");
184+
}
176185

177186
RequestedOrientation = DefaultOrientation = IsTablet ? global::Android.Content.PM.ScreenOrientation.FullUser : global::Android.Content.PM.ScreenOrientation.SensorLandscape;
178187

@@ -181,10 +190,18 @@ protected override void OnCreate(global::Android.OS.Bundle? savedInstanceState)
181190
// Manually load them so that they can be loaded by RulesetStore.loadFromAppDomain.
182191
// REMEMBER to fully uninstall previous version every time when investigating this!
183192
// Don't forget osu.Game.Tests.Android too.
184-
Assembly.Load("osu.Game.Rulesets.Osu");
185-
Assembly.Load("osu.Game.Rulesets.Taiko");
186-
Assembly.Load("osu.Game.Rulesets.Catch");
187-
Assembly.Load("osu.Game.Rulesets.Mania");
193+
try
194+
{
195+
// Using typeof() ensures the linker preserves the assemblies.
196+
Assembly.Load(typeof(osu.Game.Rulesets.Osu.OsuRuleset).Assembly.FullName);
197+
Assembly.Load(typeof(osu.Game.Rulesets.Taiko.TaikoRuleset).Assembly.FullName);
198+
Assembly.Load(typeof(osu.Game.Rulesets.Catch.CatchRuleset).Assembly.FullName);
199+
Assembly.Load(typeof(osu.Game.Rulesets.Mania.ManiaRuleset).Assembly.FullName);
200+
}
201+
catch (Exception e)
202+
{
203+
global::Android.Util.Log.Error("OsuGameActivity", $"Failed to load rulesets: {e}");
204+
}
188205
}
189206

190207
protected override void OnResume()
@@ -275,22 +292,30 @@ private void handleIntent(global::Android.Content.Intent? intent)
275292

276293
private void handleImportFromUris(params global::Android.Net.Uri[] uris) => Task.Factory.StartNew(async () =>
277294
{
278-
var tasks = new List<ImportTask>();
279-
280-
await Task.WhenAll(uris.Select(async uri =>
295+
try
281296
{
282-
var task = await AndroidImportTask.Create(ContentResolver!, uri).ConfigureAwait(false);
297+
var tasks = new List<ImportTask>();
283298

284-
if (task != null)
299+
await Task.WhenAll(uris.Select(async uri =>
285300
{
286-
lock (tasks)
301+
if (ContentResolver == null) return;
302+
var task = await AndroidImportTask.Create(ContentResolver, uri).ConfigureAwait(false);
303+
304+
if (task != null)
287305
{
288-
tasks.Add(task);
306+
lock (tasks)
307+
{
308+
tasks.Add(task);
309+
}
289310
}
290-
}
291-
})).ConfigureAwait(false);
311+
})).ConfigureAwait(false);
292312

293-
await game.Import(tasks.ToArray()).ConfigureAwait(false);
313+
await game.Import(tasks.ToArray()).ConfigureAwait(false);
314+
}
315+
catch (Exception ex)
316+
{
317+
global::Android.Util.Log.Error("OsuGameActivity", $"Failed to handle imports: {ex}");
318+
}
294319
}, TaskCreationOptions.LongRunning);
295320

296321
public global::Android.Views.Surface? GetSurface()

0 commit comments

Comments
 (0)