Udostępnij za pośrednictwem


Rozwiązywanie problemów z powiązaniami

Ważne

Obecnie badamy użycie powiązań niestandardowych na platformie Xamarin. Weź udział w tej ankiecie , aby poinformować o przyszłych wysiłkach programistycznych.

W tym artykule podsumowano typowe błędy serwera, które mogą wystąpić podczas generowania powiązań, wraz z możliwymi przyczynami i sugerowanymi sposobami ich rozwiązywania.

Omówienie

Powiązanie biblioteki systemu Android ( aar lub .jar) jest rzadko prostą sprawą. Zwykle wymaga to dodatkowego wysiłku w celu wyeliminowania problemów, które wynikają z różnic między językiem Java i platformą .NET. Te problemy uniemożliwią programowi Xamarin.Android powiązanie biblioteki systemu Android i wyświetlenie ich jako komunikatów o błędach w dzienniku kompilacji. Ten przewodnik zawiera kilka wskazówek dotyczących rozwiązywania problemów, wymienienie niektórych typowych problemów/scenariuszy i udostępnienie możliwych rozwiązań do pomyślnego powiązania biblioteki systemu Android.

Podczas tworzenia powiązania istniejącej biblioteki systemu Android należy pamiętać o następujących kwestiach:

  • Zależności zewnętrzne dla biblioteki — wszystkie zależności Języka Java wymagane przez bibliotekę systemu Android muszą być uwzględnione w projekcie Xamarin.Android jako ReferenceJar lub EmbeddedReferenceJar.

  • Poziom interfejsu API systemu Android przeznaczony dla biblioteki systemu Android — nie można "obniżyć" poziomu interfejsu API systemu Android. Upewnij się, że projekt powiązania platformy Xamarin.Android jest przeznaczony dla tego samego poziomu interfejsu API (lub wyższego) co biblioteka systemu Android.

  • Wersja zestawu JDK systemu Android, która została użyta do spakowania biblioteki systemu Android — błędy powiązania mogą wystąpić, jeśli biblioteka systemu Android została skompilowana z inną wersją zestawu JDK niż używana przez platformę Xamarin.Android. Jeśli to możliwe, ponownie skompiluj bibliotekę systemu Android przy użyciu tej samej wersji zestawu JDK używanego przez instalację zestawu Xamarin.Android.

Pierwszym krokiem rozwiązywania problemów z powiązaniem biblioteki Xamarin.Android jest włączenie diagnostycznych danych wyjściowych programu MSBuild. Po włączeniu danych wyjściowych diagnostycznych ponownie skompiluj projekt powiązania platformy Xamarin.Android i sprawdź dziennik kompilacji, aby znaleźć wskazówki dotyczące przyczyny problemu.

Może również okazać się pomocne, aby dekompilować bibliotekę systemu Android i zbadać typy i metody, które platforma Xamarin.Android próbuje powiązać. W dalszej części tego przewodnika opisano to bardziej szczegółowo.

Dekompilowanie biblioteki systemu Android

Inspekcja klas i metod klas Języka Java może dostarczyć cennych informacji, które pomogą w powiązaniu biblioteki. JD-GUI to graficzne narzędzie, które może wyświetlać kod źródłowy Języka Java z plików CLASS zawartych w pliku JAR. Można go uruchomić jako autonomiczną aplikację lub jako wtyczkę dla środowiska IntelliJ lub Eclipse.

Aby dekompilować bibliotekę systemu Android, otwórz plik . Plik JAR z dekompilerem języka Java. Jeśli biblioteka jest biblioteką . Plik AAR jest niezbędny do wyodrębnienia pliku classes.jar z pliku archiwum. Poniżej przedstawiono przykładowy zrzut ekranu przedstawiający używanie graficznego interfejsu UŻYTKOWNIKA JD do analizowania pliku JAR Picassa :

Using the Java Decompiler to analyze picasso-2.5.2.jar

Po dekompilowanej bibliotece systemu Android sprawdź kod źródłowy. Ogólnie rzecz biorąc, poszukaj polecenia :

  • Klasy, które mają cechy zaciemnienia — cechy zaciemnionych klas obejmują:

    • Nazwa klasy zawiera element $, tj. $.class
    • Nazwa klasy jest całkowicie naruszona z małymi literami, tj. klasa
  • import instrukcje dla bibliotek niereferencyjnych — zidentyfikuj niereferencję biblioteki i dodaj te zależności do projektu powiązania platformy Xamarin.Android za pomocą akcji kompilacji ReferenceJar lub EmbedddedReferenceJar.

