3

Hopefully this won't be too long-winded, but trying to be complete:

So I have an app on the Android Market. Within the app are a few Serializable classes. App is working fine everywhere (in emulator, on debugging phone, on phones that download the app).

I made a decision to add some features. Those features involve implementing Comparable on one of my Serializable classes. In Eclipse (emulator or attached phone), the changes seem valid. Also according to this Java document, I think the changes are valid changes to a Serializable class.

Now to the problem. It seems that after exporting a signed apk, I have crashes in the app. The crashes occur the first time the altered class is touched. The stack trace is obfuscated, and the details aren't too important (I think), but it is a NullPointerException crashing the app.

Here is the changed code from my experiments:

The Comparable version:

public class Card implements Serializable, Comparable<Card>{

with the added method:

public int compareTo(Card another) {
        if( another.getName() == null ) return -1;
        if( this.getName() == null ) return 1;
        return this.getName().compareTo(another.getName());
    }

The Regular/Original Version:

public class Card implements Serializable/*, Comparable<Card>*/{

without the added method.

The classes are identical otherwise. Note that getName() returns a String.

I performed the following experiment:

  1. Uninstall the app completely from my phone.
  2. Build the app without the Comparable change to the code and run on my phone through Eclipse. (runs fine).
  3. Implement the Comparable change and run on my phone through Eclipse. (runs fine).

No problems here. Now I exported a signed version of the each build described above.

  1. Uninstall the app completely from my phone.
  2. Export signed version of app without Comparable change. Upload to phone using: adb install com.myapp (runs fine).
  3. Export signed version of app WITH Comparable change. Upload using adb. Crash upon accessing previously saved (NullPointerException).

And as a sanity check I did the following:

  1. Uninstall the app completely from phone.
  2. install the exported, signed version of the app WITH Comparable change. No problems.

Note that the reason for NOT uninstalling in between steps 1. and 2. is to simulate the end user preserving his/her data between updates (an important feature here). Also note that the final sanity check above leads me to feel fairly certain it is an issue with the change to the Serializeable class.

So the fundamental questions are: What are the differences between a signed, exported app versus a version run from within Eclipse? Is there a reason that a valid change to a Serializable class would break the app after export?

Joey
  • 464
  • 2
  • 9

1 Answers1

2

It looks like this is a problem caused by obfuscating. The app you run through Eclipse is also signed, just with a different key/certificate, so no real difference there. If you are using ProGuard, exporting a 'release' version will also obfuscate the code, and in the process some classes or method might get removed (if ProGuard thinks they are not used). So the stack trace is important, and you need to find out where exactly the NPE is happening. Then setup ProGuard to ignore this class/method and test again.

Nikolay Elenkov
  • 52,576
  • 10
  • 84
  • 84
  • Thanks for the response. I am, indeed, using ProGuard. The Card class described above is actually nested within another serialized class (CardList). The NPE happens when the previously serialized CardList is read and tries to access a Card. I'll test what you've suggested, but do you suspect I'd have to ignore both the CardList and Card in ProGuard? If I don't hear from you, I'll try to update this comment thread with my results anyway. Thanks again. – Joey Sep 04 '11 at 05:08
  • Using your clues, I think [this is the definitive reference](http://proguard.sourceforge.net/manual/examples.html#serializable) on ProGuard'ing Serializables. So it sounds like I'm a bit hosed if I didn't keep those classes initially (for intial release of app). Keeping them now with their changes seems to keep them incompatible with the previously ProGaurded version. Any other thoughts or clarifications? – Joey Sep 04 '11 at 05:38
  • I haven't had to deal with this myself, but: does your class/classes have a `serialVersionUID` field? did you change anything besides implementing `Comparable`? If you don't have a `serialVersionUID`, you might be able to preserve compatibility by implementing `read/writeObject()`. Another idea is to give up on `Comparable`, most methods that use it also accept it as a parameter, so you could use an external implementation instead of having the class implement `Comparable`. – Nikolay Elenkov Sep 04 '11 at 08:11
  • I do have a serialVersionID in each Serializable. Comparable was the only change. So I think I'll take your advice on just comparing the items external to the class. Thanks for the help. – Joey Sep 04 '11 at 16:14