java.lang.IncompatibleClassChangeError: Ожидалось, что метод будет прямого типа, но вместо этого было обнаружено, что он имеет тип virtual nut - PullRequest
0 голосов
/ 22 мая 2019

Я сделал обновление для плагина Android Gradle до 3.2.1 и собрал инструменты до 28.0.3.

Начал получать сбой во время выполнения при использовании функции обратной связи Android с приложением.

Ошибка: «java.lang.IncompatibleClassChangeError: метод 'void android.support.v4.widget.ExploreByTouchHelper.updateHoveredVirtualView (int)' ожидался как прямой тип, но вместо этого был обнаружен виртуальный тип"

Я проверил декс с помощью dexdump

22              : (in Landroid/support/v4/widget/ExploreByTouchHelper;)

name          : 'updateHoveredVirtualView'

   type          : '(I)V'

   **access        : 0x0001 (PUBLIC)  code**          -       

   registers     : 4       

   ins           : 2       

   outs          : 3       insns 

   size    : 20 16-bit code units       

   catches       : (none)       

   positions     :         0x0000 

line=612         0x0004 

line=613         0x0005 

line=616         0x0007 

line=617         0x0009 

line=621         0x000e 

line=622         0x0013 

line=624       

locals        :         0x0000 - 0x0014 reg=2 this 
Landroid/support/v4/widget/y;   

source_file_idx   : 14328 (SourceFile)

Здесь тип доступа метода void updateHoveredVirtualView (int) является общедоступным, но я проверил определение в файле ExploreTouchByHelper.java, и он является личным

Я использую следующие настройки proguard:

-repackageclasses ''

-allowaccessmodification

Если я удалил вышеуказанные правила, тогда тип доступа к методу становится закрытым, и проблема решена. Но я не хочу удалять allowaccessmodication, так как это вызывает проблемы с производительностью.

Кроме того, я нашел еще один вариант удаления следующих правил из proguard

#Preserve all View implementations, their special context constructors, and their setters.

-keep public class * extends android.view.View {

    public <init>(android.content.Context);

    public <init>(android.content.Context, android.util.AttributeSet);

    public <init>(android.content.Context, android.util.AttributeSet, int);

    public void set*(...);

}

# Preserve all classes that have special context constructors, and the

# constructors themselves.

-keepclasseswithmembers class * {

    public <init>(android.content.Context, android.util.AttributeSet);

}

\# Preserve all classes that have special context constructors, and the

# constructors themselves.

-keepclasseswithmembers class * {

    public <init>(android.content.Context, android.util.AttributeSet, int);

}

Удаление вышеуказанных правил также решает сбой, но я не уверен в побочных эффектах удаления. Может кто-нибудь объяснить, почему удаление вышеуказанных правил хранения препятствует тому, чтобы allowaccessmodification (Proguard) изменил тип доступа метода с частного на публичный?

Снимок ExploreTouchByHelper.java

package android.support.v4.widget;

import android.content.Context;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.SparseArrayCompat;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewParentCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.view.accessibility.AccessibilityNodeProviderCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import java.util.ArrayList;
import java.util.List;

public abstract class ExploreByTouchHelper
  extends AccessibilityDelegateCompat
{
  public static final int INVALID_ID = Integer.MIN_VALUE;
  public static final int HOST_ID = -1;
  private static final String DEFAULT_CLASS_NAME = "android.view.View";
  private static final Rect INVALID_PARENT_BOUNDS = new Rect(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);



  private final Rect mTempScreenRect = new Rect();
  private final Rect mTempParentRect = new Rect();
  private final Rect mTempVisibleRect = new Rect();
  private final int[] mTempGlobalRect = new int[2];


  private final AccessibilityManager mManager;


  private final View mHost;


  private MyNodeProvider mNodeProvider;


  int mAccessibilityFocusedVirtualViewId = Integer.MIN_VALUE;



  int mKeyboardFocusedVirtualViewId = Integer.MIN_VALUE;



  private int mHoveredVirtualViewId = Integer.MIN_VALUE;


  ......



  public ExploreByTouchHelper(@NonNull View host)
  {
    if (host == null) {
      throw new IllegalArgumentException("View may not be null");
    }

    mHost = host;

    Context context = host.getContext();
    mManager = ((AccessibilityManager)context.getSystemService("accessibility"));



    host.setFocusable(true);
    if (ViewCompat.getImportantForAccessibility(host) == 0)
    {
      ViewCompat.setImportantForAccessibility(host, 1);
    }
  }

  public final boolean dispatchHoverEvent(@NonNull MotionEvent event)
  {
    if ((!mManager.isEnabled()) || (!mManager.isTouchExplorationEnabled())) {
      return false;
    }

    switch (event.getAction()) {
    case 7: 
    case 9: 
      int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
      updateHoveredVirtualView(virtualViewId);
      return virtualViewId != Integer.MIN_VALUE;
    case 10: 
      if (mHoveredVirtualViewId != Integer.MIN_VALUE) {
        updateHoveredVirtualView(Integer.MIN_VALUE);
        return true;
      }
      return false;
    }
    return false;
  }

  private void updateHoveredVirtualView(int virtualViewId)
  {
    if (mHoveredVirtualViewId == virtualViewId) {
      return;
    }

    int previousVirtualViewId = mHoveredVirtualViewId;
    mHoveredVirtualViewId = virtualViewId;



    sendEventForVirtualView(virtualViewId, 128);
    sendEventForVirtualView(previousVirtualViewId, 256);
  }

  .....

}

Я вызываю API ExploreTouchByHelper :: dispatchHoverEvent () из кода приложения, который в свою очередь вызывает updateHoveredVirtualView () и вызывает сбой

...