xamarinxamarin.androidproguardxamarin-linker

How can I correctly exclude some libraries so that they are not linked in Xamarin.Android?


I have an app that is working fine but since I cannot link the libraries (because it causes an abnormal behavior) it becomes significantly bigger (20 MiB more). The main issue happens with one of my own libraries:

https://www.nuget.org/packages/Xamarin-MaterialSearchBar/

I “excluded” the package and its dependencies:

Xamarin.AndroidX.AppCompat;Xamarin.AndroidX.ConstraintLayout;Xamarin.AndroidX.RecyclerView;Xamarin.AndroidX.CardView

But the “linking” options behave quite odd because they are not excluded neither in linking all or only linking SDK only. The animations got somehow mad and they are working in a "particular" way, the hamburger menu transforms into an arrow when it shouldn’t and vice-versa and if I do click on the X, it just removes the X forever and doesn’t clean. The following image can give you an idea of the abnormal behavior.

preview

This is the expected behavior and normal when I don't link anything:

preview

The app only works properly when I use Don’t Link. However, I want to understand what I must exclude (or how to do it properly) or change to avoid this issue. At this point, I tried excluding it in the ProGuard file, in the “exclusion” section, etc. but nothing works.

In my ProGuard file, I have these combinations:

-keep class androidx.work.** { *; }
-keep class androidx.concurrent.** { *; }
-keep class androidx.tracing.** { *; }
-keep class androidx.paging.** { *; }
-keep class com.google.android.gms.** { *; }
-keep class androidx.appcompat.widget.** { *; }
-keep class com.google.android.material.** { *; }

And I also tried to add:

-keep class tk.supernovaic.MaterialSearchBar.** { *; }

But the results were exactly the same. I don't know what I can exclude being honest. Any idea?

P.S.:

I can create the package to be published, this is not my problem.

My library is open-source if you want to give me any advice on how to change this logic and fix this problem:

https://github.com/FANMixco/Xamarin-SearchBar

It's important to highlight that I wrote this library in C# and Xamarin. This is not the binding of another one.

In my opinion, the issue happens in this section:

public void OnClick(View v)
{
    int id = v.Id;
    if (id == Id)
    {
        if (!IsSearchEnabled)
        {
            EnableSearch();
        }
    }
    else if (id == Resource.Id.mt_arrow)
    {
        DisableSearch();
    }
    else if (id == Resource.Id.mt_search)
    {
        if (ListenerExists())
        {
            OnSearchActionListener.OnButtonClicked(BUTTON_SPEECH);
        }
    }
    else if (id == Resource.Id.mt_clear)
    {
        SearchEdit.Text = "";
    }
    else if (id == Resource.Id.mt_menu)
    {
        PopupMenu.Show();
    }
    else if (id == Resource.Id.mt_nav)
    {
        int button = IsSearchEnabled ? BUTTON_BACK : BUTTON_NAVIGATION;
        if (IsSearchEnabled)
        {
            DisableSearch();
        }
        if (ListenerExists())
        {
            OnSearchActionListener.OnButtonClicked(button);
        }
    }
}

Update 1:

I have added to my library:

[Preserve(AllMembers = true)]

[assembly: LinkerSafe]

And this to my project:

--linkskip=tk.supernovaic.MaterialSearchBar

And the bug persists; therefore, it should be related to the animations library or something like that.

Update 2:

I have tried also:

-keep class android.animation.ObjectAnimator.** { *; } -keep class **.R$* { public static <fields>; }

and:

-keep class android.animation.** { *; }

Both still fails but it seems it´s related to the animations:

https://github.com/FANMixco/Xamarin-SearchBar/blob/master/tk.supernovaic.MaterialSearchBar/Resources/animator/menu_to_back_rotation.xml

Also, I added --linkskip=android.animation --linkskip=tk.supernovaicMaterialSearchBar without any result.

Update 3:

I copied the files from my lib to my project and kept all previous rules without any positive result.

Update 4:

I updated my library and app and added a new folder in the Resources called raw that contains a keep.xml with the following code:

<?xml version="1.0" encoding="UTF-8" ?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@anim/fade_in_left,@anim/fade_in_right,@anim/fade_out,@anim/fade_out_left,@animator/back_to_menu_morph,@animator/back_to_menu_rotation,@animator/menu_to_back_morph,@animator/menu_to_back_rotation" />

And added to my ProGuard:

-keep class **.R
-keepclassmembers class tk.supernovaic.MaterialSearchBar.* {
    <fields>;
    <init>();
    <methods>;
}

All this without any positive result. The bug persists.

Update 5:

I have raised a ticket to Microsoft because I'm almost convinced this is a bug related to the linker itself:

https://developercommunity.visualstudio.com/t/xamarinandroid-linking-libs-not-working-as-expecte/1482147?from=email

My conclusions are based on these three points:

  1. I added the original files (animations) to the project.
  2. I added a rule to keep the XMLs in the project and library.
  3. I added rules to preserve the R classes and everything (resources) in the ProGuard file.

It makes no sense that with the previous 3 points the animations are not preserved, in my opinion. If you have any other thoughts feel free to share them, I'm open to listening to them.

Update 6:

I also opened a ticket in GitHub because I believe it's a bug:

https://github.com/xamarin/xamarin-android/issues/6156


Solution

  • After a lot of updates and running between StackOverflow, GitHub, Visual Studio and the Microsoft Forums, I was able to find the problem:

    dellis1972 Changing this line https://github.com/FANMixco/Xamarin-SearchBar/blob/master/tk.supernovaic.MaterialSearchBar/MaterialSearchBar.cs#L633 to be.

    if (NavIcon.Drawable is AnimatedVectorDrawable a) Fixes the issue. For some reason the IAnimatable interface is not supported after the linker is run.

    fanmixco Hi @dellis1972, I'm going to check it. Thanks. However, is it still a bug to be reported, isn't it? Because it allows the icons to be shown properly but the animation doesn't happen.

    dellis1972 It is still a bug, but you have a work around for now. We'll need to look at see why the interface is removed as part of the link step.

    ok. so its not a linker bug, but I understand what is happening in this case.

    So the code in the MaterialSearchBar library is relying on the AnimatedVectorDrawable class deal with its animations. However these is no code in the library which actually uses it directly. So the type is linked out as part of the SdkOnly/Full linker step. The java side of the code will still exist , but all the glue on the C# side which will react to events being raised from the java side is all gone. So when you access the NavIcon.Drawable property you just get a Android.Graphics.Drawable type back which does not support IAnimatable, because the actual type which the java type is , doesn't exist on the C# side.

    The reason why my workaround works is that is stops the type from being linked away. So you can either use the work around I provided or add something like this to the MaterialSearchBar class

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

    #pragma warning restore 0219, 0649 as described in https://learn.microsoft.com/en-us/xamarin/android/deploy-test/linker#falseflag.

    So its not a bug, its how the linker works. There isn't anything we can really fix on this side. If the code is not directly using a type, it will be removed. The code above fools the linker into thinking the type is used.

    Source: https://github.com/xamarin/xamarin-android/issues/6156

    I updated my library and added the recommended workaround. I still believe it's a bug but my library is working properly as my app.

    https://www.nuget.org/packages/Xamarin-MaterialSearchBar/