Xamarin.Android API 디자인 원칙

Xamarin.Android는 Mono의 일부인 핵심 기본 클래스 라이브러리 외에도 개발자가 Mono를 사용하여 네이티브 Android 애플리케이션을 만들 수 있도록 다양한 Android API에 대한 바인딩을 제공합니다.

Xamarin.Android의 핵심에는 C# 세계를 Java 세계와 연결하고 개발자에게 C# 또는 기타 .NET 언어의 Java API에 대한 액세스를 제공하는 interop 엔진이 있습니다.

디자인 원칙

다음은 Xamarin.Android 바인딩에 대한 몇 가지 디자인 원칙입니다.

  • .NET Framework 디자인 지침을 따릅니다.

  • 개발자가 Java 클래스를 서브클래스할 수 있도록 허용합니다.

  • 서브클래스는 C# 표준 구문에서 작동해야 합니다.

  • 기존 클래스에서 파생합니다.

  • 기본 생성자를 호출하여 체인합니다.

  • 재정의 메서드는 C#의 재정의 시스템을 사용하여 수행해야 합니다.

  • 일반적인 Java 작업을 쉽고 하드 Java 작업을 가능하게 합니다.

  • JavaBean 속성을 C# 속성으로 노출합니다.

  • 강력한 형식의 API 노출:

    • 형식 안전성 향상.

    • 런타임 오류를 최소화합니다.

    • 반환 형식에서 IDE intellisense를 가져옵니다.

    • IDE 팝업 설명서를 허용합니다.

  • API의 IDE 내 탐색 권장

    • 프레임워크 대안을 활용하여 Java 클래스리브 노출을 최소화합니다.

    • 적절하고 적용 가능한 경우 단일 메서드 인터페이스 대신 C# 대리자(람다, 익명 메서드 및 System.Delegate)를 노출합니다.

    • 임의의 Java 라이브러리( Android.Runtime.JNIEnv)를 호출하는 메커니즘을 제공합니다.

어셈블리

Xamarin.Android에는 MonoMobile 프로필을 구성하는 여러 어셈블리가 포함되어 있습니다. 자세한 내용은 어셈블리 페이지를 참조하세요.

Android 플랫폼에 대한 바인딩은 어셈블리에 Mono.Android.dll 포함됩니다. 이 어셈블리에는 Android API를 사용하고 Android 런타임 VM과 통신하기 위한 전체 바인딩이 포함되어 있습니다.

바인딩 디자인

컬렉션

Android API는 java.util 컬렉션을 광범위하게 활용하여 목록, 집합 및 맵을 제공합니다. 바인딩에서 System.Collections.Generic 인터페이스를 사용하여 이러한 요소를 노출합니다. 기본 매핑은 다음과 같습니다.

이러한 형식을 더 빠르게 복사하지 않고 마샬링할 수 있도록 도우미 클래스를 제공했습니다. 가능하면 프레임워크 제공 구현(예 List<T> : 또는 Dictionary<TKey, TValue>) 대신 이러한 제공된 컬렉션을 사용하는 것이 좋습니다. Android.Runtime 구현은 내부적으로 네이티브 Java 컬렉션을 활용하므로 Android API 멤버에 전달할 때 네이티브 컬렉션에서 복사할 필요가 없습니다.

해당 인터페이스를 수락하는 Android 메서드에 인터페이스 구현을 전달할 수 있습니다. 예를 들어 ArrayAdapter int>(Context, int, IList<int>) 생성자에 전달 List<int><합니다. 그러나 Android.Runtime 구현을 제외한 모든 구현의 경우 Mono VM에서 Android 런타임 VM으로 목록을 복사하는 작업이 포함됩니다. 목록이 나중에 Android 런타임 내에서 변경되는 경우(예: ArrayAdapter<T>를 호출하여) Add(T) 메서드) 이러한 변경 내용은 관리 코드에 표시되지 않습니다. 사용된 JavaList<int> 경우 해당 변경 내용이 표시됩니다.

위에 나열된 도우미 클래스중 하나가 아닌 컬렉션 인터페이스 구현은 [In]만 마샬링합니다.

