给定 VectorDrawable 的字节数组,我如何从中创建 VectorDrawable 的实例?
Given byte-array of VectorDrawable, how can I create an instance of VectorDrawable from it?
背景
My app 在设备的存储中查找 APK 文件,显示有关它们的信息并允许对其执行操作。
问题
事情是,自从 Google 添加了关于存储的新限制,我试图找到关于如何在没有存储权限的情况下解析 APK 文件的解决方案(因为框架只能处理文件路径)。现在我使用 special legacy flag (requestLegacyExternalStorage) to allow me 来处理文件路径,但这是一个临时解决方案,直到 Android.
的下一个版本
我已经尝试并发现了什么
目前,使用 workaround and a the library (link here),我成功获取了基本信息和应用程序名称,但对于图标,它变得混乱:错误的限定符,可以 return PNG 代替 VectorDrawable,并且VectorDrawable 只是一个字节数组...
但即使我忽略了它的问题,我也尝试查看各种 VectorDrawable 函数及其 android-x 函数(实际上是 looked very promising),以尝试创建一个实例我得到的。出于某种原因,我尝试的每个功能都没有帮助。
问题
是否可以从字节码动态加载VectorDrawable?现在我打算使用 this library 它自己解析 APK(因为遗憾的是 Google 正计划添加访问文件的限制,以使用可怕的 SAF),但遗憾的是对于 VectorDrawable 它只有字节数组...
明确一点:我说的是从 SAF 访问 APK 文件(不是已安装的文件,因为获取此信息仍然可以正常工作)。有了存储许可,它工作正常(当然还有已安装的应用程序)。参见 here。当您尝试将 SAF 与普通框架一起使用时,您将得到糟糕的结果:一个以包名称作为应用程序名称的字符串,并将默认图标作为应用程序图标(或更糟:异常)。
另请注意:我知道我可以将文件复制到我自己的应用程序的存储中并仅使用框架,但这不是一个好的解决方案,因为如果我想显示有关设备上所有 APK 文件的信息,那将是一种浪费space 和时间,只是为了获取图标和应用程序名称...我需要更直接的方法。应用程序处理速度最快的一种。
如果您拥有的矢量可绘制对象的字节数组具有二进制 XML 形式,那么您应该能够通过将数组传递给我从 previous answer 中获取的以下方法来创建它:
/**
* Create a vector drawable from a binary XML byte array.
* @param context Any context.
* @param binXml Byte array containing the binary XML.
* @return The vector drawable or null it couldn't be created.
*/
public static Drawable getVectorDrawable(@NonNull Context context, @NonNull byte[] binXml) {
try {
// Get the binary XML parser (XmlBlock.Parser) and use it to create the drawable
// This is the equivalent of what AssetManager#getXml() does
@SuppressLint("PrivateApi")
Class<?> xmlBlock = Class.forName("android.content.res.XmlBlock");
Constructor xmlBlockConstr = xmlBlock.getConstructor(byte[].class);
Method xmlParserNew = xmlBlock.getDeclaredMethod("newParser");
xmlBlockConstr.setAccessible(true);
xmlParserNew.setAccessible(true);
XmlPullParser parser = (XmlPullParser) xmlParserNew.invoke(
xmlBlockConstr.newInstance((Object) binXml));
if (Build.VERSION.SDK_INT >= 24) {
return Drawable.createFromXml(context.getResources(), parser);
} else {
// Before API 24, vector drawables aren't rendered correctly without compat lib
final AttributeSet attrs = Xml.asAttributeSet(parser);
int type = parser.next();
while (type != XmlPullParser.START_TAG) {
type = parser.next();
}
return VectorDrawableCompat.createFromXmlInner(context.getResources(), parser, attrs, null);
}
} catch (Exception e) {
Log.e(TAG, "Vector creation failed", e);
}
return null;
}
我还没有测试过它,但我看不出如果你的字节数组真的是矢量可绘制对象的二进制 XML 它不能工作的原因,这就是 XmlPullParser
和 VectorDrawable
期望解析。
编辑: 我尝试使用 XmlPullParser 来创建可绘制对象,如下所示:
val xml = """
|<vector xmlns:android="http://schemas.android.com/apk/res/android"
| android:width="24dp"
| android:height="24dp"
| android:viewportWidth="24.0"
| android:viewportHeight="24.0">
| <path
| android:pathData="M9.5,3A6.5,6.5 0,0 0,3 9.5A6.5,6.5 0,0 0,9.5 16A6.5,6.5 0,0 0,13.33 14.744L18.586,20L20,18.586L14.742,13.328A6.5,6.5 0,0 0,16 9.5A6.5,6.5 0,0 0,9.5 3zM9.5,5A4.5,4.5 0,0 1,14 9.5A4.5,4.5 0,0 1,9.5 14A4.5,4.5 0,0 1,5 9.5A4.5,4.5 0,0 1,9.5 5z"
| android:fillColor="#000000"/>
|</vector>
""".trimMargin()
val parser = XmlPullParserFactory.newInstance().newPullParser()
parser.setInput(xml.reader())
val drawable = Drawable.createFromXml(resources, parser, theme)
iconView.setImageDrawable(drawable)
然而,这会失败并出现异常:java.lang.ClassCastException:android.util.XmlPullAttributes 无法转换为 android.content.res.XmlBlock$Parser。所以这毕竟不可能回答你的评论。
背景
My app 在设备的存储中查找 APK 文件,显示有关它们的信息并允许对其执行操作。
问题
事情是,自从 Google 添加了关于存储的新限制,我试图找到关于如何在没有存储权限的情况下解析 APK 文件的解决方案(因为框架只能处理文件路径)。现在我使用 special legacy flag (requestLegacyExternalStorage) to allow me 来处理文件路径,但这是一个临时解决方案,直到 Android.
的下一个版本我已经尝试并发现了什么
目前,使用 workaround and a the library (link here),我成功获取了基本信息和应用程序名称,但对于图标,它变得混乱:错误的限定符,可以 return PNG 代替 VectorDrawable,并且VectorDrawable 只是一个字节数组...
但即使我忽略了它的问题,我也尝试查看各种 VectorDrawable 函数及其 android-x 函数(实际上是 looked very promising),以尝试创建一个实例我得到的。出于某种原因,我尝试的每个功能都没有帮助。
问题
是否可以从字节码动态加载VectorDrawable?现在我打算使用 this library 它自己解析 APK(因为遗憾的是 Google 正计划添加访问文件的限制,以使用可怕的 SAF),但遗憾的是对于 VectorDrawable 它只有字节数组...
明确一点:我说的是从 SAF 访问 APK 文件(不是已安装的文件,因为获取此信息仍然可以正常工作)。有了存储许可,它工作正常(当然还有已安装的应用程序)。参见 here。当您尝试将 SAF 与普通框架一起使用时,您将得到糟糕的结果:一个以包名称作为应用程序名称的字符串,并将默认图标作为应用程序图标(或更糟:异常)。 另请注意:我知道我可以将文件复制到我自己的应用程序的存储中并仅使用框架,但这不是一个好的解决方案,因为如果我想显示有关设备上所有 APK 文件的信息,那将是一种浪费space 和时间,只是为了获取图标和应用程序名称...我需要更直接的方法。应用程序处理速度最快的一种。
如果您拥有的矢量可绘制对象的字节数组具有二进制 XML 形式,那么您应该能够通过将数组传递给我从 previous answer 中获取的以下方法来创建它:
/**
* Create a vector drawable from a binary XML byte array.
* @param context Any context.
* @param binXml Byte array containing the binary XML.
* @return The vector drawable or null it couldn't be created.
*/
public static Drawable getVectorDrawable(@NonNull Context context, @NonNull byte[] binXml) {
try {
// Get the binary XML parser (XmlBlock.Parser) and use it to create the drawable
// This is the equivalent of what AssetManager#getXml() does
@SuppressLint("PrivateApi")
Class<?> xmlBlock = Class.forName("android.content.res.XmlBlock");
Constructor xmlBlockConstr = xmlBlock.getConstructor(byte[].class);
Method xmlParserNew = xmlBlock.getDeclaredMethod("newParser");
xmlBlockConstr.setAccessible(true);
xmlParserNew.setAccessible(true);
XmlPullParser parser = (XmlPullParser) xmlParserNew.invoke(
xmlBlockConstr.newInstance((Object) binXml));
if (Build.VERSION.SDK_INT >= 24) {
return Drawable.createFromXml(context.getResources(), parser);
} else {
// Before API 24, vector drawables aren't rendered correctly without compat lib
final AttributeSet attrs = Xml.asAttributeSet(parser);
int type = parser.next();
while (type != XmlPullParser.START_TAG) {
type = parser.next();
}
return VectorDrawableCompat.createFromXmlInner(context.getResources(), parser, attrs, null);
}
} catch (Exception e) {
Log.e(TAG, "Vector creation failed", e);
}
return null;
}
我还没有测试过它,但我看不出如果你的字节数组真的是矢量可绘制对象的二进制 XML 它不能工作的原因,这就是 XmlPullParser
和 VectorDrawable
期望解析。
编辑: 我尝试使用 XmlPullParser 来创建可绘制对象,如下所示:
val xml = """
|<vector xmlns:android="http://schemas.android.com/apk/res/android"
| android:width="24dp"
| android:height="24dp"
| android:viewportWidth="24.0"
| android:viewportHeight="24.0">
| <path
| android:pathData="M9.5,3A6.5,6.5 0,0 0,3 9.5A6.5,6.5 0,0 0,9.5 16A6.5,6.5 0,0 0,13.33 14.744L18.586,20L20,18.586L14.742,13.328A6.5,6.5 0,0 0,16 9.5A6.5,6.5 0,0 0,9.5 3zM9.5,5A4.5,4.5 0,0 1,14 9.5A4.5,4.5 0,0 1,9.5 14A4.5,4.5 0,0 1,5 9.5A4.5,4.5 0,0 1,9.5 5z"
| android:fillColor="#000000"/>
|</vector>
""".trimMargin()
val parser = XmlPullParserFactory.newInstance().newPullParser()
parser.setInput(xml.reader())
val drawable = Drawable.createFromXml(resources, parser, theme)
iconView.setImageDrawable(drawable)
然而,这会失败并出现异常:java.lang.ClassCastException:android.util.XmlPullAttributes 无法转换为 android.content.res.XmlBlock$Parser。所以这毕竟不可能回答你的评论。