Uwaga

Dekompilowanie biblioteki Języka Java może być zabronione lub podlega ograniczeniom prawnym na podstawie przepisów lokalnych lub licencji, na podstawie której opublikowano bibliotekę Języka Java. W razie potrzeby należy zarejestrować usługi prawnika przed podjęciem próby dekompilowania biblioteki Języka Java i sprawdzenia kodu źródłowego.

Sprawdzanie API.XML

W ramach tworzenia projektu powiązania platforma Xamarin.Android wygeneruje nazwę pliku XML obj/Debug/api.xml:

Generated api.xml under obj/Debug

Ten plik zawiera listę wszystkich interfejsów API Języka Java, które próbuje powiązać platforma Xamarin.Android. Zawartość tego pliku może pomóc zidentyfikować brakujące typy lub metody, zduplikowane powiązanie. Chociaż inspekcja tego pliku jest żmudna i czasochłonna, może zapewnić wskazówki dotyczące tego, co może powodować problemy z powiązaniem. Na przykład api.xml może ujawnić, że właściwość zwraca niewłaściwy typ lub że istnieją dwa typy, które mają taką samą nazwę zarządzaną.

Znane problemy

Ta sekcja zawiera listę niektórych typowych komunikatów o błędach lub objawów występujących podczas próby powiązania biblioteki systemu Android.

Problem: Niezgodność wersji języka Java

Czasami typy nie zostaną wygenerowane lub mogą wystąpić nieoczekiwane awarie, ponieważ używasz nowszej lub starszej wersji języka Java w porównaniu z tym, z czym została skompilowana biblioteka. Ponownie skompiluj bibliotekę systemu Android przy użyciu tej samej wersji zestawu JDK, z którego korzysta projekt Xamarin.Android.

Problem: Wymagana jest co najmniej jedna biblioteka Języka Java

Zostanie wyświetlony błąd "wymagana jest co najmniej jedna biblioteka Języka Java", mimo że element . Plik JAR został dodany.

Możliwe przyczyny:

Upewnij się, że akcja kompilacji jest ustawiona na EmbeddedJar. Ponieważ istnieje wiele akcji kompilacji dla programu . Pliki JAR (takie jak InputJar, EmbeddedJarReferenceJar i EmbeddedReferenceJar), generator powiązań nie może automatycznie odgadnąć, który z nich ma być używany domyślnie. Aby uzyskać więcej informacji na temat akcji kompilacji, zobacz Build Actions (Akcje kompilacji).

Problem: Narzędzia powiązań nie mogą załadować elementu . Biblioteka JAR

Nie można załadować generatora biblioteki powiązań. Biblioteka JAR.

Możliwe przyczyny

Niektóre. Biblioteki JAR korzystające z zaciemnienia kodu (za pomocą narzędzi takich jak Proguard) nie mogą być ładowane przez narzędzia Języka Java. Ponieważ nasze narzędzie korzysta z odbicia języka Java i biblioteki inżynierii kodu bajtowego ASM, te narzędzia zależne mogą odrzucać zaciemnione biblioteki, podczas gdy narzędzia środowiska uruchomieniowego systemu Android mogą zostać przekazane. Obejściem tego problemu jest ręczne powiązanie tych bibliotek zamiast używania generatora powiązań.

Problem: Brak typów języka C# w wygenerowanych danych wyjściowych.

Powiązanie .dll kompilacji, ale pomija niektóre typy języka Java lub wygenerowane źródło języka C# nie jest tworzone z powodu błędu informującego, że brakuje typów.

Możliwe przyczyny:

