5

I know that I can either catch the NameNotFoundException from a call to PackageManager.getPackageInfo or loop through the PackageInfo list returned by PackageManager.getInstalledPackages to know whether a particular package is installed, but both of these seem either long winded or ugly. On my personal phone, I have more than 300 packages installed, so I'd hate to have to do that operation every time I need to check. And catching an exception as a means of performing application logic just makes me feel wrong all over. Am I missing the isPackageInstalled method somewhere, or do I just need to implement it myself using one of the above mentioned techniques? And if the latter, which would be considered the faster and less resource intensive option?

Rich
  • 36,270
  • 31
  • 115
  • 154
  • [`getPackageInfo`](http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r2.1/android/app/ApplicationPackageManager.java#67) is even deliberately turning a null result into an exception. – zapl Nov 29 '12 at 01:45
  • @zapl I know! At least allow me to check for null before slapping me on the wrist. lol – Rich Nov 29 '12 at 01:54

4 Answers4

3

Since PackageManager.getInstalledPackages() returns a List, you don't need to loop through it manually. You can use List.contains() or List.containsAll() to accomplish the task in one line of code. Of course, this doesn't change the efficiency since both methods likely contain a loop themselves.

Code-Apprentice
  • 81,660
  • 23
  • 145
  • 268
  • I like your logic, however List.contains would take an object of type PackageInfo and do a reference comparison as opposed to just checking if the packageName property is equal which is what I really need. And I'm sure they're looping internally so in terms of performance it's probably equal to me doing it myself. In C# I could just pass a lambda expression to the method to describe my preferred comparison logic, but as far as I know Java doesn't have this kind of feature. – Rich Nov 29 '12 at 03:09
  • How about Collections.binarySearch() which allows you to provide a comparator? Of course, you would have to sort the list first... – Code-Apprentice Nov 29 '12 at 03:21
  • It looks cooler but the sorting actually made it about 6x slower than just looping, and I tested the looping without a termination condition so that I could measure worst case. Still, the expensive part is just asking the system for the packages in the first place. Looks like there's no good answer. Catching the ex seems to be the recommended way by the Android authors. Thanks for the help +1 – Rich Nov 29 '12 at 03:39
1

If using the API really bugs you then you might look into a hack involving the following

Bash shell expression that gets the PM list Java Runtime expression Java Pipes and buffers and streams Java NIO Java grep

So the bash expression would be :

pm list packages -f | sed 's/^package.//' | awk -F"=" ' { print $2" "$1 } ' | sort

and of list of references for handling stdout from the 'pm list' in a way that might wind up being faster...

PipedBuffers

NIO/grep

Runtime/streams

Community
  • 1
  • 1
Robert Rowntree
  • 6,230
  • 2
  • 24
  • 43
0

Handling a NameNotFoundExcepetion should not make you feel "wrong all over" IMHO. According to the documentation this exception will be thrown if the package does not exist since api level 1. Using try/catch statement is very similar to using an if/then statement to test for a null value.

In this case it should not be considered a workaround or a hack as you are using the documented and expected return value of an exception to determine if a package exists.

I would assume this method to be faster than iterating through the List returned by getInstalledPackages(). However, I don't know what steps android takes prior to returning a NameNotFoundExcepetion. This would make an interesting benchmark test.

I'm not aware of any other practical method to test for an installed package.

Jason Hessley
  • 1,608
  • 10
  • 8
  • 3
    Strongly disagree. Not only is the operation of throwing an exception an expensive and slow one, it's ugly and doesn't express my intention. You should read some of the discussions on the following page: http://stackoverflow.com/questions/567579/how-expensive-are-exceptions. As far as looping, even ~300 iterations isn't that expensive in terms of CPU cycles, the expensive part is the internals of the getInstalledPackages call. – Rich Nov 29 '12 at 02:11
  • @Rich That's an excellent link. I think you should include it as a reference in your question. It is exactly the kind of benchmark I was suggesting and goes to what I said I don't know about the internal process of throwing an exception. With that being said, it does appear as this is how the android team intended it to be used. As such, it should not be considered dirty. – Jason Hessley Nov 29 '12 at 02:23
0

I wrote some benchmarks and tested catching the exception vs a few different ways of fetching the installed packages and looping through them. Here is my result

  1. Calling PackageManager.getPackageInfo and catching the NameNotFoundException took between 1 and 4 ms in all cases whether the requested package was installed or not, and I made sure to also include cases where this was the first call to the PackageManager for a particular run of the app and as a subsequent call just in case the framework does any caching of this information per app launch.

  2. Calling PackageManger.getPackageInfo took between 1 and 1.5 seconds in all cases as well.

Calling getPackageInfo and catching the exception to determine if the package isn't installed is by far the faster way to check.

Rich
  • 36,270
  • 31
  • 115
  • 154