Monday, October 22, 2012

Get the APK's Code Size in Android

In the early version of my DELTA Statistics application for Android I collected code size of an installed application. When a lot of statistics about an installed application is easy to get, code size slightly more complicated.

 Step by step:
  1. Download IPackageStatsObserver.aidl and PackageStats.aild. You can get them from here.
  2. Create new package in your project and name it android.content.pm.
  3. Add IPackageStatsObserver.aidl and PackageStats.aild to the created package. You can do it by right-clicking on the package name and choosing Import option. Then select File System and click Next. Browse to the folder where you downloaded .aidl files, check the files you need and click Finish.
  4. Check that Eclipse automatically generated IPackageStatsObserver.java in gen.android.content.pm. Mine has some warning but trying to fix them is just a waist of time.
  5. Now we can get code size of application. In the example below I collect code size of all the installed applications. When I first wrote it I got a weird bug. Sometimes code size was 0 for the last N applications. N was different practically all the time, so, I though that it was a concurrency issue. As I understand, getPackageSizeInfo() method is invoked in another thread and because of that for loop can end before all the values of code size have been computed. To solve this problem I included a semaphore that is acquired at each loop iteration and is released after code size has been computed. It guarantees that getPackageSizeInfo() will finish before the next loop iteration will start.
    context = getApplicationContext();
    final PackageManager packageManager = context.getPackageManager();
    List<ApplicationInfo> installedApplications = 
        packageManager.getInstalledApplications(PackageManager.GET_META_DATA);
    
    // Semaphore to handle concurrency
    final Semaphore codeSizeSemaphore = new Semaphore(1, true);
    
    // Code size will be here
    long codeSize = 0;
    
    for (ApplicationInfo appInfo : installedApplications)
    {
        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,
                                                    IPackageStatsObserver.class);
            
            getPackageSizeInfo.invoke(packageManager, appInfo.packageName, 
                                      new 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;
                    codeSizeSemaphore.release();
                }
            });
        }
        catch (Exception e)
        {
            e.printStackTrace(System.err);
        }
    }
    

7 comments:

  1. Hi, thanks for the info. I followed steps above, however got errors in my auto generated IPackageStatsObserver.java file.
    I tied to fix errors by removing @Override, but it automatically gets back to previous state with errors. What do i do?? without fixing IPackageStatsObserver.java my project cannot run.

    ReplyDelete
    Replies
    1. I will look into it today and will post a solution here in comments...
      Thank you for the feedback.

      Delete
    2. You need Java 1.6 or higher to rid yourself of those Override errors

      Delete
  2. Hi,thank you for this post. It worked perfectly for me!

    ReplyDelete
  3. Above code works great, but i face some problem in semaphore,
    I created codeSizeSemaphore.acquire(); in for loop then the execution is not reaching upto
    getPackageSizeInfo.invoke() and not releasing semaphore. So please tell me what to do ?

    ReplyDelete
    Replies
    1. I will be glad to help if you will give some details. My advice: go to stackoverflow.com, post a question with your code and send me a link.

      Delete