Getting installed app size

The question:

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 @hide and relies on some obscure IPackageStatsObserver for result so I can’t call it via reflection.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

Unfortunately there is currently no official way to do that. However, you can call the PackageManager‘s hidden getPackageSize method if you import the PackageStats and IPackageStatsObserver AIDLs into our project and generate the stubs. You can then use reflection to invoke 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);
        }
    });

That’s obviously a big hack and should not be used for public applications.

Method 2

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

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

Method 3

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

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

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

 PackageManager.getPackageSizeInfo(String packageName, IPackageStatsObserver observer);

changed into:

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

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

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 MB instead of long like 2342334 🙂

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

Method 4

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().

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

Method 5

You can get Size of apps without AIDL Files ——-> Kotlin Language

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)

Remember to convert it in MB from Bytes

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


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Comment