Obtener el tamaño de la aplicación instalada
-
05-07-2019 - |
Pregunta
Estoy tratando de averiguar cómo obtener el tamaño de una aplicación instalada.
Lo que ya falló:
- nuevo archivo ('/ data / app / some.apk')
- informa de tamaño incorrecto
- PackageManager.getPackageSizeInfo (String packageName, IPackageStatsObserver observer)
- es @hide
y confía en algún oscuro IPackageStatsObserver
para el resultado, así que no puedo llamar a través de la reflexión.
Solución
Lamentablemente, actualmente hay no official way para hacer eso. Sin embargo, puede llamar al PackageManager
está oculto getPackageSize
si importa el método PackageStats
y IPackageStatsObserver
AIDL en nuestro proyecto y genera los apéndices. Luego puede usar la reflexión para invocar getPackageSize
:
PackageManager pm = getPackageManager();
Method getPackageSizeInfo = pm.getClass().getMethod(
"getPackageSizeInfo", String.class, IPackageStatsObserver.class);
getPackageSizeInfo.invoke(pm, "com.android.mms",
new IPackageStatsObserver.Stub() {
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
throws RemoteException {
Log.i(TAG, "codeSize: " + pStats.codeSize);
}
});
Eso es obviamente un big hack y no debe utilizarse para aplicaciones públicas.
Otros consejos
Puede hacerlo más simple obteniendo una ruta al archivo apk y comprobando su longitud:
final PackageManager pm = context.getPackageManager();
ApplicationInfo applicationInfo = pm.getApplicationInfo(appInfo.getPackage(), 0);
File file = new File(applicationInfo.publicSourceDir);
int size = file.length();
Aquí está la respuesta adicional de @Josef Pfleger 's, para comentarios
& # 8220; Encontré que algún dispositivo no tiene getPackageSizeInfo (), entonces obtienes esta java.lang.NoSuchMethodException: getPackageSizeInfo () & # 8221; @ ATom 29 de noviembre de 2011 a las 15:56.
Después del api 16 (Build.VERSION.SDK_INT > 16), el método
PackageManager.getPackageSizeInfo(String packageName, IPackageStatsObserver observer);
cambiado a:
PackageManager.getPackageSizeInfo(String packageName, int userHandle, IPackageStatsObserver observer);
Y la explicación para el nuevo parámetro agregado userHandle
es: El usuario cuya información de tamaño se debe recuperar.
Así que deberíamos hacerlo así:
int sysVersion= Build.VERSION.SDK_INT;
if (pkgName != null) {// packageName
PackageManager pm = getPackageManager();
try {
Class<?> clz = pm.getClass();
if (sysVersion>16) {
Method myUserId=UserHandle.class.getDeclaredMethod("myUserId");//ignore check this when u set ur min SDK < 17
int userID = (Integer) myUserId.invoke(pm);
Method getPackageSizeInfo = clz.getDeclaredMethod(
"getPackageSizeInfo", String.class,int.class,
IPackageStatsObserver.class);//remember add int.class into the params
getPackageSizeInfo.invoke(pm,pkgName, userID, new PkgSizeObserver());
} else {//for old API
Method getPackageSizeInfo = clz.getDeclaredMethod(
"getPackageSizeInfo", String.class,
IPackageStatsObserver.class);
getPackageSizeInfo.invoke(pm, pkgName, new PkgSizeObserver());
}
} catch (Exception ex) {
Log.e(TAG, "NoSuchMethodException");
ex.printStackTrace();
throw ex;}
La clase necesaria para devolver la llamada como:
private class PkgSizeObserver extends IPackageStatsObserver.Stub {
/***
* @param pStatus
* @param succeeded
*/
@Override
public void onGetStatsCompleted(PackageStats pStats, boolean succeeded)
throws RemoteException {
cachesize = pStats.cacheSize;//remember to declare these fields
datasize = pStats.dataSize;
codesize = pStats.codeSize;
totalsize = cachesize + datasize + codesize;
Log.i("123","cachesize--->" + cachesize + " datasize---->"
+ datasize + " codeSize---->" + codesize);
}
}
Y usa este método para analizar long2string, entonces puedes ver xx MB
en lugar de long
como 2342334 :)
private String formateFileSize(long size) {
return Formatter.formatFileSize(MainActivity.this, size);
}
Recuerde el permiso necesario. Resolví estos problemas agregando el siguiente permiso al manifiesto:
< uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
O esto está mal: no use getDeclaredMethod ()
, debe usar getMethod ()
.
Método getPackageSizeInfo = mPackageManager.getClass (). getMethod (" getPackageSizeInfo " ;, String.class, IPackageStatsObserver.class);
package inc.xiomi.apkextrator.entity;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
import android.content.pm.ResolveInfo;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.util.DisplayMetrics;
import android.util.Log;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.concurrent.Semaphore;
public class AppInfo implements Comparable<Object> {
private Context ctx;
private ResolveInfo ri;
private ComponentName componentName = null;
private PackageInfo pi = null;
private Drawable icon = null;
String size = null;
String name = null;
// Code size will be here
long codeSize = 0;
PackageManager packageManager;
// Semaphore to handle concurrency
Semaphore codeSizeSemaphore = new Semaphore(1, true);
public AppInfo(Context ctx, ResolveInfo ri) {
this.ctx = ctx;
this.ri = ri;
packageManager = ctx.getPackageManager();
this.componentName = new ComponentName(ri.activityInfo.applicationInfo.packageName, ri.activityInfo.name);
try {
pi = ctx.getPackageManager().getPackageInfo(getPackageName(), 0);
} catch (NameNotFoundException e) {
}
}
public String getName() {
if (name != null) {
return name;
} else {
try {
return getNameFromResolveInfo(ri);
} catch (NameNotFoundException e) {
return getPackageName();
}
}
}
public String getSize() {
if (size != null) {
return size;
} else {
try {
return getSizeFromResolveInfo(ri);
} catch (Exception e) {
return getPackageName();
}
}
}
public String getActivityName() {
return ri.activityInfo.name;
}
public String getPackageName() {
return ri.activityInfo.packageName;
}
public ComponentName getComponentName() {
return componentName;
}
public String getComponentInfo() {
if (getComponentName() != null) {
return getComponentName().toString();
} else {
return "";
}
}
public ResolveInfo getResolveInfo() {
return ri;
}
public PackageInfo getPackageInfo() {
return pi;
}
public String getVersionName() {
PackageInfo pi = getPackageInfo();
if (pi != null) {
return pi.versionName;
} else {
return "";
}
}
public int getVersionCode() {
PackageInfo pi = getPackageInfo();
if (pi != null) {
return pi.versionCode;
} else {
return 0;
}
}
public Drawable getIcon() {
if (icon == null) {
icon = getResolveInfo().loadIcon(ctx.getPackageManager());
/*
Drawable dr = getResolveInfo().loadIcon(ctx.getPackageManager());
Bitmap bitmap = ((BitmapDrawable) dr).getBitmap();
icon = new BitmapDrawable(ctx.getResources(), AppHelper.getResizedBitmap(bitmap, 144, 144));
*/
}
return icon;
}
@SuppressLint("NewApi")
public long getFirstInstallTime() {
PackageInfo pi = getPackageInfo();
if (pi != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
return pi.firstInstallTime;
} else {
return 0;
}
}
@SuppressLint("NewApi")
public long getLastUpdateTime() {
PackageInfo pi = getPackageInfo();
if (pi != null && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
return pi.lastUpdateTime;
} else {
return 0;
}
}
@Override
public int compareTo(Object o) {
AppInfo f = (AppInfo) o;
return getName().compareTo(f.getName());
}
@Override
public String toString() {
return getName();
}
/**
* Helper method to get an applications name!
*
* @param ri
* @return
* @throws android.content.pm.PackageManager.NameNotFoundException
*/
public String getNameFromResolveInfo(ResolveInfo ri) throws NameNotFoundException {
String name = ri.resolvePackageName;
if (ri.activityInfo != null) {
Resources res = ctx.getPackageManager().getResourcesForApplication(ri.activityInfo.applicationInfo);
Resources engRes = getEnglishRessources(res);
if (ri.activityInfo.labelRes != 0) {
name = engRes.getString(ri.activityInfo.labelRes);
if (name == null || name.equals("")) {
name = res.getString(ri.activityInfo.labelRes);
}
} else {
name = ri.activityInfo.applicationInfo.loadLabel(ctx.getPackageManager()).toString();
}
}
return name;
}
public String getSizeFromResolveInfo(ResolveInfo ri) throws Exception {
try {
codeSizeSemaphore.acquire();
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
// Collect some other statistics
// Collect code size
try {
Method getPackageSizeInfo = packageManager.getClass().getMethod("getPackageSizeInfo",
String.class,
android.content.pm.IPackageStatsObserver.class);
getPackageSizeInfo.invoke(packageManager, ri.activityInfo.packageName,
new android.content.pm.IPackageStatsObserver.Stub() {
// Examples in the Internet usually have this method as @Override.
// I got an error with @Override. Perfectly works without it.
public void onGetStatsCompleted(PackageStats pStats, boolean succeedded)
throws RemoteException {
codeSize = pStats.codeSize;
Log.e("codeSize", codeSize + "");
codeSizeSemaphore.release();
}
});
} catch (Exception e) {
e.printStackTrace(System.err);
}
return String.valueOf(codeSize);
}
public Resources getEnglishRessources(Resources standardResources) {
AssetManager assets = standardResources.getAssets();
DisplayMetrics metrics = standardResources.getDisplayMetrics();
Configuration config = new Configuration(standardResources.getConfiguration());
config.locale = Locale.US;
return new Resources(assets, metrics, config);
}
}