问题描述
我正在尝试从设备上存储的apk中获取minSdkVersion
和targetSdkVersion
的值.在此处中讨论了其他详细信息,但仅 targetSdkVersion 在ApplicationInfo
类中可用.能否通过提取apk文件并读取AndroidManifest.xml来获得minSdkVersion
?
I am trying to get the values of minSdkVersion
and targetSdkVersion
from an apk stored on the device. Getting other details are discussed here, but only the targetSdkVersion is available in the ApplicationInfo
class. Can the minSdkVersion
be obtained other than by extracting the apk file and reading AndroidManifest.xml?
推荐答案
我不认为这是可以自己完成的,并且没有预制的api.当前读取和解析AndroidManifest的方法根本不考虑minSdkVersion.
I do not believe this is possible to do on your own and there is no pre-made api for this. The current methods that read and parse the AndroidManifest do not consider minSdkVersion at all.
要在不使用现成功能的情况下检查apk文件,最终需要将其手动添加到资产管理器中.并且该方法标记有不供应用程序使用",根据我的经验,这通常意味着从应用程序中调用它不是一个好主意.
In order to check your apk file without using the ready made functions you end up needing to add it manually to the asset manager. And that method is marked with "Not for use by applications" which in my experience usually means that it's not a good idea to call it from an application.
如果您确实要拨打电话:
If you do manage to call:
public final int addAssetPath(String path) {
从您的应用程序中,您应该能够通过解析XML文件来获取minSdkVersion,请考虑以下代码:
From your application you should be able to get the minSdkVersion by parsing the XML file, consider this code:
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
....
method:
final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);
Resources res = null;
XmlResourceParser parser = null;
try {
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
final String[] outError = new String[1];
final Package pkg = parseBaseApk(res, parser, flags, outError);
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
}
您应该可以在其中使用XmlResourceParser解析AndroidManifest文件并找到minSdkVersion的元素.
Where you should be able to parse your AndroidManifest file using the XmlResourceParser and find the element for the minSdkVersion.
如果您想自己尝试一下,只需复制以下静态方法并调用getMinSdkVersion(yourApkFile)
:
If you want to try it out yourself, just copy following static methods and call getMinSdkVersion(yourApkFile)
:
/**
* Parses AndroidManifest of the given apkFile and returns the value of
* minSdkVersion using undocumented API which is marked as
* "not to be used by applications"
*
* @param apkFile
* @return minSdkVersion or -1 if not found in Manifest
* @throws IOException
* @throws XmlPullParserException
*/
public static int getMinSdkVersion(File apkFile) throws IOException,
XmlPullParserException {
XmlResourceParser parser = getParserForManifest(apkFile);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.getEventType() == XmlPullParser.START_TAG
&& parser.getName().equals("uses-sdk")) {
for (int i = 0; i < parser.getAttributeCount(); i++) {
if (parser.getAttributeName(i).equals("minSdkVersion")) {
return parser.getAttributeIntValue(i, -1);
}
}
}
}
return -1;
}
/**
* Tries to get the parser for the given apkFile from {@link AssetManager}
* using undocumented API which is marked as
* "not to be used by applications"
*
* @param apkFile
* @return
* @throws IOException
*/
private static XmlResourceParser getParserForManifest(final File apkFile)
throws IOException {
final Object assetManagerInstance = getAssetManager();
final int cookie = addAssets(apkFile, assetManagerInstance);
return ((AssetManager) assetManagerInstance).openXmlResourceParser(
cookie, "AndroidManifest.xml");
}
/**
* Get the cookie of an asset using an undocumented API call that is marked
* as "no to be used by applications" in its source code
*
* @see <a
* href="http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/content/res/AssetManager.java#612">AssetManager.java#612</a>
* @return the cookie
*/
private static int addAssets(final File apkFile,
final Object assetManagerInstance) {
try {
Method addAssetPath = assetManagerInstance.getClass().getMethod(
"addAssetPath", new Class[] { String.class });
return (Integer) addAssetPath.invoke(assetManagerInstance,
apkFile.getAbsolutePath());
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return -1;
}
/**
* Get {@link AssetManager} using reflection
*
* @return
*/
private static Object getAssetManager() {
Class assetManagerClass = null;
try {
assetManagerClass = Class
.forName("android.content.res.AssetManager");
Object assetManagerInstance = assetManagerClass.newInstance();
return assetManagerInstance;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
您可能也需要进行反射调用来设置此设置:
You may need a reflection call to set this as well:
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
由于要创建新的AssetManager而不依赖于您的应用程序,因此不能保证它会正常工作(也不会对您的手机造成不良影响)的操作应该是安全的.从C ++代码的快速浏览看来,它似乎没有被添加到任何全局列表中.
No guarantees that it will work (nor that it won't be bad for your phone) the operation should be safe since you're creating a new AssetManager and not relying on the AssetManager for your application. From a quick look in the C++ code it seems that it's not being added to any global list.
代码: http://androidxref.com/5.1.1_r6/xref/frameworks/base/libs/androidfw/AssetManager.cpp#173
这篇关于从APK文件中获取minSdkVersion和targetSdkVersion的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!