Ten błąd może wystąpić z kilku powodów wymienionych poniżej:

  • Powiązana biblioteka może odwoływać się do drugiej biblioteki Języka Java. Jeśli publiczny interfejs API dla powiązanej biblioteki używa typów z drugiej biblioteki, należy również odwołać się do powiązania zarządzanego dla drugiej biblioteki.

  • Istnieje możliwość, że biblioteka została wstrzyknięta z powodu odbicia języka Java, podobnie jak przyczyna powyższego błędu ładowania biblioteki, powodując nieoczekiwane ładowanie metadanych. Narzędzia platformy Xamarin.Android nie mogą obecnie rozwiązać tej sytuacji. W takim przypadku biblioteka musi być powiązana ręcznie.

  • Wystąpił błąd w środowisku uruchomieniowym platformy .NET 4.0, który nie mógł załadować zestawów, gdy powinien. Ten problem został rozwiązany w środowisku uruchomieniowym platformy .NET 4.5.

  • Język Java umożliwia wyprowadzanie klasy publicznej z klasy innej niż publiczna, ale nie jest to obsługiwane na platformie .NET. Ponieważ generator powiązań nie generuje powiązań dla klas innych niż publiczne, klasy pochodne, takie jak te, nie mogą być generowane poprawnie. Aby rozwiązać ten problem, usuń wpis metadanych dla tych klas pochodnych przy użyciu polecenia remove-node w Metadata.xml lub napraw metadane, które upublicznią klasę inną niż publiczna. Mimo że to drugie rozwiązanie utworzy powiązanie, tak aby źródło języka C# zostało skompilować, nie należy używać klasy innej niż publiczna.

    Na przykład:

    <attr path="/api/package[@name='com.some.package']/class[@name='SomeClass']"
        name="visibility">public</attr>
    
  • Narzędzia, które zaciemniają biblioteki Języka Java, mogą zakłócać działanie generatora powiązań platformy Xamarin.Android i możliwość generowania klas otoki języka C#. Poniższy fragment kodu pokazuje, jak zaktualizować Metadata.xml , aby usunąć nazwę klasy:

    <attr path="/api/package[@name='{package_name}']/class[@name='{name}']"
        name="obfuscated">false</attr>
    

Problem: Wygenerowane źródło języka C# nie jest kompilowane z powodu niezgodności typów parametrów

Wygenerowane źródło języka C# nie jest kompilowane. Typy parametrów metody przesłaniania nie są zgodne.

Możliwe przyczyny:

Platforma Xamarin.Android zawiera różne pola języka Java, które są mapowane na wyliczenia w powiązaniach języka C#. Mogą one powodować niezgodności typu w wygenerowanych powiązaniach. Aby rozwiązać ten problem, należy zmodyfikować sygnatury metod utworzone na podstawie generatora powiązań, aby używać wyliczenia. Aby uzyskać więcej informacji, zobacz Poprawianie wyliczenia.

Problem: NoClassDefFoundError w opakowaniu

java.lang.NoClassDefFoundError jest zgłaszany w kroku pakowania.

Możliwe przyczyny:

Najbardziej prawdopodobną przyczyną tego błędu jest to, że do projektu aplikacji musi zostać dodana obowiązkowa biblioteka Języka Java (csproj). . Pliki JAR nie są automatycznie rozwiązywane. Powiązanie biblioteki Języka Java nie zawsze jest generowane dla zestawu użytkownika, który nie istnieje na urządzeniu docelowym lub emulatorze (np. Google Mapy maps.jar). Tak nie jest w przypadku obsługi projektu biblioteki systemu Android jako biblioteki . Plik JAR jest osadzony w bibliotece dll.

Problem: Duplikowanie niestandardowych typów EventArgs

Kompilacja kończy się niepowodzeniem z powodu zduplikowanych niestandardowych typów EventArgs. Wystąpi błąd podobny do następującego:

error CS0102: The type `Com.Google.Ads.Mediation.DismissScreenEventArgs' already contains a definition for `p0'

Możliwe przyczyny:

Jest to spowodowane konfliktem między typami zdarzeń pochodzącymi z więcej niż jednego typu "odbiornika" interfejsu, który udostępnia metody o identycznych nazwach. Jeśli na przykład w poniższym przykładzie istnieją dwa interfejsy Języka Java, generator tworzy DismissScreenEventArgs zarówno dla elementu , jak MediationBannerListener i MediationInterstitialListener, co powoduje błąd.

// Java:
public interface MediationBannerListener {
    void onDismissScreen(MediationBannerAdapter p0);
}
public interface MediationInterstitialListener {
    void onDismissScreen(MediationInterstitialAdapter p0);
}

Jest to zgodnie z projektem, aby unikać długich nazw typów argumentów zdarzeń. Aby uniknąć tych konfliktów, wymagane jest przekształcenie niektórych metadanych. Edytuj transforms\Metadata.xml i dodaj argsType atrybut na jednym z interfejsów (lub w metodzie interfejsu):

<attr path="/api/package[@name='com.google.ads.mediation']/
        interface[@name='MediationBannerListener']/method[@name='onDismissScreen']"
        name="argsType">BannerDismissScreenEventArgs</attr>

