6

I have made a platform independent library that I want to use on a J2SE and Android project. Within this library I have a Version class that loads its version details from the manifest. On PC this works well, on Android I'm getting a NullPointerException and I can't figure out why.

This is my class:

public class Version {

    private static int APPCODE = 12354;
    private static int MAJOR;
    private static int MINOR;
    private static char RELEASE;
    private static int BUILD;
    private static int PROTOCOL;

    static {
        try {
            Class clazz = Version.class;
            String className = clazz.getSimpleName() + ".class";
            String classPath = clazz.getResource(className).toString(); //NullPointerException
            if (classPath.startsWith("jar")) {
                String manifestPath = classPath.substring(0,
                        classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF";
                Manifest manifest = new Manifest(new URL(manifestPath).openStream());
                Attributes attr = manifest.getMainAttributes();
                //APPCODE = Integer.parseInt(attr.getValue("APPCODE"));
                MAJOR = Integer.parseInt(attr.getValue("MAJOR"));
                MINOR = Integer.parseInt(attr.getValue("MINOR"));
                RELEASE = attr.getValue("RELEASE").charAt(0);
                BUILD = Integer.parseInt(attr.getValue("BUILD"));
                PROTOCOL = Integer.parseInt(attr.getValue("PROTOCOL"));
            } else {
                System.err.println("Couldn't find manifest, reverting to test mode");
                MAJOR = 0;
                MINOR = 0;
                RELEASE = 'n';
                BUILD = 0;
                //APPCODE = 12354;
            }
        } catch (IOException e) {
            System.err.println("Failed to load manifest file. " + e);
            MAJOR = 0;
            MINOR = 0;
            RELEASE = '0';
            BUILD = 0;
            //APPCODE = 12354;
            PROTOCOL = 0;
        } catch (NumberFormatException e) {
            System.err.println("Failed to load manifest file. " + e);
            MAJOR = 0;
            MINOR = 0;
            RELEASE = '0';
            BUILD = 0;
            //APPCODE = 12354;
            PROTOCOL = 0;
        }
    }

    public static int getProtocol() {
        return PROTOCOL;
    }

    public static int getAppCode() {
        return APPCODE;
    }

    public static int getBuildNumber() {
        return BUILD;
    }

    public static int getMajor() {
        return MAJOR;
    }

    public static int getMinor() {
        return MINOR;
    }

    public static char getRelease() {
        return RELEASE;
    }
}

(Please excuse the System.err.println() lines, those are for debugging on PC).

Why would this work fine on PC but not on Android?

Full stack trace:

03-12 22:13:11.687: E/AndroidRuntime(11780): FATAL EXCEPTION: main
03-12 22:13:11.687: E/AndroidRuntime(11780): java.lang.ExceptionInInitializerError
03-12 22:13:11.687: E/AndroidRuntime(11780):    at com.logandam.wififileshare.android.MainActivity.onCreate(MainActivity.java:24)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.app.Activity.performCreate(Activity.java:4465)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1052)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1932)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1993)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.app.ActivityThread.access$600(ActivityThread.java:127)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1159)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.os.Handler.dispatchMessage(Handler.java:99)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.os.Looper.loop(Looper.java:137)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at android.app.ActivityThread.main(ActivityThread.java:4507)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at java.lang.reflect.Method.invokeNative(Native Method)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at java.lang.reflect.Method.invoke(Method.java:511)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
03-12 22:13:11.687: E/AndroidRuntime(11780):    at dalvik.system.NativeStart.main(Native Method)
03-12 22:13:11.687: E/AndroidRuntime(11780): Caused by: java.lang.NullPointerException
03-12 22:13:11.687: E/AndroidRuntime(11780):    at com.logandam.wififileshare.net.Version.<clinit>(Version.java:30)
03-12 22:13:11.687: E/AndroidRuntime(11780):    ... 15 more

Update: className = Version.class on both PC and Android. Using getName() instead of getSimpleName() breaks on PC and still doesn't work on Android.

Syl Sta
  • 42
  • 4
ldam
  • 4,412
  • 6
  • 45
  • 76

3 Answers3

2

You are getting NullPointerException because the method call clazz.getResource(className) is returning null which means the resource specified by className cannot be found.
Put your resource file in a folder and set it as resource folder. Check this answer on SO for more information.
You can also check this SO answer for detials about recource folders.

Community
  • 1
  • 1
Mohammad Banisaeid
  • 2,376
  • 27
  • 35
  • Except my resource is a class in a jar file. :/ – ldam Mar 12 '13 at 20:42
  • There will be no problem as long as the jar file is in your classpath. Are you sure you're exporting the jar file along with your apk file? – Mohammad Banisaeid Mar 12 '13 at 20:45
  • 1
    I'm assuming so, otherwise the actual meat of the library would not be working (which it currently is). – ldam Mar 12 '13 at 20:49
  • Make sure your jar file is exported along with your apk file. Check this out: http://stackoverflow.com/questions/2316445/how-to-use-and-package-a-jar-file-with-my-android-app – Mohammad Banisaeid Mar 12 '13 at 20:50
  • This has nothing to do with properly exporting an APK (which seems to have happened) but with the APK file-format and the differences between a JVM and dalvik. – Stephan Mar 12 '13 at 20:55
  • I guess this is the same problem as yours http://stackoverflow.com/questions/4998648/use-resources-from-prebuilt-jar-in-android – Mohammad Banisaeid Mar 12 '13 at 21:06
1

I am not 100% sure but I don't think that this mechanism works under Android as it does in a real JVM (since dalvik is essentially not a JVM). I think (and I might be wrong here), that in the packaged APK, the JAR does no longer exist and therefore your path to the resource probably is wrong. The classes of your JAR are converted to the dex file format and added to the classes.dex file. Therefore you cannot resolve a resource like foo.bar.class because it does not exist in the classpath.

Try to put the Version information into a text (or other resource) file and add it into the JAR and read that. The resource file should be added properly and you should be able to read it with the mechanism.

Stephan
  • 7,360
  • 37
  • 46
  • I had forgotten about the APK part completely. I just figured that since the library is in a jar file it would get packaged along inside the APK itself and everything would still work. – ldam Mar 12 '13 at 20:48
  • I added some more explanation, and an idea how to overcome it in a platform independent way. – Stephan Mar 12 '13 at 20:54
  • I ended up putting a `version` file in `/res` and then using the `Property` class to neatly read it, which works perfectly on both Android and standard Java. – ldam Mar 14 '13 at 18:27
0

I think you want to keep version information in manifest file and extract it in runtime. If so, welcome to Android :-)

You have several better options rather than the one with issues you're facing.

I think those are better.

Syl Sta
  • 42
  • 4
  • That's Android specific. I need platform independence. But in essence this is what I want to do. – ldam Mar 13 '13 at 06:06