【问题标题】:Getting installed app size获取已安装的应用程序大小
【发布时间】:2009-11-27 00:37:58
【问题描述】:

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

【问题讨论】:

  • 我使用它作为 getPackageSizeInfo() 的后备,这也适用于非 root 手机。
  • @CommonsWare 实际上它确实有效(至少在我尝试过的所有案例中)。它获取 APK 文件的大小,您甚至可以复制此文件。您可以在我制作的应用程序上查看这一点(此处:play.google.com/store/apps/details?id=com.lb.app_manager),并避免授予它 root 权限。在应用程序上,尝试共享 APK 文件。我认为共享 APK 不适用于受保护/购买的应用程序,但获取 APK 文件大小实际上对我尝试过的所有情况都有效。很奇怪。

标签: android


【解决方案1】:

不幸的是,目前有no 官方way 可以做到这一点。但是,如果您将PackageStatsIPackageStatsObserver AIDL 导入我们的项目并生成存根,您可以调用PackageManager 的隐藏getPackageSize 方法。然后可以使用反射来调用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);
        }
    });

这显然是一个 big hackshould not be used 用于公共应用程序。

【讨论】:

  • 我发现有些设备没有 getPackageSizeInfo() 然后你得到这个 java.lang.NoSuchMethodException: getPackageSizeInfo()
  • 只有在我删除 @Override 属性时才为我工作。
  • 谢谢。我正在做另一项任务,但需要使用这两个辅助工具。但是对于 PackageStats,我正在复制 .java 文件而不是aidls,并且正在与无法解决的导入作斗争:)。感谢you import the PackageStats and IPackageStatsObserver AIDLs..的行。
  • 在 Android N 中不再工作:code.google.com/p/android/issues/…
  • 自 8.0 getPackageSizeInfo 已弃用。所以使用 StorageStatsmanagerrefer this link
【解决方案2】:

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

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

【讨论】:

  • 这是 APK 文件的大小。每个应用程序正在使用的其他文件呢:缓存、数据库、下载的内容……?
  • 什么是appInfo对象?
  • 上下文。它也应该是 getPackageName 而不是 getPackage。
  • 文件大小始终为 long 而不是 int... 更正? file.length() 将返回一个 long 值
【解决方案3】:

这是@Josef Pfleger 的附加答案,供评论

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

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

 PackageManager.getPackageSizeInfo(String packageName, IPackageStatsObserver observer);

改成:

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

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

所以我们应该这样做:

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

需要回调的类如:

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

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

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

【讨论】:

  • 你好 droida,我似乎无法让它工作,我一直让 Android Studio 说它无法解析符号 IPackageStatsObserver,尽管我认为我完成了两者指示的所有步骤@Josef Pfleger 和你 @droida 有什么建议吗?
  • 嗨@CMPSoares,您是否尝试过导入AIDL (www-jo.se/f.pfleger/using-aidl)?
  • 是的@droida 进入包/文件夹 android.content.pm 是正确的吗?
  • 嗨伙计,也许你可以挖掘出Android O中是否有任何API发生变化,然后将其发布并发布以节省剩余时间:) @androiddeveloper
  • @droida 我认为他们没有写过它。我试图找到它,但失败了。我认为原因是它已经是隐藏的 API,所以不应该使用它。但是,我尝试调试,它确实找到了具有 2 个参数(不是 3 个参数)的方法,但是当我调用它时,我得到了 succeeded==false 和 pStats==null。也许他们有错误或其他东西,正如我在这里报告的那样:issuetracker.google.com/issues/37237952。无论如何,我在这里请求了一个官方 API:issuetracker.google.com/issues/37238101
【解决方案4】:

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

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

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

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

【讨论】:

  • 这正是我所需要的,因为没有它就会出现java.lang.reflect.InvocationTargetException 错误,非常感谢!
【解决方案5】:

您可以在没有 AIDL 文件的情况下获取应用程序大小 -------> Kotlin 语言

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

    val intent = Intent(Intent.ACTION_MAIN)
    intent.addCategory(Intent.CATEGORY_LAUNCHER)  
        
    val list = packageManager.queryIntentActivities(intent,0)
            
    // Set adapter to LIST VIEW
    listView.adapter = getApps(list)
 }
       
 private fun getApps(List: MutableList<ResolveInfo>): List<AppData> {
            
     val list = ArrayList<AppData>()
      for (packageInfo in List) {
                      
         val packageName = packageInfo.activityInfo.packageName
           // return size in form of Bytes(Long)             
    val size = File(packageManager.getApplicationInfo(packageName,0).publicSourceDir).length()
          
          val item = AppData(Size)
            list += item
      }
   return list
   }
}
 // Make Data Class
data class AppData(val size: Long)

记得把它从 Bytes 转换成 MB

【讨论】:

    【解决方案6】:
    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);
        }
    }
    

    【讨论】:

    • 信号量用于获取并发。android.content.pm.IPackageStatsObserver 是辅助工具。
    • edit 解释为什么/如何此代码回答问题?不鼓励仅使用代码的答案,因为它们不像带有解释的代码那样容易学习。如果没有解释,则需要花费更多的时间和精力来理解正在执行的操作、对代码所做的更改、代码是否回答了问题等。解释对于试图从答案中学习的人和评估结果的人来说都很重要回答看看它是否有效,或者是否值得投票。
    • 这是一个非常大量的代码,远远超过了回答问题所需要的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-30
    • 1970-01-01
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多