<attr path="/api/package[@name='com.google.ads.mediation']/
        interface[@name='MediationInterstitialListener']/method[@name='onDismissScreen']"
        name="argsType">IntersitionalDismissScreenEventArgs</attr>

<attr path="/api/package[@name='android.content']/
        interface[@name='DialogInterface.OnClickListener']"
        name="argsType">DialogClickEventArgs</attr>

Problem: Klasa nie implementuje metody interfejsu

Zostanie wygenerowany komunikat o błędzie wskazujący, że wygenerowana klasa nie implementuje metody wymaganej dla interfejsu implementowanego przez wygenerowaną klasę. Jednak patrząc na wygenerowany kod, widać, że metoda jest zaimplementowana.

Oto przykład błędu:

obj\Debug\generated\src\Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.cs(8,23):
error CS0738: 'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter' does not
implement interface member 'Oauth.Signpost.Http.IHttpRequest.Unwrap()'.
'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.Unwrap()' cannot implement
'Oauth.Signpost.Http.IHttpRequest.Unwrap()' because it does not have the matching
return type of 'Java.Lang.Object'

Możliwe przyczyny:

Jest to problem, który występuje z powiązaniami metod Java z kowariantnymi typami zwracanymi. W tym przykładzie metoda musi zwrócić wartość Oauth.Signpost.Http.IHttpRequest.UnWrap()Java.Lang.Object. Jednak metoda Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.UnWrap() ma zwracany typ HttpURLConnection. Istnieją dwa sposoby rozwiązania tego problemu:

  • Dodaj deklarację klasy częściowej dla HttpURLConnectionRequestAdapter elementu i jawnie zaimplementuj polecenie IHttpRequest.Unwrap():

    namespace Oauth.Signpost.Basic {
        partial class HttpURLConnectionRequestAdapter {
            Java.Lang.Object OauthSignpost.Http.IHttpRequest.Unwrap() {
                return Unwrap();
            }
        }
    }
    
  • Usuń kowariancję z wygenerowanego kodu języka C#. Obejmuje to dodanie następującej transformacji do transforms\Metadata.xml , co spowoduje, że wygenerowany kod języka C# będzie miał zwracany typ Java.Lang.Object:

    <attr
        path="/api/package[@name='oauth.signpost.basic']/class[@name='HttpURLConnectionRequestAdapter']/method[@name='unwrap']"
        name="managedReturn">Java.Lang.Object
    </attr>
    

Problem: Kolizje nazw w klasach wewnętrznych / właściwości

Konfliktowy wgląd w dziedziczone obiekty.

W języku Java nie jest wymagane, aby klasa pochodna miała taką samą widoczność jak jej element nadrzędny. Język Java po prostu naprawi to za Ciebie. W języku C# musi to być jawne, dlatego należy upewnić się, że wszystkie klasy w hierarchii mają odpowiednią widoczność. W poniższym przykładzie pokazano, jak zmienić nazwę pakietu Java z com.evernote.android.job na Evernote.AndroidJob:

<!-- Change the visibility of a class -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']" name="visibility">public</attr>

<!-- Change the visibility of a method -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']/method[@name='MethodName']" name="visibility">public</attr>

Problem: Biblioteka wymagana przez powiązanie nie jest ładowana

Niektóre projekty powiązań mogą również zależeć od funkcjonalności w bibliotece .so . Możliwe, że platforma Xamarin.Android nie załaduje automatycznie biblioteki .so . Po wykonaniu opakowanego kodu Java narzędzie Xamarin.Android zakończy się niepowodzeniem wywołania JNI i komunikatem o błędzie java.lang.UnsatisfiedLinkError: nie znaleziono metody natywnej: pojawi się w logcat dla aplikacji.

Rozwiązaniem tego problemu jest ręczne załadowanie biblioteki .so z wywołaniem metody Java.Lang.JavaSystem.LoadLibrary. Na przykład przy założeniu, że projekt platformy Xamarin.Android ma udostępnioną bibliotekę libpocketsphinx_jni.so uwzględnioną w projekcie powiązania z akcją kompilacji EmbeddedNativeLibrary, następujący fragment kodu (wykonany przed użyciem biblioteki udostępnionej) załaduje bibliotekę .so :

Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni");

Podsumowanie

W tym artykule wymieniono typowe problemy z rozwiązywaniem problemów związanych z powiązaniami Języka Java i wyjaśniono, jak je rozwiązać.