与 ProGuard 和 Linker 相关的神秘错误
你做了一个很棒的应用程序并在 Debug 中进行了测试,效果很好。一切都很好!
但是,你决定准备发布应用程序。你设置了 MultiDex,ProGuard 和 Linker,然后它就停止了工作。
本教程旨在帮助你找出可能导致神秘错误的 ProGuard 和 Linker 相关的常见问题。
了解 Xamarin.Linker
Xamarin.Linker 是构建过程中的一个工具,它可以从 .NET 代码中删除未使用的代码和类 (而不是 Java 代码) 。在项目的属性 - > Android 选项 - >链接器中,将有一个选项框链接选项:
无 :没有删除代码。
仅限 Sdk 程序集 :此选项使 Xamarin.Linker 仅在 Xamarin 库中检查未使用的代码。这个选项很安全。
Sdk 和用户程序集 :此选项使 Xamarin.Linker 检查 Xamarin 库和项目代码(包括 PCL,Xamarin 组件和 NuGet 包)中未使用的代码。这个选项并不总是安全的!
使用 Sdk 和用户程序集选项时,Xamarin.Linker 可能会认为部分代码在实际使用时非常未使用! 这可能会导致某些库停止正常工作并导致应用中出现错误。
要使 Xamarin.Linker 不删除代码,有 3 个选项:
- 将链接选项设置为无或“仅限 Sdk 程序集”;
- 跳过链接组件;
- 使用保留属性。
示例 2.跳过链接程序集:
在下面的示例中,使用 Xamarin.Linker 导致 NuGet 包( Octokit )可以正常工作,因为它无法再连接到 Internet:
[0:] ERROR
[0:] SOURCE: mscorlib
[0:] MESSAGE: Object reference not set to an instance of an object.
[0:] STACK TRACE: at Octokit.PocoJsonSerializerStrategy.DeserializeObject (System.Object value, System.Type type) [0x003d8] in D:\repos\octokit.net\Octokit\SimpleJson.cs:1472
at Octokit.Internal.SimpleJsonSerializer+GitHubSerializerStrategy.DeserializeObject (System.Object value, System.Type type) [0x001c3] in D:\repos\octokit.net\Octokit\Http\SimpleJsonSerializer.cs:165
at Octokit.SimpleJson.DeserializeObject (System.String json, System.Type type, Octokit.IJsonSerializerStrategy jsonSerializerStrategy) [0x00007] in D:\repos\octokit.net\Octokit\SimpleJson.cs:583
at Octokit.SimpleJson.DeserializeObject[T] (System.String json, Octokit.IJsonSerializerStrategy jsonSerializerStrategy) [0x00000] in D:\repos\octokit.net\Octokit\SimpleJson.cs:595
at Octokit.Internal.SimpleJsonSerializer.Deserialize[T] (System.String json) [0x00000] in D:\repos\octokit.net\Octokit\Http\SimpleJsonSerializer.cs:21
at Octokit.Internal.JsonHttpPipeline.DeserializeResponse[T] (Octokit.IResponse response) [0x000a7] in D:\repos\octokit.net\Octokit\Http\JsonHttpPipeline.cs:62
at Octokit.Connection+<Run>d__54`1[T].MoveNext () [0x0009c] in D:\repos\octokit.net\Octokit\Http\Connection.cs:574
--- End of stack trace from previous location where exception was thrown ---
要使库再次开始工作,必须在 Skip links assemblies 字段中添加包引用名称,该字段位于项目 - > Properties - > Android Options - > Linker 中,如下图所示:
之后,库开始工作,没有任何问题。
示例 3.使用 Preserve 属性:
Xamarin.Linker 将未使用的代码视为项目核心中模型类的代码。
要在链接过程中保留类,可以使用保留属性。
首先,在项目核心中创建一个名为 PreserveAttribute.cs 的类,插入以下代码并将命名空间替换为项目的命名空间:
PreserveAttribute.cs:
namespace My_App_Core.Models
{
public sealed class PreserveAttribute : System.Attribute
{
public bool AllMembers;
public bool Conditional;
}
}
在项目核心的每个模型类中,插入 Preserve 属性,如下例所示:
Country.cs:
using System;
using System.Collections.Generic;
namespace My_App_Core.Models
{
[Preserve(AllMembers = true)]
public class Country
{
public String name { get; set; }
public String ISOcode { get; set; }
[Preserve(AllMembers = true)]
public Country(String name, String ISOCode)
{
this.name = name;
this.ISOCode = ISOCode;
}
}
}
之后,链接过程将不再删除保留的代码。
了解 ProGuard
ProGuard 是构建过程中的一个工具,可以从 Java 代码中删除未使用的代码和类。它还混淆和优化代码。
但是,ProGuard 有时可能会删除它认为未使用的代码。为避免这种情况,开发人员必须调试应用程序(在 Android 设备监视器和 Visual Studio 调试中)并检测已删除的类,然后配置 ProGuard 配置文件以保留该类。
例
在下面的示例中,ProGuard 删除了 AXML 布局文件中使用的两个类(Android.Support.V7.Widget.FitWindowsLinearLayout 和 Android.Support.Design.Widget.AppBarLayout),但在代码中被视为未使用。在呈现活动布局时,删除导致 Java 代码中的 ClassNotFoundException:
layout_activitymain.axml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activitymain_drawerlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true" <!-- ### HERE ### -->
tools:openDrawer="start">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<!-- ### HERE ## -->
<android.support.design.widget.AppBarLayout
android:id="@+id/activitymain_appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
...
在 SetContentView 中创建布局时 LogCat 显示错误:
要修复此错误,必须将以下行添加到项目的 ProGuard 配置文件中:
-keep public class android.support.v7.widget.FitWindowsLinearLayout
-keep public class android.support.design.widget.AppBarLayout
之后,创建布局时不再显示错误。
ProGuard 警告
在构建项目后,ProGuard 有时会在错误列表中显示警告。虽然他们提出了一个问题,即你的应用程序是否正常,但并非所有警告都表明存在问题,特别是如果你的应用程序成功构建。
一个例子是使用 Picasso 库时:使用 ProGuard 时,这可能会显示 okio.Okio: can't find referenced class (...)
或 can't write resource [META-INF/MANIFEST.MF] (Duplicate zip entry [okhttp.jar:META-INF/MANIFEST.MF]) (...)
等警告,但应用程序构建和库可以正常工作。