Skip to content

Commit 0a70968

Browse files
jonathanpeppersjonpryor
authored andcommitted
[monodroid] ensure main app assembly is loaded (#4058)
Fixes: https://developercommunity.visualstudio.com/content/problem/788217/xamarin-android-resource-ids-mismatch-1.html In a project where: 1. The `MainLauncher` activity is in a class library 2. This activity uses a value from `Resource.designer.cs` You get a crash such as: UNHANDLED EXCEPTION: Android.Content.Res.Resources+NotFoundException: Resource ID #0x7f09001c at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualVoidMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0008e] in <e7e2d009b69d4e5f9a00e6ee600b8a8e>:0 at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualVoidMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0005d] in <e7e2d009b69d4e5f9a00e6ee600b8a8e>:0 at Android.App.Activity.SetContentView (System.Int32 layoutResID) [0x00022] in <82e50ec67af648c3b9f43b3d70e21b96>:0 at ClassLibrary1.SharedActivity.OnCreate (Android.OS.Bundle savedInstanceState) [0x00011] in <aff3f2d8f15f4b618f6674d9c306f099>:0 at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (System.IntPtr jnienv, System.IntPtr native__this, System.IntPtr native_savedInstanceState) [0x00011] in <82e50ec67af648c3b9f43b3d70e21b96>:0 at (wrapper dynamic-method) Android.Runtime.DynamicMethodNameCounter.4(intptr,intptr,intptr) --- End of managed Android.Content.Res.Resources+NotFoundException stack trace --- android.content.res.Resources$NotFoundException: Resource ID #0x7f09001c at android.content.res.ResourcesImpl.getValue(ResourcesImpl.java:237) at android.content.res.Resources.loadXmlResourceParser(Resources.java:2281) at android.content.res.Resources.getLayout(Resources.java:1175) at android.view.LayoutInflater.inflate(LayoutInflater.java:532) at android.view.LayoutInflater.inflate(LayoutInflater.java:481) at android.support.v7.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:469) at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:140) at crc64fd99288c2cd04dcf.SharedActivity.n_onCreate(Native Method) at crc64fd99288c2cd04dcf.SharedActivity.onCreate(SharedActivity.java:30) at android.app.Activity.performCreate(Activity.java:7802) at android.app.Activity.performCreate(Activity.java:7791) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1306) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3245) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3409) at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83) at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) In this example `App1.dll` is the main app assembly, but `ClassLibrary1.SharedActivity` is the `MainLauncher`. What was strange is that when I looked at the list of assemblies in `AppDomain.CurrentDomain.GetAssemblies ()` before the failure, `App1.dll` was not in the list! So I enabled a bunch of logging: adb shell setprop debug.mono.log default,assembly,timing The log messages like this did not mention `App1.dll`: I monodroid-assembly: open_from_update_dir: loaded assembly: 0x7723bca880 I monodroid-timing: Assembly load: ClassLibrary1.dll preloaded; elapsed: 0s:1::252032 Which lead me to this code: /* skip element 0, as that's loaded in create_domain() */ for (size_t i = 1; i < assemblies.get_length (); ++i) { I don't actually see anything in `create_domain()` that would load `App1.dll`? Looking through the commit history I don't understand how this project would have ever worked? Changing `i = 1` to `i = 0` solves the crash in the project. I added a test that reproduces the issue. I also added a `DebuggingTest.SetTargetFrameworkAndManifest()` helper method to cleanup `DebuggingTest.cs`.
1 parent fdd9ffa commit 0a70968

File tree

3 files changed

+75
-26
lines changed

3 files changed

+75
-26
lines changed

Documentation/release-notes/4058.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
### Issues fixed
2+
3+
* [DevCom 788217](https://developercommunity.visualstudio.com/content/problem/788217/xamarin-android-resource-ids-mismatch-1.html):
4+
A crash on startup could be encountered when the `MainLauncher` activity is implemented in a Xamarin.Android class library.

src/monodroid/jni/monodroid-glue.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,8 +1340,7 @@ MonodroidRuntime::load_assemblies (MonoDomain *domain, JNIEnv *env, jstring_arra
13401340
if (XA_UNLIKELY (utils.should_log (LOG_TIMING)))
13411341
total_time.mark_start ();
13421342

1343-
/* skip element 0, as that's loaded in create_domain() */
1344-
for (size_t i = 1; i < assemblies.get_length (); ++i) {
1343+
for (size_t i = 0; i < assemblies.get_length (); ++i) {
13451344
jstring_wrapper &assembly = assemblies [i];
13461345
load_assembly (domain, env, assembly);
13471346
}

tests/MSBuildDeviceIntegration/Tests/DebuggingTest.cs

Lines changed: 70 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ public void ClearDebugProperties ()
1818
ClearDebugProperty ();
1919
}
2020

21+
void SetTargetFrameworkAndManifest(XamarinAndroidApplicationProject proj, Builder builder)
22+
{
23+
string apiLevel;
24+
proj.TargetFrameworkVersion = builder.LatestTargetFrameworkVersion (out apiLevel);
25+
proj.AndroidManifest = $@"<?xml version=""1.0"" encoding=""utf-8""?>
26+
<manifest xmlns:android=""http://schemas.android.com/apk/res/android"" android:versionCode=""1"" android:versionName=""1.0"" package=""UnnamedProject.UnnamedProject"">
27+
<uses-sdk android:minSdkVersion=""24"" android:targetSdkVersion=""{apiLevel}"" />
28+
<application android:label=""${{PROJECT_NAME}}"">
29+
</application >
30+
</manifest>";
31+
}
32+
2133
[Test]
2234
[Retry (1)]
2335
public void ApplicationRunsWithoutDebugger ([Values (false, true)] bool isRelease)
@@ -36,14 +48,7 @@ public void ApplicationRunsWithoutDebugger ([Values (false, true)] bool isReleas
3648
}
3749
proj.SetDefaultTargetDevice ();
3850
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
39-
string apiLevel;
40-
proj.TargetFrameworkVersion = b.LatestTargetFrameworkVersion (out apiLevel);
41-
proj.AndroidManifest = $@"<?xml version=""1.0"" encoding=""utf-8""?>
42-
<manifest xmlns:android=""http://schemas.android.com/apk/res/android"" android:versionCode=""1"" android:versionName=""1.0"" package=""UnnamedProject.UnnamedProject"">
43-
<uses-sdk android:minSdkVersion=""24"" android:targetSdkVersion=""{apiLevel}"" />
44-
<application android:label=""${{PROJECT_NAME}}"">
45-
</application >
46-
</manifest>";
51+
SetTargetFrameworkAndManifest (proj, b);
4752
b.Save (proj, saveProject: true);
4853
proj.NuGetRestore (Path.Combine (Root, b.ProjectDirectory), b.PackagesDirectory);
4954
Assert.True (b.Build (proj), "Project should have built.");
@@ -60,6 +65,61 @@ public void ApplicationRunsWithoutDebugger ([Values (false, true)] bool isReleas
6065
}
6166
}
6267

68+
[Test]
69+
public void ClassLibraryMainLauncherRuns ()
70+
{
71+
if (!HasDevices) {
72+
Assert.Ignore ("Test needs a device attached.");
73+
return;
74+
}
75+
76+
var path = Path.Combine ("temp", TestName);
77+
78+
var app = new XamarinAndroidApplicationProject {
79+
ProjectName = "MyApp",
80+
};
81+
if (!CommercialBuildAvailable) {
82+
var abis = new string [] { "armeabi-v7a", "x86" };
83+
app.SetProperty (KnownProperties.AndroidSupportedAbis, string.Join (";", abis));
84+
}
85+
app.SetDefaultTargetDevice ();
86+
87+
var lib = new XamarinAndroidLibraryProject {
88+
ProjectName = "MyLibrary"
89+
};
90+
lib.Sources.Add (new BuildItem.Source ("MainActivity.cs") {
91+
TextContent = () => lib.ProcessSourceTemplate (app.DefaultMainActivity).Replace ("${JAVA_PACKAGENAME}", app.JavaPackageName),
92+
});
93+
lib.AndroidResources.Clear ();
94+
foreach (var resource in app.AndroidResources) {
95+
lib.AndroidResources.Add (resource);
96+
}
97+
var reference = $"..\\{lib.ProjectName}\\{lib.ProjectName}.csproj";
98+
app.References.Add (new BuildItem.ProjectReference (reference, lib.ProjectName, lib.ProjectGuid));
99+
100+
// Remove the default MainActivity.cs & AndroidResources
101+
app.AndroidResources.Clear ();
102+
app.AndroidResources.Add (new AndroidItem.AndroidResource ("Resources\\layout\\foo.xml") {
103+
TextContent = () => "<?xml version=\"1.0\" encoding=\"utf-8\" ?><LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" />"
104+
});
105+
app.Sources.Remove (app.GetItem ("MainActivity.cs"));
106+
107+
using (var libBuilder = CreateDllBuilder (Path.Combine (path, lib.ProjectName)))
108+
using (var appBuilder = CreateApkBuilder (Path.Combine (path, app.ProjectName))) {
109+
SetTargetFrameworkAndManifest (app, appBuilder);
110+
Assert.IsTrue (libBuilder.Build (lib), "library build should have succeeded.");
111+
Assert.True (appBuilder.Install (app), "app should have installed.");
112+
ClearAdbLogcat ();
113+
if (CommercialBuildAvailable)
114+
Assert.True (appBuilder.RunTarget (app, "_Run"), "Project should have run.");
115+
else
116+
AdbStartActivity ($"{app.PackageName}/{app.JavaPackageName}.MainActivity");
117+
118+
Assert.True (WaitForActivityToStart (app.PackageName, "MainActivity",
119+
Path.Combine (Root, appBuilder.ProjectDirectory, "logcat.log"), 30), "Activity should have started.");
120+
}
121+
}
122+
63123
#pragma warning disable 414
64124
static object [] DebuggerCustomAppTestCases = new object [] {
65125
new object[] {
@@ -147,14 +207,7 @@ public override void OnCreate ()
147207
"),
148208
});
149209
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
150-
string apiLevel;
151-
proj.TargetFrameworkVersion = b.LatestTargetFrameworkVersion (out apiLevel);
152-
proj.AndroidManifest = $@"<?xml version=""1.0"" encoding=""utf-8""?>
153-
<manifest xmlns:android=""http://schemas.android.com/apk/res/android"" android:versionCode=""1"" android:versionName=""1.0"" package=""UnnamedProject.UnnamedProject"">
154-
<uses-sdk android:minSdkVersion=""24"" android:targetSdkVersion=""{apiLevel}"" />
155-
<application android:label=""${{PROJECT_NAME}}"">
156-
</application >
157-
</manifest>";
210+
SetTargetFrameworkAndManifest (proj, b);
158211
b.Save (proj, saveProject: true);
159212
proj.NuGetRestore (Path.Combine (Root, b.ProjectDirectory), b.PackagesDirectory);
160213
Assert.True (b.Build (proj), "Project should have built.");
@@ -279,14 +332,7 @@ public void ApplicationRunsWithDebuggerAndBreaks (bool useSharedRuntime, bool em
279332
proj.SetProperty (KnownProperties.AndroidSupportedAbis, string.Join (";", abis));
280333
proj.SetDefaultTargetDevice ();
281334
using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) {
282-
string apiLevel;
283-
proj.TargetFrameworkVersion = b.LatestTargetFrameworkVersion (out apiLevel);
284-
proj.AndroidManifest = $@"<?xml version=""1.0"" encoding=""utf-8""?>
285-
<manifest xmlns:android=""http://schemas.android.com/apk/res/android"" android:versionCode=""1"" android:versionName=""1.0"" package=""UnnamedProject.UnnamedProject"">
286-
<uses-sdk android:minSdkVersion=""24"" android:targetSdkVersion=""{apiLevel}"" />
287-
<application android:label=""${{PROJECT_NAME}}"">
288-
</application >
289-
</manifest>";
335+
SetTargetFrameworkAndManifest (proj, b);
290336
b.Save (proj, saveProject: true);
291337
proj.NuGetRestore (Path.Combine (Root, b.ProjectDirectory), b.PackagesDirectory);
292338
Assert.True (b.Build (proj), "Project should have built.");

0 commit comments

Comments
 (0)