yanchenko
yanchenko

Reputation: 57156

Getting installed app size

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.

Upvotes: 22

Views: 32824

Answers (6)

krawiec
krawiec

Reputation: 407

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

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

Upvotes: 24

Gurarshdeep Singh
Gurarshdeep Singh

Reputation: 92

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

Upvotes: 0

Tomas
Tomas

Reputation: 41

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

Upvotes: 4

droida
droida

Reputation: 141

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

Upvotes: 5

shefi
shefi

Reputation: 1

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

Upvotes: -2

Josef Pfleger
Josef Pfleger

Reputation: 74507

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.

Upvotes: 30

Related Questions