// This fails:
var badSource  = new List<int> { 1, 2, 3 };
var badAdapter = new ArrayAdapter<int>(context, textViewResourceId, badSource);
badAdapter.Add (4);
if (badSource.Count != 4) // true
    throw new InvalidOperationException ("this is thrown");

// this works:
var goodSource  = new JavaList<int> { 1, 2, 3 };
var goodAdapter = new ArrayAdapter<int> (context, textViewResourceId, goodSource);
goodAdapter.Add (4);
if (goodSource.Count != 4) // false
    throw new InvalidOperationException ("should not be reached.");

속성

Java 메서드는 적절한 경우 속성으로 변환됩니다.

  • Java 메서드 쌍 T getFoo()void setFoo(T) 속성으로 Foo 변환됩니다. 예: Activity.Intent.

  • Java 메서드 getFoo() 는 읽기 전용 Foo 속성으로 변환됩니다. 예: Context.PackageName.

  • 집합 전용 속성이 생성되지 않습니다.

  • 속성 형식이 배열이면 속성이 생성되지 않습니다 .

이벤트 및 수신기

Android API는 Java를 기반으로 빌드되며 해당 구성 요소는 이벤트 수신기를 연결하기 위한 Java 패턴을 따릅니다. 이 패턴은 사용자가 익명 클래스를 만들고 재정의할 메서드를 선언해야 하기 때문에 번거로운 경향이 있습니다. 예를 들어 Java를 사용하여 Android에서 작업을 수행하는 방법입니다.

final android.widget.Button button = new android.widget.Button(context);

button.setText(this.count + " clicks!");
button.setOnClickListener (new View.OnClickListener() {
    public void onClick (View v) {
        button.setText(++this.count + " clicks!");
    }
});

이벤트를 사용하는 C#의 해당 코드는 다음과 같습니다.

var button = new Android.Widget.Button (context) {
    Text = string.Format ("{0} clicks!", this.count),
};
button.Click += (sender, e) => {
    button.Text = string.Format ("{0} clicks!", ++this.count);
};

위의 두 메커니즘은 모두 Xamarin.Android에서 사용할 수 있습니다. 수신기 인터페이스를 구현하고 View.SetOnClickListener와 연결하거나 일반적인 C# 패러다임을 통해 만든 대리자를 Click 이벤트에 연결할 수 있습니다.

수신기 콜백 메서드에 void 반환이 있는 경우 EventHandler<TEventArgs> 대리자를 기반으로 API 요소를 만듭니다. 이러한 수신기 유형에 대한 위의 예제와 같은 이벤트를 생성합니다. 그러나 수신기 콜백이 void가 아닌 부울 값을 반환하는 경우 이벤트 및 EventHandlers는 사용되지 않습니다. 대신 콜백 서명에 대한 특정 대리자를 생성하고 이벤트 대신 속성을 추가합니다. 그 이유는 대리자 호출 순서 및 반환 처리를 처리하기 위한 것입니다. 이 방법은 Xamarin.iOS API로 수행되는 작업을 미러.

C# 이벤트 또는 속성은 Android 이벤트 등록 방법인 경우에만 자동으로 생성됩니다.

  1. 접두사( set 예: OnClickListener 설정)가 있습니다.

  2. 반환 형식이 void 있습니다.

  3. 매개 변수를 하나만 허용하고, 매개 변수 형식은 인터페이스이고, 인터페이스에는 하나의 메서드만 있고, 인터페이스 이름은 View.OnClick 수신기와 같이 끝납니다 Listener .

또한 수신기 인터페이스 메서드에 void 대신 부울반환 형식이 있는 경우 생성된 EventArgs 하위 클래스에는 Handled 속성이 포함됩니다. Handled 속성의 값은 수신기 메서드의 반환 값으로 사용되며 기본값은 .입니다true.

예를 들어 Android View.setOnKeyListener() 메서드는 View.OnKeyListener 인터페이스를 허용하고 View.OnKeyListener.onKey(View, int, KeyEvent) 메서드에는 부울 반환 형식이 있습니다. Xamarin.Android는 EventHandler View.KeyEventArgs인 해당 View.KeyPress 이벤트를 생성합니다>.< KeyEventArgs 클래스에는 View.OnKeyListener.onKey() 메서드의 반환 값으로 사용되는 View.KeyEventArgs.Handled 속성이 있습니다.

