Android의 연결Linking on Android

Xamarin.Android 애플리케이션은 링커를 사용하여 애플리케이션의 크기를 줄일 수 있습니다.Xamarin.Android applications use a linker to reduce the size of the application. 링커는 애플리케이션의 정적 분석을 통해 실제로 사용되는 어셈블리, 형식 및 멤버를 확인합니다.The linker employes static analysis of your application to determine which assemblies are actually used, which types are actually used, and which members are actually used. 그런 다음, 가비지 수집기로 작동하여 참조되는 어셈블리, 형식 및 멤버의 의 완전한 클로저가 발견될 때까지, 참조되는 어셈블리, 형식 및 멤버를 계속 찾습니다.The linker then behaves like a garbage collector, continually looking for the assemblies, types, and members that are referenced until the entire closure of referenced assemblies, types, and members is found. 그런 다음, 이 클로저 외부의 모든 것은 삭제됩니다.Then everything outside of this closure is discarded.

예를 들어 Hello, Android 샘플은 다음과 같습니다.For example, the Hello, Android sample:

ConfigurationConfiguration 1.2.0 크기1.2.0 Size 4.0.1 크기4.0.1 Size
연결 없이 릴리스:Release without Linking: 14.0MB14.0 MB 16.0MB16.0 MB
연결 없이 릴리스:Release with Linking: 4.2MB4.2 MB 2.9MB2.9 MB

연결 결과 1.2.0에서 원래(연결되지 않은) 패키지 크기의 30%, 4.0.1에서 연결되지 않은 패키지 크기의 18%에 해당하는 패키지가 생성됩니다.Linking results in a package that is 30% the size of the original (unlinked) package in 1.2.0, and 18% of the unlinked package in 4.0.1.

컨트롤Control

연결은 정적 분석을 기반으로 합니다.Linking is based on static analysis. 따라서 런타임 환경에 종속된 모든 것은 검색되지 않습니다.Consequently, anything that depends upon the runtime environment won't be detected:

// To play along at home, Example must be in a different assembly from MyActivity.
public class Example {
    // Compiler provides default constructor...
}

[Activity (Label="Linker Example", MainLauncher=true)]
public class MyActivity {
    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Will this work?
        var o = Activator.CreateInstance (typeof (ExampleLibrary.Example));
    }
}

링커 동작Linker Behavior

링커 제어의 기본 메커니즘은 프로젝트 옵션 대화 상자 내 링커 동작(Visual Studio의 연결) 드롭다운입니다.The primary mechanism for controlling the linker is the Linker Behavior (Linking in Visual Studio) drop-down within the Project Options dialog box. 여기에는 세 가지 옵션이 있습니다.There are three options:

  1. 연결하지 않음(Visual Studio의 없음)Don't Link (None in Visual Studio)
  2. SDK 어셈블리 연결(SDK 어셈블리만)Link SDK Assemblies (Sdk Assemblies Only)
  3. 모든 어셈블리 연결(SDK 및 사용자 어셈블리)Link All Assemblies (Sdk and User Assemblies)

연결하지 않음 옵션은 링커를 해제합니다. 위의 "연결 없이 릴리스" 애플리케이션 크기 예제에서는 이 동작을 사용했습니다.The Don't Link option turns off the linker; the above "Release without Linking" application size example used this behavior. 이는 런타임 오류를 해결할 때 링커가 원인인지 파악하는 데 유용합니다.This is useful for troubleshooting runtime failures, to see if the linker is responsible. 이 설정은 일반적으로 프로덕션 빌드에 권장되지 않습니다.This setting is not usually recommended for production builds.

SDK 어셈블리 연결 옵션은 Xamarin.Android와 함께 제공되는 어셈블리만 연결합니다.The Link SDK Assemblies option only links assemblies that come with Xamarin.Android. 다른 모든 어셈블리(예: 코드)는 연결되지 않습니다.All other assemblies (such as your code) are not linked.

모든 어셈블리 연결 옵션은 모든 어셈블리를 연결하므로, 정적 참조가 없을 경우 코드도 제거될 수 있습니다.The Link All Assemblies option links all assemblies, which means your code may also be removed if there are no static references.

위의 예제에서는 연결하지 않음SDK 어셈블리 연결 옵션을 사용할 수 있으며, 모든 어셈블리 연결 동작을 사용할 경우 다음 오류가 생성됩니다.The above example will work with the Don't Link and Link SDK Assemblies options, and will fail with the Link All Assemblies behavior, generating the following error:

