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:

構成Configuration 1.2.0 のサイズ1.2.0 Size 4.0.1 のサイズ4.0.1 Size
リンクなしのリリース:Release without Linking: 14.0 MB14.0 MB 16.0 MB16.0 MB
リンクありのリリース:Release with Linking: 4.2 MB4.2 MB 2.9 MB2.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. 次の 3 つのオプションがあります。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 シリアル化を利用する場合は、型のプロパティを保持することができます。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)]

たとえば、次のコード フラグメントでは、Example クラス全体が XML シリアル化に対して維持されています。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. ファイルの SSDL セクションに次の 要素を追加します。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