다른 메서드 및 ctor에 대한 오버로드를 추가하여 대리자 기반 연결을 노출하려고 합니다. 또한 콜백이 여러 개인 수신기는 개별 콜백을 구현하는 것이 적절한지 확인하기 위해 몇 가지 추가 검사가 필요하므로 식별된 대로 변환하고 있습니다. 해당 이벤트가 없는 경우 수신기는 C#에서 사용해야 하지만 대리자 사용량이 있을 수 있다고 생각되는 모든 수신기를 주의해야 합니다. 또한 "수신기" 접미사가 없는 인터페이스의 일부 변환은 대리자 대안의 이점을 얻을 수 있다는 것이 분명했을 때 수행되었습니다.

모든 수신기 인터페이스는 다음을 구현합니다. Android.Runtime.IJavaObject 인터페이스는 바인딩의 구현 세부 정보 때문에 수신기 클래스가 이 인터페이스를 구현해야 합니다. 이 작업은 Java.Lang.Object서브클래스 또는 Android 활동과 같은 다른 래핑된 Java 개체에서 수신기 인터페이스를 구현하여 수행할 수 있습니다.

Runnables

Java는 java.lang.Runnable 인터페이스를 활용하여 위임 메커니즘을 제공합니다. java.lang.Thread 클래스는 이 인터페이스의 주목할 만한 소비자입니다. Android는 API에서도 인터페이스를 사용했습니다. Activity.runOnUiThread()View.post() 는 주목할 만한 예입니다.

인터페이스에는 Runnable 단일 void 메서드 run ()이 포함됩니다. 따라서 C#에서 System.Action 대리자로 바인딩할 수 있습니다. 네이티브 API에서 사용하는 모든 API 멤버에 대한 매개 변수를 허용하는 Action 오버로드(예: Activity.RunOnUiThread()View.Post())를 바인딩에 제공했습니다.Runnable

여러 형식이 인터페이스를 구현하므로 직접 실행 가능 오버로드로 전달될 수 있으므로 IRunnable 오버로드를 대체하지 않고 그대로 두었다.

내부 클래스

Java에는 두 가지 유형의 중 첩 클래스, 즉 정적 중첩 클래스와 비정적 클래스가 있습니다.

Java 정적 중첩 클래스는 C# 중첩 형식과 동일합니다.

내부 클래스라고도 하는 비정적 중첩 클래스는 크게 다릅니다. 바깥쪽 형식의 인스턴스에 대한 암시적 참조를 포함하며 정적 멤버를 포함할 수 없습니다(이 개요의 범위를 벗어난 다른 차이점 중에서).

바인딩 및 C# 사용과 관련하여 정적 중첩 클래스는 일반 중첩 형식으로 처리됩니다. 한편 내부 클래스에는 두 가지 중요한 차이점이 있습니다.

  1. 포함하는 형식에 대한 암시적 참조는 생성자 매개 변수로 명시적으로 제공되어야 합니다.

  2. 내부 클래스에서 상속하는 경우 내부 클래스는 기본 내부 클래스 의 포함하는 형식에서 상속되는 형식 내에 중첩되어야 하며 파생 형식은 C# 포함 형식과 동일한 형식의 생성자를 제공해야 합니다.

예를 들어 Android.Service.Wallpaper.WallpaperService.Engine 내부 클래스를 고려합니다. 내부 클래스이므로 WallpaperService.Engine() 생성자는 WallpaperService 인스턴스에 대한 참조를 사용합니다(매개 변수를 사용하지 않는 Java WallpaperService.Engine() 생성자와 비교 및 대조).

내부 클래스의 파생 예제는 CubeWallpaper.CubeEngine입니다.

class CubeWallpaper : WallpaperService {
    public override WallpaperService.Engine OnCreateEngine ()
    {
        return new CubeEngine (this);
    }

    class CubeEngine : WallpaperService.Engine {
        public CubeEngine (CubeWallpaper s)
                : base (s)
        {
        }
    }
}

