我希望能够解析来自各种来源或各种来源的APK文件。我只想获得关于它们的非常具体的基本信息:
Android框架具有解析APK文件(PackageManager.getPackageArchiveInfo)的单一功能,但有2个缺点:
它需要一个文件路径。这并不总是可用,因为您可能需要处理Uri,或者您的APK文件位于某个ZIP文件中。
它无法处理拆分的APK文件。只有其中之一。如果在拆分的APK文件上尝试使用,则只会得到null。
因此,我试图找到一种可靠的Java&Kotlin解决方案,可以轻松处理它们。
我发现的每个解决方案都有其缺点。有些库我什至找不到使用方法,有些库在没有真实文件路径的情况下无法解析APK文件。其中一些只是创建新文件,而不是用Java / Kotlin返回对象。有些甚至还使用其他编程语言,这使我想知道是否可以使用它们。
到目前为止,我发现唯一足够出色的是"hsiafan" apk-parser,它在APK中仅需要两个文件:清单文件和资源文件。它有一些问题,但通常它可以为您提供我所提到的信息。
[正如我所写,它并不总是有效,所以我想回到基础知识,至少在我注意到它无法解析的情况下,至少在分割APK文件为标准APK的情况下。它不能很好处理的一些情况是:
和其他一些。
所以,我想再次尝试Android框架,但是这次有了一个新的主意:除了处理整个原始APK,我还可以根据当前的观察结果仅提取它真正需要的内容。
例如,如果我知道需要处理的资源,则只能将它们(以及一些需要的关键文件)复制到新的APK中,然后放弃其余的。这样可以减少仅解析APK并获取有关信息的复制/下载大量数据的需求。例如,如果APK文件为100MB,则当我们只需要一小部分文件时,就无需全部获取它。
首先,我想看看如何创建可以解析的新APK,所以现在我将原始APK(当前为当前应用程序)的所有条目复制到一个新文件中,然后选择解析它:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState != null)
return
thread {
val originalApplicationInfo = packageManager.getApplicationInfo(packageName, 0)
val filePath = originalApplicationInfo.publicSourceDir
val outputFile = File(cacheDir, "test.apk")
outputFile.parentFile!!.mkdirs()
outputFile.delete()
ZipFile(filePath).use { zipFile ->
ZipOutputStream(FileOutputStream(outputFile)).use { zipOutputStream ->
for (entry in zipFile.entries()) {
val name = entry.name
zipOutputStream.putNextEntry(ZipEntry(name))
zipFile.getInputStream(entry).use { it.copyTo(zipOutputStream.buffered()) }
zipOutputStream.closeEntry()
}
}
}
val originalLabel = originalApplicationInfo.loadLabel(packageManager)
val originalIcon: Drawable? = originalApplicationInfo.loadIcon(packageManager)
Log.d("AppLog", "originalPackageInfo: label:$originalLabel appIcon:${originalIcon?.javaClass?.simpleName}")
//
val packageArchiveInfo = packageManager.getPackageArchiveInfo(outputFile.absolutePath, 0)
val label = packageArchiveInfo?.applicationInfo?.loadLabel(packageManager)?.toString()
val appIcon = packageArchiveInfo?.applicationInfo?.loadIcon(packageManager)
Log.d("AppLog", "packageArchiveInfo!=null?${packageArchiveInfo != null} label:$label appIcon:${appIcon?.javaClass?.simpleName}")
}
}
}
确实生成了文件,但是由于packageArchiveInfo
为空,由于某些原因,Android框架无法解析它。
getPackageArchiveInfo
函数需要至少多少个文件集才能解析APK?更改复制代码自
zipFile.getInputStream(entry).use { it.copyTo(zipOutputStream.buffered()) }
to
zipFile.getInputStream(entry).use {
val bufStream = zipOutputStream.buffered()
it.copyTo(bufStream)
bufStream.flush()
}
或类似的方法,以确保所有数据都被写出。
我以为APK无法通过证书验证,但事实并非如此。前述内容将为您提供标签和图标。
使用原始代码,我看到了以下错误
块大小大于给定数据 无法在APK'/ data / user / 0 / com中加载'resources.arsc'
这向我表明输出出现了问题。通过上述更改,我看到了以下内容:
originalPackageInfo:标签:APK复制并读取appIcon:AdaptiveIconDrawable packageArchiveInfo!= null?true标签:APK复制并读取appIcon:AdaptiveIconDrawable
[如果我从Android Studio中的设备资源管理器中打开创建的APK,它将很好地解析。我确实注意到每个文件的“原始文件大小”反映了“ test.apk”的压缩大小,而“ base.apk”则显示了真正的未压缩大小。