Android 获取已安装的应用程序大小

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1806286/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:38:51  来源:igfitidea点击:

Getting installed app size

android

提问by yanchenko

I'm trying to figure out how to get the size of an installed app.
What's already failed:
- new File('/data/app/some.apk')- reports incorrect size
- PackageManager.getPackageSizeInfo(String packageName, IPackageStatsObserver observer)- is @hideand relies on some obscure IPackageStatsObserverfor result so I can't call it via reflection.

我想弄清楚如何获取已安装应用程序的大小。
什么是已经失败:
- new File('/data/app/some.apk')-报告不正确的大小
- PackageManager.getPackageSizeInfo(String packageName, IPackageStatsObserver observer)-是@hide和依赖于一些模糊IPackageStatsObserver的结果,所以我不能通过反射调用它。

回答by Josef Pfleger

Unfortunately there is currently noofficial wayto do that. However, you cancall the PackageManager's hidden getPackageSizemethod if you import the PackageStatsand IPackageStatsObserverAIDLs into our project and generate the stubs. You can then use reflection to invoke getPackageSize:

不幸的是,目前没有官方方法可以做到这一点。但是,如果将和AIDL 导入我们的项目并生成存根,则可以调用PackageManager的隐藏getPackageSize方法。然后您可以使用反射来调用:PackageStatsIPackageStatsObservergetPackageSize

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);
        }
    });

That's obviously a big hackand should not be usedfor public applications.

这显然是一个大黑客不应该用于公共应用程序。

回答by krawiec

You can do it simplier by gettting path to apk file, and checking its lenght:

您可以通过获取 apk 文件的路径并检查其长度来更简单地完成此操作:

final PackageManager pm = context.getPackageManager();
ApplicationInfo applicationInfo = pm.getApplicationInfo(appInfo.getPackage(), 0);
File file = new File(applicationInfo.publicSourceDir);
int size = file.length();

回答by droida

Here is additional answer @Josef Pfleger 's, for comment

这是@Josef Pfleger 的其他答案,供评论

“I found that some device doesn't have getPackageSizeInfo() then you get this java.lang.NoSuchMethodException: getPackageSizeInfo()” @ ATom Nov 29 '11 at 15:56.

“我发现某些设备没有 getPackageSizeInfo() 然后你得到这个 java.lang.NoSuchMethodException: getPackageSizeInfo()”@ ATom 2011 年 11 月 29 日 15:56。

After api 16( Build.VERSION.SDK_INT >16),the method

api 16(Build.VERSION.SDK_INT >16)后,方法

 PackageManager.getPackageSizeInfo(String packageName, IPackageStatsObserver observer);

changed into:

改变成:

PackageManager.getPackageSizeInfo(String packageName, int userHandle, IPackageStatsObserver observer);

And the explain for the new added param userHandleis :The user whose size information should be retrieved.

新添加的参数的解释userHandle是:应该检索其尺寸信息的用户。

So we should do it like this:

所以我们应该这样做:

 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;} 

The class needed to callback like:

需要回调的类,如:

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);
    }
}

And use this method to parse long2string,then you can see xx MBinstead of longlike 2342334 :)

并使用此方法解析long2string,然后您可以看到xx MB而不是long像2342334 :)

private String formateFileSize(long size) {
    return Formatter.formatFileSize(MainActivity.this, size);
}

回答by Tomas

Remember the needed permission, I solved these issues by adding the following permission to the manifest:

记住所需的权限,我通过向清单添加以下权限解决了这些问题:

< uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />

Or this wrong: not use getDeclaredMethod(),should be use getMethod().

或者这个错误:不使用getDeclaredMethod(),应该使用getMethod()

Method getPackageSizeInfo = mPackageManager.getClass().getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);

Method getPackageSizeInfo = mPackageManager.getClass().getMethod("getPackageSizeInfo", String.class, IPackageStatsObserver.class);

回答by shefi

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);
    }
}