내에 중첩되는 방법CubeWallpaper.CubeEngine, CubeWallpaper 포함하는 클래스WallpaperService.Engine에서 상속되는 방법 및 CubeWallpaper.CubeEngine 선언 형식 CubeWallpaper 을 사용하는 생성자가 있습니다(이 경우 모두 위에서 지정CubeWallpaper한 대로).

인터페이스

Java 인터페이스에는 세 개의 멤버 집합이 포함될 수 있으며, 그 중 두 집합은 C#에서 문제를 일으킬 수 있습니다.

  1. 메서드

  2. 유형

  3. 필드

Java 인터페이스는 다음 두 가지 형식으로 변환됩니다.

  1. 메서드 선언을 포함하는 (선택 사항) 인터페이스입니다. 이 인터페이스에는 'I' 접두사도 있지만 Java 인터페이스와 이름이 같습니다.

  2. Java 인터페이스 내에 선언된 필드를 포함하는 (선택 사항) 정적 클래스입니다.

중첩 형식은 접두사로 바깥쪽 인터페이스 이름을 사용하여 중첩된 형식 대신 바깥쪽 인터페이스의 형제로 "재배치"됩니다.

예를 들어 android.os.Parcelable 인터페이스를 고려합니다. 구획 가능 인터페이스에는 메서드, 중첩 형식 및 상수가 포함됩니다. 구획 가능 인터페이스 메서드는 Android.OS.IParcelable 인터페이스에 배치됩니다. 구획 가능 인터페이스 상수는 Android.OS.ParcelableConsts 형식에 배치됩니다. 중첩된 android.os.Parcelable.ClassLoaderCreator<T>android.os.Parcelable.Creator<T> 형식은 현재 제네릭 지원의 제한 사항으로 인해 바인딩되지 않습니다. 지원되는 경우 Android.OS.IParcelableClassLoaderCreatorAndroid.OS.IParcelableCreator 인터페이스로 표시됩니다. 예를 들어 중첩된 android.os.IBinder.DeathRecipient 인터페이스는 Android.OS.IBinderDeathRecipient 인터페이스로 바인딩됩니다.

참고 항목

Xamarin.Android 1.9부터 Java 인터페이스 상수는 Java 코드 포팅을 간소화하기 위해 복제됩니다. 이렇게 하면 Android 공급자 인터페이스 상수에 의존하는 Java 코드 포팅을 개선하는 데 도움이 됩니다.

위의 형식 외에도 다음과 같은 네 가지 추가 변경 내용이 있습니다.

  1. Java 인터페이스와 이름이 같은 형식이 생성되어 상수가 포함됩니다.

  2. 인터페이스 상수가 포함된 형식에는 구현된 Java 인터페이스에서 제공되는 모든 상수도 포함됩니다.

  3. 상수가 포함된 Java 인터페이스를 구현하는 모든 클래스는 구현된 모든 인터페이스의 상수가 포함된 새 중첩된 InterfaceConsts 형식을 가져옵니다.

  4. Consts 형식은 이제 사용되지 않습니다.

android.os.Parcelable 인터페이스의 경우 이제 상수가 포함된 Android.OS.Parcelable 형식이 있음을 의미합니다. 예를 들어 Parcelable.CONTENTS_FILE_DESCRIPTOR 상수는 ParcelableConsts.ContentsFileDescriptor 상수가 아닌 Parcelable.ContentsFileDescriptor 상수로 바인딩됩니다.

상수가 더 포함된 다른 인터페이스를 구현하는 상수가 포함된 인터페이스의 경우 이제 모든 상수의 합합이 생성됩니다. 예를 들어 android.provider.MediaStore.Video.VideoColumns 인터페이스는 android.provider.MediaStore.MediaColumns 인터페이스를 구현합니다 . 그러나 1.9 이전의 Android.Provider.MediaStore.Video.VideoColumnsConsts 형식은 Android.Provider.MediaStore.MediaColumnsConsts에 선언된 상수에 액세스할 방법이 없습니다. 따라서 Java 식 MediaStore.Video.VideoColumns.TITLE 은 Java 설명서를 많이 읽지 않고는 검색하기 어려운 C# 식 MediaStore.Video.MediaColumnsConsts.Title 에 바인딩되어야 합니다. 1.9에서 해당하는 C# 식은 MediaStore.Video.VideoColumns.Title입니다.

