9

I am using Gson to Map data into ArrayList. It's working fine while running app on device or in debug mode but it is not mapping data in Production Mode APK. Here is the Code

Const.courses = new ArrayList<>();
    Log.v("Courses",object.toString());
    Type type = new TypeToken<ArrayList<Course>>() {
    }.getType();

    if(object != null && object.has("data") ){

        try {
            if(object.get("data") != null && object.getJSONArray("data").length()>0) {
                Const.courses.clear();
                Const.courses = new GsonBuilder().create().fromJson(object.getJSONArray("data").toString(), type);

                Log.d("Course from Array",Const.courses.get(0).getTitle());

  adapter = new CourseAdapter(getApplicationContext(), R.layout.course_row_layout, Const.courses);
                listView.setDivider(new ColorDrawable(ContextCompat.getColor(getApplicationContext(), android.R.color.transparent)));
                listView.setAdapter(adapter);
            }else{
                tvSelectCourse.setVisibility(View.GONE);
                tvNoCourse.setVisibility(View.VISIBLE);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }
}

Here is The Logcat. Any help will be Appreciated.

    02-16 23:30:57.836 1234-1234/? V/Courses: {"contentEncoding":null,"contentType":null,"data":[{"id":1,"title":"course Updated"},{"id":12,"title":"Arabic"},{"id":13,"title":"usman"},{"id":14,"title":"really "},{"id":15,"title":"urdu"},{"id":17,"title":"abc"},{"id":21,"title":"course"},{"id":22,"title":"Ali don"},{"id":24,"title":"umair"},{"id":25,"title":"math"},{"id":27,"title":"world"},{"id":28,"title":"wether"},{"id":33,"title":"computer Science "},{"id":34,"title":"cs"},{"id":37,"title":"maths"},{"id":38,"title":"hello"},{"id":39,"title":"course Updated"},{"id":42,"title":"for testing purpose"}],"jsonRequestBehavior":0,"maxJsonLength":null,"recursionLimit":null}
02-16 23:30:57.852 1234-1234/? D/AndroidRuntime: Shutting down VM
02-16 23:30:57.859 1234-1234/? E/AndroidRuntime: FATAL EXCEPTION: main
                                                 Process: com.umer.doratiteacher, PID: 1234
                                                 java.lang.NullPointerException: println needs a message
                                                     at android.util.Log.println_native(Native Method)
                                                     at android.util.Log.d(Log.java:139)
                                                     at com.umer.doratiteacher.MainActivity$3.a(Unknown Source)
                                                     at com.umer.doratiteacher.d.a$3.a(Unknown Source)
                                                     at com.umer.doratiteacher.d.a$3.a(Unknown Source)
                                                     at com.a.a.a.i.a(Unknown Source)
                                                     at com.a.a.e$a.run(Unknown Source)
                                                     at android.os.Handler.handleCallback(Handler.java:739)
                                                     at android.os.Handler.dispatchMessage(Handler.java:95)
                                                     at android.os.Looper.loop(Looper.java:234)
                                                     at android.app.ActivityThread.main(ActivityThread.java:5526)
                                                     at java.lang.reflect.Method.invoke(Native Method)
                                                     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Hobbit
  • 601
  • 1
  • 9
  • 22
  • did you use the pro_guard ? it will change your class name. you have to define some setting in the pro_guard file. – king Feb 16 '17 at 18:52
  • here is what you are asking for .. buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } – Hobbit Feb 16 '17 at 18:56
  • What pro_guard settings i need? – Hobbit Feb 16 '17 at 18:58

6 Answers6

17

@Gary has correctly identified that Proguard is the problem here. Solution given by him does work. Another way to achieve this is to add

@Keep 

annotation from androidx.annotation.Keep to yourClass.

Aalap
  • 2,847
  • 2
  • 26
  • 24
  • Upvote for simplicity and the issue is exactly what the annotation was created for. Another advantage: The annotation actually is immediately visible on the according class and not somewhere hidden in the config. – Loop Oct 09 '22 at 14:44
16

As has been said, the problem arises from Proguard (when minifyEnabledtrue). Proguard gets rid of Type which GSON needs for parsing.

Based on this link, https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg, I was able to get my code to work by only adding 3 lines

# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature

# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }

# Application classes that will be serialized/deserialized over Gson
-keep class com.example.YourPackage.** { *; }

The last line is the name of the package your code is in. If you have all of your code in one main package (all of the java files in one directory) or only have GSON parsing in one file, then substitute last line for this (I've tested the above but have not test the following)

-keep class com.example.YourPackage.YourClass.** { *; }
Gary99
  • 1,750
  • 1
  • 19
  • 33
1

here is the code in build.gradle

buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
            lintOptions {
                disable 'MissingTranslation'
            }
        }
...

you have to define which classes should not change their name in proguard_rules.pro

here is mine:

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose

-dontwarn org.joda.time.**
-keep class org.joda.time.** {
*;
}

-keep class com.xxx.xxx.** {
public *;
}

-keep class android.support.**{*;}
-keep class org.jsoup.**{*;}
-keep class com.google.**{*;}

use -keep to keep your class name or method name, here is the detail link from the file

# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html
king
  • 304
  • 1
  • 8
1

You should annotate your class properties, so Gson knows what to look for:

    public class RData {

    @SerializedName("code")
    public String code;
    @SerializedName("info")
    public String info; 
}
behnam b
  • 19
  • 1
  • The problem wasn't related to GSON Mapping. It's already working in dev. It was because of minifyEnabled True. – Hobbit Apr 20 '20 at 11:25
1

After all day struggling with this I ended up here and the above response marked as valid pointed me in the right direction. I already had all that proguard rules mentioned and all worked fine until...I've updated gradle and Android Studio!
Now Android Studio uses R8 with proguard rules as default. So I look for answers at R8 and voila! Here is the link

I added to the above rules the following:

-keepclassmembers,allowobfuscation class * {
 @com.google.gson.annotations.SerializedName <fields>;
}

And finally worked for me. Hope this helps others

Javier
  • 483
  • 1
  • 5
  • 11
0

At first glance of the stack trace, it looks like you're passing null to your Log call:

Log.d("Course from Array",Const.courses.get(0).getTitle());.

A quick fix would be to check to make sure getTitle() method isn't null before passing it to the log method. So, something like Log.d("Course from Array", TextUtils.isEmpty(Const.courses.get(0).getTitle()) ? "" : Const.courses.get(0).getTitle());

Submersed
  • 8,810
  • 2
  • 30
  • 38
  • Nope. It is not null. You can check Courses in logcat. The first one is present. Moreover, it works fine in debug mode.. – Hobbit Feb 16 '17 at 18:55
  • 1
    Just sayin' from the stacktrace you provided. It's possible that it's a pro-guard/minify issue then. – Submersed Feb 16 '17 at 18:56
  • What kind of issue ?? Can you please elaborate what you are trying to say?? – Hobbit Feb 16 '17 at 19:00
  • 1
    Pro-guard/minifyEnabled obfuscates code, so it could be stripping out classes you need in your production build that are present in your debug build. Also, looking at the stack trace that looks possible. Try switching minifiedEnabled = false for a prod build and see if it fixes the issue. If it does you'll need to configure your pro-guard rules to make sure you aren't stripping out important classes. – Submersed Feb 16 '17 at 19:01