安卓6.0(棉花糖)svg-android库中getDeclaredField()的静态初始化异常

12

我对这段代码遇到了很严重的问题,它来自于svg-android

public class ParserHelper {

private static final Field STRING_CHARS;
static {
    try {
        STRING_CHARS = String.class.getDeclaredField("value"); //<-- exception here
        STRING_CHARS.setAccessible(true);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private final char[] s;
private final int n;
private char current;
public int pos;

public ParserHelper(String str, int pos) {
    try {
        this.s = (char[]) STRING_CHARS.get(str); 
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    this.pos = pos;
    n = s.length;
    current = s[pos];
}

STRING_CHARS = String.class.getDeclaredField("value"); 抛出异常。

10-09 10:25:58.240: E/AndroidRuntime(3430): Caused by: java.lang.RuntimeException: java.lang.NoSuchFieldException: No field value in class Ljava/lang/String; (declaration of 'java.lang.String' appears in /system/framework/core-libart.jar)

只在 Android 6.0 Marshmallow 中出现,我无法继续工作。有什么想法吗?

已解决:我没有解决静态初始化问题,但是我改变了char[] s的初始化:

public class ParserHelper {

//  private static final Field STRING_CHARS;
//  static {
//      try {
//          STRING_CHARS = String.class.getDeclaredField("value");
//          STRING_CHARS.setAccessible(true);
//      } catch (Exception e) {
//          throw new RuntimeException(e);
//      }
//  }

    private final char[] s;
    private final int n;
    private char current;
    public int pos;

    public ParserHelper(String str, int pos) {
        try {
            s = new char[str.length()];
            str.getChars(0, str.length(), this.s, 0); //<-- here
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        this.pos = pos;
        n = s.length;
        current = s[pos];
    }

你不需要使用一个字符串实例来执行反射吗?我对此不是很专业。 - Nanoc
@Nanoc 我也一样... - Seraphim's
那么请尝试这个,然后告诉我 "new String("Hello").class.getDeclaredField("value");"。 - Nanoc
@Nanoc 无法编译,.class 文件出错,“语法错误,标记为 class...” - Seraphim's
好的,这是一个愚蠢的错误,请使用getClass()而不是.class "new String("Hello").getClass().getDeclaredField("value");" - Nanoc
通过反射访问私有字段是有风险的。它们之所以是私有的,是有原因的。 - Henry
1个回答

15

看起来 String 的私有字段叫做 value,它保存字符数组的名称在 Marshmallow 中已更改为 ASCII。 因此,在这些行中(取自 com.larvalabs.svgandroid.ParserHelper 类),您会遇到 RuntimeException

    try {
        STRING_CHARS = String.class.getDeclaredField("value");
        STRING_CHARS.setAccessible(true);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
svgandroid已经被停止维护,因此项目作者几乎没有机会修复这个问题并将新的jar推送到maven。您可以创建自己的svgandroid库分支,合并pull-request,构建jar并从现在开始使用它,而不是maven化版本。

或者,您可以更进一步,自己将修复后的版本推送到mvnrepository。 :)


1
哦,亲爱的...如果有人能为这个库提供修复,那就太棒了。我认为很多人在使用它,现在他们所有的应用程序都在marshmallow上崩溃。 - Deinlandel
虽然你的答案确实解决了问题,但我想知道它是否会对性能产生任何影响。我认为这样做是为了更高效,因为新方法会创建一个副本,而通过反射直接访问它。 - Gabriel Falcone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接