또한 Java 구획 가능 인터페이스를 구현하는 android.os.Bundle 형식을 고려해 보세요. 인터페이스를 구현하므로 해당 인터페이스의 모든 상수는 번들 형식을 "통해" 액세스할 수 있습니다. 예를 들어 Bundle.CONTENTS_FILE_DESCRIPTOR 완벽하게 유효한 Java 식입니다. 이전에는 이 식을 C#으로 이식하려면 구현된 모든 인터페이스를 확인하여 CONTENTS_FILE_DESCRIPTOR 제공된 형식을 확인해야 합니다. Xamarin.Android 1.9부터 상수가 포함된 Java 인터페이스를 구현하는 클래스에는 상속된 모든 인터페이스 상수가 포함된 중첩된 InterfaceConsts 형식이 있습니다. 이렇게 하면 Bundle.CONTENTS_FILE_DESCRIPTOR Bundle.InterfaceConsts.ContentsFileDescriptor로 변환수 있습니다.

마지막으로 Android.OS.ParcelableConsts와 같은 Consts 접미사가 있는 형식은 이제 새로 도입된 InterfaceConsts 중첩 형식이 아닌 사용되지 않습니다. Xamarin.Android 3.0에서 제거됩니다.

리소스

이미지, 레이아웃 설명, 이진 Blob 및 문자열 사전은 애플리케이션에 리소스 파일포함할 수 있습니다. 다양한 Android API는 이미지, 문자열 또는 이진 Blob을 직접 처리하는 대신 리소스 ID에서 작동하도록 설계되었습니다.

예를 들어 사용자 인터페이스 레이아웃(), 국제화 테이블 문자열(main.axml) 및 일부 아이콘(strings.xmldrawable-*/icon.png)이 포함된 샘플 Android 앱은 해당 리소스를 애플리케이션의 "리소스" 디렉터리에 유지합니다.

Resources/
    drawable-hdpi/
        icon.png

    drawable-ldpi/
        icon.png

    drawable-mdpi/
        icon.png

    layout/
        main.axml

    values/
        strings.xml

네이티브 Android API는 파일 이름으로 직접 작동하지 않고 대신 리소스 ID에서 작동합니다. 리소스를 사용하는 Android 애플리케이션을 컴파일하는 경우 빌드 시스템은 배포를 위해 리소스를 패키지하고 포함된 각 리소스에 대한 토큰을 포함하는 클래스 Resource 를 생성합니다. 예를 들어 위의 리소스 레이아웃의 경우 R 클래스에서 노출하는 항목은 다음과 같습니다.

public class Resource {
    public class Drawable {
        public const int icon = 0x123;
    }

    public class Layout {
        public const int main = 0x456;
    }

    public class String {
        public const int first_string = 0xabc;
        public const int second_string = 0xbcd;
    }
}

그런 다음 파일을 참조 drawable/icon.png 하거나 파일을 참조 layout/main.xml 하거나 Resource.String.first_stringResource.Layout.main 사전 파일values/strings.xml의 첫 번째 문자열을 참조하는 데 사용합니다Resource.Drawable.icon.

상수 및 열거형

네이티브 Android API에는 int의 의미를 확인하기 위해 상수 필드에 매핑되어야 하는 int를 사용하거나 반환하는 여러 메서드가 있습니다. 이러한 메서드를 사용하려면 설명서를 참조하여 적절한 값인 상수가 이상적이지 않은지 확인해야 합니다.

예를 들어 Activity.requestWindowFeature(int featureID)를 고려합니다.

이러한 경우 관련 상수들을 함께 .NET 열거형으로 그룹화하고 대신 메서드를 다시 매핑하여 열거형을 수행합니다. 이렇게 하면 IntelliSense에서 잠재적인 값을 선택할 수 있습니다.

위의 예는 Activity.RequestWindowFeature(WindowFeatures featureId)입니다.

이는 함께 속하는 상수와 이러한 상수에서 사용하는 API를 파악하기 위한 매우 수동적인 프로세스입니다. 열거형으로 더 잘 표현될 수 있는 API에 사용되는 상수에 대한 버그를 제출하세요.