E/mono    (17755): [0xafd4d440:] EXCEPTION handling: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
I/MonoDroid(17755): UNHANDLED EXCEPTION: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
I/MonoDroid(17755): at System.Activator.CreateInstance (System.Type,bool) <0x00180>
I/MonoDroid(17755): at System.Activator.CreateInstance (System.Type) <0x00017>
I/MonoDroid(17755): at LinkerScratch2.Activity1.OnCreate (Android.OS.Bundle) <0x00027>
I/MonoDroid(17755): at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) <0x00057>
I/MonoDroid(17755): at (wrapper dynamic-method) object.95bb4fbe-bef8-4e5b-8e99-ca83a5d7a124 (intptr,intptr,intptr) <0x00033>
E/mono    (17755): [0xafd4d440:] EXCEPTION handling: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
E/mono    (17755):
E/mono    (17755): Unhandled Exception: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
E/mono    (17755):   at System.Activator.CreateInstance (System.Type type, Boolean nonPublic) [0x00000] in <filename unknown>:0
E/mono    (17755):   at System.Activator.CreateInstance (System.Type type) [0x00000] in <filename unknown>:0
E/mono    (17755):   at LinkerScratch2.Activity1.OnCreate (Android.OS.Bundle bundle) [0x00000] in <filename unknown>:0
E/mono    (17755):   at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState) [0x00000] in <filename unknown>:0
E/mono    (17755):   at (wrapper dynamic-method) object:95bb4fbe-bef8-4e5b-8e99-ca83a5d7a124 (intptr,intptr,intptr)

코드 유지Preserving Code

링커는 사용자가 유지하려는 코드를 제거하는 경우가 있습니다.The linker will sometimes remove code that you want to preserve. 예:For example:

  • System.Reflection.MemberInfo.Invoke를 통해 동적으로 호출하는 코드가 있을 수 있습니다.You might have code that you call dynamically via System.Reflection.MemberInfo.Invoke.

  • 형식을 동적으로 인스턴스화하는 경우 형식의 기본 생성자를 유지하는 것이 좋습니다.If you instantiate types dynamically, you may want to preserve the default constructor of your types.

  • XML serialization을 사용하는 경우 형식의 속성을 유지하는 것이 좋습니다.If you use XML serialization, you may want to preserve the properties of your types.

이러한 경우 Android.Runtime.Preserve 특성을 사용할 수 있습니다.In these cases, you can use the Android.Runtime.Preserve attribute. 애플리케이션에 의해 정적으로 연결되지 않은 모든 멤버는 제거될 수 있으므로, 이 특성을 사용하여 정적으로 참조되지 않았지만 여전히 애플리케이션에 필요한 멤버를 표시할 수 있습니다.Every member that is not statically linked by the application is subject to be removed, so this attribute can be used to mark members that are not statically referenced but are still needed by your application. 형식의 모든 멤버 또는 형식 자체에 이 특성을 적용할 수 있습니다.You can apply this attribute to every member of a type, or to the type itself.

다음 예에서는 이 특성을 사용하여 Example 클래스의 생성자를 유지합니다.In the following example, this attribute is used to preserve the constructor of the Example class:

public class Example
{
    [Android.Runtime.Preserve]
    public Example ()
    {
    }
}

전체 형식을 유지하려는 경우 다음 특성 구문을 사용할 수 있습니다.If you want to preserve the entire type, you can use the following attribute syntax:

[Android.Runtime.Preserve (AllMembers = true)]

예를 들어 다음 코드 조각에서는 XML 직렬화를 위해 전체 Example 클래스가 유지됩니다.For example, in the following code fragment the entire Example class is preserved for XML serialization:

[Android.Runtime.Preserve (AllMembers = true)]
class Example
{
    // Compiler provides default constructor...
}

특정 멤버를 유지하려는 경우가 있습니다(포함 형식이 유지된 경우에만 해당).Sometimes you want to preserve certain members, but only if the containing type was preserved. 이러한 경우에는 다음 특성 구문을 사용합니다.In those cases, use the following attribute syntax:

[Android.Runtime.Preserve (Conditional = true)]

Xamarin 라이브러리에 대한 종속성을 유지하지 않으려는 경우(예를 들어 플랫폼 간에 이식 가능한 클래스 라이브러리(PCL)를 빌드하는 경우)에도 Android.Runtime.Preserve 특성을 사용할 수 있습니다.If you do not want to take a dependency on the Xamarin libraries – for example, you are building a cross platform portable class library (PCL) – you can still use the Android.Runtime.Preserve attribute. 이렇게 하려면 다음과 같이 Android.Runtime 내에 PreserveAttribute 클래스를 선언합니다.To do this, declare a PreserveAttribute class within the Android.Runtime namespace like this:

namespace Android.Runtime
{
    public sealed class PreserveAttribute : System.Attribute
    {
        public bool AllMembers;
        public bool Conditional;
    }
}

