與 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]) (...)
等警告,但應用程式構建和庫可以正常工作。