Fernando Gallego
Fernando Gallego

Reputation: 4116

Android maps exception java.lang.NoClassDefFoundError: android.security.MessageDigest

I have an app which uses Google Maps (v1) and from the crash reports, I am seeing this exception from time to time:

java.lang.NoClassDefFoundError: android.security.MessageDigest
at com.google.android.maps.KeyHelper.getSignatureFingerprint(KeyHelper.java:60)
at com.google.android.maps.MapActivity.createMap(MapActivity.java:513)
at com.google.android.maps.MapActivity.onCreate(MapActivity.java:409)

I have defined

<uses-library
            android:name="com.google.android.maps"
            android:required="true" />

inside the application tag and I am extending MapActivity as well. The application works fine on most devices but there are some uncommon ones that report this exception, usually on Android 4.0.4 like Woxter Tablet PC 90BL, TAB9008GBBK and other generic names.

From what I read in Stackoverflow, it is a problem in the ROM and it can be solved by the user doing some advanced tricks but what I want is to prevent this crash, as I don't think it can be solved, I just want to inform the user (and thell him to buy a better device :) and disable maps functionality instead of crashing. But I can't find a way to handle this error or test it with the devices I have.

Also my main activity is based on MapActivity so I don't know how can I handle this exception before opening it.

Upvotes: 4

Views: 3509

Answers (3)

Emil Adz
Emil Adz

Reputation: 41129

Try to change the import android.security.MessageDigest to java.security.MessageDigest

by the look at this link:

What is 'android.security.MessageDigest''?

It looks that the android.security.MessageDigest was remove from Honeycomb so change it to the java one. and check this link as well:

http://productforums.google.com/forum/#!category-topic/maps/google-maps-for-mobile/KinrGn9DcIE

As been suggested there by @XGouchet:

Try downloading the latest version of the Google Maps API and rebuild your application with targetSDK set to the highest available (as of today it should be 17 / Jelly Bean).

Upvotes: 2

Deepak Bala
Deepak Bala

Reputation: 11185

Disclaimer: I've not come across this error on any of my apps / devices but I solved a similar problem. May be that same technique can help you.

Given that the class is either unavailable or an exception occurrs while loading the class, why not try to force load it when your application starts ? Class.forName("android.security.MessageDigest") should load the class and you can catch the Error thrown from that call. I know its dirty, but it should work. You can declare a custom Application class on the manifest to make this check.

Class loading test

    try
    {
        Class.forName("android.security.MessageDigest");
    }
    catch (Throwable e1)
    {
        e1.printStackTrace();
        //Bad device
    }

You can also perform a litmus test and check the functionality of the class should the class loading succeed by digesting a simple String.

Functional test

    try
    {
        MessageDigest digester = MessageDigest.getInstance("MD5");
        digester.update("test".getBytes("UTF-8"));
        byte[] digest = digester.digest();
    }
    catch (Throwable e1)
    {
        e1.printStackTrace();
        // Class available but not functional
    }

If the class loading / litmus test fails, update a shared preference flag and let the user know that his device sucks :)

Upvotes: 4

Trinimon
Trinimon

Reputation: 13967

The class android.security.MessageDigest is an abstract class (see MessageDigest API) what means that it can't be instantiated right away. So what happens is, that any time a device/app can't find an implementation of this class you will get the exception above, namely

java.lang.NoClassDefFoundError: android.security.MessageDigest

It's a good question why this happens. May be some phone vendors didn't ship their phone with the required library that actually implements this abstract class. I faced a similar issue with the TUN.ko module in the past.

Approach 1

What should help is, if you provide your own (empty) implementation of this class that "implements" the abstract classes and methods like this:

public class MessageDigestSpi extends Object {
     byte[] engineDigest() { return new byte[0]; }              

     void engineReset() { }

     void engineUpdate(byte[] input, int offset, int len) { }

}

public class MessageDigest extends MessageDigestSpi {

}

... and put those classes into the folder <src>/java/security/. So this way you provide your own implementation that is always found and might contain some code in order to inform the user or provide an alternative implementation.

So the remaining questions are: what does the app do, if the implementation is provided by the system, too and how to control that the system implementation is the first choice?

The answer: which implementation is chosen depends on the import order. Looking at Eclipse you can define the order in the project properties, Java build path, tab order and export. Be sure that you have any system libraries on top that might include the system implementation (most likely the Android libraries). This way the system searches in those libraries first. If nothing is found your classes get loaded and executed.

Approach 2

As an alternative to the implementation in an own abstract class you could of course simply instantiate the MessageDigest class, catch the NoClassDefFoundError exception and store the result for later evaluation:

import android.security.MessageDigest;

public class MessageDigestTester {

    private static Boolean messageDigestAvailable = null;

    public static Boolean isLibraryAvailable() {
        if (messageDigestAvailable == null) {
            try {
                MessageDigest digest = MessageDigest.getInstance("MD5");

                messageDigestAvailable = true;

            } catch (NoClassDefFoundError e) {
                messageDigestAvailable = false;
            }

        } 

        return messageDigestAvailable;
    }
}

Then use if (MessageDigestTester.isLibraryAvailable()) { } else { } in your code in order to encapsulate the usage of this library and to provide an alternative.

Approach two is easier to implement whereas approach one is the more sophisticated solution.

Hope this was helpful ... Cheers!

Upvotes: 1

Related Questions