위의 예에서 Preserve 특성은 Android.Runtime 네임스페이스에 선언되어 있습니다. 그러나 링커가 형식 이름별로 이 특성을 조회하기 때문에 임의의 네임스페이스에서 Preserve 특성을 사용할 수 있습니다.In the above examples, the Preserve attribute is declared in the Android.Runtime namespace; however, you can use the Preserve attribute in any namespace because the linker looks up this attribute by type name.

falseflagfalseflag

[Preserve] 특성을 사용할 수 없는 경우에는 링커가 해당 형식이 사용된다고 믿도록 코드 블록을 제공하는 한편 이 코드 블록이 런타임에 실행되지 않도록 하면 유용합니다.If the [Preserve] attribute can't be used, it is often useful to provide a block of code so that the linker believes that the type is used, while preventing the block of code from being executed at runtime. 이 기법을 활용하려면 다음과 같이 할 수 있습니다.To make use of this technique, we could do:

[Activity (Label="Linker Example", MainLauncher=true)]
class MyActivity {

#pragma warning disable 0219, 0649
    static bool falseflag = false;
    static MyActivity ()
    {
        if (falseflag) {
            var ignore = new Example ();
        }
    }
#pragma warning restore 0219, 0649

    // ...
}

linkskiplinkskip

사용자가 제공한 어셈블리 집합을 절대 연결해서는 안 된다고 지정하고, AndroidLinkSkip MSBuild 속성을 사용하여 SDK 어셈블리 연결 동작으로 다른 사용자 어셈블리를 건너뛸 수 있게 할 수 있습니다.It is possible to specify that a set of user-provided assemblies should not be linked at all, while allowing other user assemblies to be skipped with the Link SDK Assemblies behavior by using the AndroidLinkSkip MSBuild property:

<PropertyGroup>
    <AndroidLinkSkip>Assembly1;Assembly2</AndroidLinkSkip>
</PropertyGroup>

LinkDescriptionLinkDescription

사용자 지정 링커 구성 파일을 포함한 파일에서 @(LinkDescription) 빌드 동작을 사용할 수 있습니다.The @(LinkDescription) Build action may be used on files which can contain a Custom linker configuration file. 추가합니다.file. 사용자 지정 링커 구성 파일은 유지해야 하는 internal 또는 private 멤버를 유지하는 데 필요할 수 있습니다.Custom linker configuration files may be required to preserve internal or private members that need to be preserved.

사용자 지정 특성Custom Attributes

어셈블리가 연결되면 모든 멤버에서 다음 사용자 지정 특성 형식이 제거됩니다.When an assembly is linked, the following custom attribute types will be removed from all members:

  • System.ObsoleteAttributeSystem.ObsoleteAttribute
  • System.MonoDocumentationNoteAttributeSystem.MonoDocumentationNoteAttribute
  • System.MonoExtensionAttributeSystem.MonoExtensionAttribute
  • System.MonoInternalNoteAttributeSystem.MonoInternalNoteAttribute
  • System.MonoLimitationAttributeSystem.MonoLimitationAttribute
  • System.MonoNotSupportedAttributeSystem.MonoNotSupportedAttribute
  • System.MonoTODOAttributeSystem.MonoTODOAttribute
  • System.Xml.MonoFIXAttributeSystem.Xml.MonoFIXAttribute

어셈블리가 연결되면 릴리스 빌드의 모든 멤버에서 다음 사용자 지정 특성 형식이 제거됩니다.When an assembly is linked, the following custom attribute types will be removed from all members in Release builds:

  • System.Diagnostics.DebuggableAttributeSystem.Diagnostics.DebuggableAttribute
  • System.Diagnostics.DebuggerBrowsableAttributeSystem.Diagnostics.DebuggerBrowsableAttribute
  • System.Diagnostics.DebuggerDisplayAttributeSystem.Diagnostics.DebuggerDisplayAttribute
  • System.Diagnostics.DebuggerHiddenAttributeSystem.Diagnostics.DebuggerHiddenAttribute
  • System.Diagnostics.DebuggerNonUserCodeAttributeSystem.Diagnostics.DebuggerNonUserCodeAttribute
  • System.Diagnostics.DebuggerStepperBoundaryAttributeSystem.Diagnostics.DebuggerStepperBoundaryAttribute
  • System.Diagnostics.DebuggerStepThroughAttributeSystem.Diagnostics.DebuggerStepThroughAttribute
  • System.Diagnostics.DebuggerTypeProxyAttributeSystem.Diagnostics.DebuggerTypeProxyAttribute
  • System.Diagnostics.DebuggerVisualizerAttributeSystem.Diagnostics.DebuggerVisualizerAttribute