I have Thrift client library and few generated thrift java classes in my app project.Need help to figure out why unused Thrift client library and generated classes not been stripped out by Proguard.
I have confirmed code shrinking is enabled
minifyEnabled true
proguardFiles file('proguard-android.txt')
I completely commented out the Thrift usage inside my app.[Earlier In my code I was using only one generated class with Thrifts TDeserializer and TSerializer classes and was expecting Proguard to not keep TServer etc classes as they are not been used.But I noticed its still there.To ensure its not the generated classes retaining the Thrift client classes indirectly, I not commented it all]
Even with no one actually calling the com.test.thrift.Task inside my app code, seeds.txt is still showing the com.test.thrift.Task inside it.
My attempts to debug why the class is not been removed:
Use
-verbose
-whyareyoukeeping class com.offerup.android.thrift.Task { public * ; }
Only thing it displays is com.offerup.android.thrift.Task is kept by a directive in the configuration.
I tried to remove some candidates such as below. Doesn't seem to make any difference. Other than brute force way of removing every thing and figuring out the configuration clause, Is there better way to know why these classes are been held to?
#-keep public class * extends java.lang.Exception
#-keep class * implements android.os.Parcelable {
# public static final android.os.Parcelable$Creator *;
#}
#-keepclassmembers enum * {
# public static **[] values();
# public static ** valueOf(java.lang.String);
#}
#-keep class org.apache.** { *; }
Proguard config looks like:
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
# For Fabric (Crashlytics)
-keep class com.crashlytics.** { *; }
-keep class com.crashlytics.android.**
-keepattributes SourceFile,LineNumberTable, *Annotation*
-keep public class * extends java.lang.Exception
# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
#-dontoptimize
#-dontpreverify
# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# "proguard-android-optimize.txt" file instead of this one from your
# project.properties file.
-keepattributes *Annotation*
-keepclasseswithmembers class * {
@retrofit.http.* <methods>;
}
#-keep public class com.google.vending.licensing.ILicensingService
#-keep public class com.android.vending.licensing.ILicensingService
-keep class com.android.vending.billing.**
# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
#-keepclasseswithmembernames class * {
# native <methods>;
#}
# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
#-keepclassmembers public class * extends android.view.View {
# void set*(***);
# *** get*();
#}
# We want to keep methods in Activity that could be used in the XML attribute onClick
#-keepclassmembers class * extends android.app.Activity {
# public void *(android.view.View);
#}
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
#-keepclassmembers enum * {
# public static **[] values();
# public static ** valueOf(java.lang.String);
#}
#-keep class * implements android.os.Parcelable {
# public static final android.os.Parcelable$Creator *;
#}
#-keepclassmembers class **.R$* {
# public static <fields>;
#}
# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**
-dontwarn com.google.android.**
-dontwarn retrofit.**
-dontwarn com.square.**
-dontwarn com.squareup.**
-dontwarn org.apache.**
-dontwarn org.joda.**
-dontwarn rx.**
# Jars that we need to keep
-keep class org.apache.** { *; }
-keep class org.joda.** { *; }
-keep class com.google.** { *; }
-keep class com.mobileapptracker.** { *; }
-keep class com.urbanairship.** { *; }
-keep class com.appsflyer.** { *; }
# Libaries that we need to keep
# Jumio
-keep class com.jumio.** { *; }
-keep class jumiomobile.** { *; }
-keep class net.sf.scuba.smartcards.IsoDepCardService {*;}
-keep class org.jmrtd.** { *; }
-keep class net.sf.scuba.** {*;}
-keep class org.spongycastle.** {*;}
-keep class org.ejbca.** {*;}
-dontwarn org.ejbca.**
-dontwarn org.spongycastle.**
# Apptentive
-dontwarn com.apptentive.android.sdk.**
-keepattributes SourceFile,LineNumberTable
-keep class com.apptentive.android.sdk.** { *; }
-keep class com.facebook.** { *; }
-keep class com.android.** { *; }
-keep class uk.co.senab.** { *; }
# Gradle libraries that we need to keep
-keep class retrofit.** { *; }
-keep class com.square.** { *; }
-keep class com.squareup.** { *; }
-keep class com.crashlytics.** { *; }
-keep class com.stripe.** { *; }
# Android libraries that we need to keep
#-keep class android.support.** { *; }
-keep class com.google.android.** { *; }
#-keep interface android.support.** { *; }
##
# Allow obfuscation of android.support.v7.internal.view.menu.**
# to avoid problem on Samsung 4.2.2 devices with appcompat v21
# see https://code.google.com/p/android/issues/detail?id=78377
#-keep class !android.support.v7.internal.view.menu.**,android.support.** {*;}
# https://code.google.com/p/android/issues/detail?id=78377#c224
# MenuBuilder not found issue on AppCompat 23.1+. Keep all classes except the ones indicated by the patterns preceded by an exclamation mark
#http://cudou.com/pages/ddbffhcj-once-again-getting-java-lang-noclassdeffounderror-android-support-v7-internal-v.html
-keep class !android.support.v7.view.menu.**,!android.support.design.internal.NavigationMenu,!android.support.design.internal.NavigationMenuPresenter,!android.support.design.internal.NavigationSubMenu,** {*;}
# For otto subscribers/producers
-keepclassmembers class ** {
@com.squareup.otto.Subscribe public *;
@com.squareup.otto.Produce public *;
}
# More retrofit fixes
-keep class javax.inject.* { *; }
-keepattributes Signature
-keep class sun.misc.Unsafe { *; }
# okhttp 2.2 fixes
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# Local files that are needed
-keep class com.byarger.exchangeit.** { *; }
-keep class com.test.R { *; }
-keep class com.test.R$* { *; }
-keep class com.test.BuildConfig { *; }
-keep class com.test.Manifest { *; }
-keep class com.test.Manifest$* { *; }
# Trying other things to get search to load
# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
# Ensure the unreferenced Creator isn't removed by proguard
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# We want to keep methods in Activity that could be used in the XML attribute onClick
#-keepclassmembers class * extends android.app.Activity {
# public void *(android.view.View);
#}
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#-keep class * implements retrofit.Callback { *; }
-keepclassmembers class com.test.android.activities.BaseActivity {
protected android.location.LocationManager mLocationManager;
}
-keepclass class com.test.android.bus.BusProvider { *; }
-keep class com.test.android.gson.** { *; }
-keep class com.test.android.dto.** { *; }
-keep class android.support.v7.widget.** {*;}
-keepclassmembers class * {
@com.leanplum.annotations.* <fields>;
}
-keep class com.leanplum.** { *; }
-dontwarn com.leanplum.**
#RXJava work arounds
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
long producerNode;
long consumerNode;
}
Thrift generated unused class looks like:
/**
* Autogenerated by Thrift Compiler (0.9.3)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
*/
package com.test.thrift;
import org.apache.thrift.scheme.IScheme;
import org.apache.thrift.scheme.SchemeFactory;
import org.apache.thrift.scheme.StandardScheme;
import org.apache.thrift.scheme.TupleScheme;
import org.apache.thrift.protocol.TTupleProtocol;
import org.apache.thrift.EncodingUtils;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import javax.annotation.Generated;
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
@Generated(value = "Autogenerated by Thrift Compiler (0.9.3)", date = "2016-09-30")
public class Task implements org.apache.thrift.TBase<Task, Task._Fields>, java.io.Serializable, Cloneable, Comparable<Task> {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Task");
private static final org.apache.thrift.protocol.TField IDENTIFIER_FIELD_DESC = new org.apache.thrift.protocol.TField("Identifier", org.apache.thrift.protocol.TType.I64, (short)1);
private static final org.apache.thrift.protocol.TField DESCRIPTION_FIELD_DESC = new org.apache.thrift.protocol.TField("Description", org.apache.thrift.protocol.TType.STRING, (short)2);
private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
static {
schemes.put(StandardScheme.class, new TaskStandardSchemeFactory());
schemes.put(TupleScheme.class, new TaskTupleSchemeFactory());
}
/**
* A unique identifier for this task.
*/
public long Identifier; // required
/**
* A description of the task.
*/
public String Description; // required
/** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements org.apache.thrift.TFieldIdEnum {
/**
* A unique identifier for this task.
*/
IDENTIFIER((short)1, "Identifier"),
/**
* A description of the task.
*/
DESCRIPTION((short)2, "Description");
private static final Map<String, _Fields> byName = new HashMap<String, _Fields>();
static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
}
/**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // IDENTIFIER
return IDENTIFIER;
case 2: // DESCRIPTION
return DESCRIPTION;
default:
return null;
}
}
/**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
}
/**
* Find the _Fields constant that matches name, or null if its not found.
*/
public static _Fields findByName(String name) {
return byName.get(name);
}
private final short _thriftId;
private final String _fieldName;
_Fields(short thriftId, String fieldName) {
_thriftId = thriftId;
_fieldName = fieldName;
}
public short getThriftFieldId() {
return _thriftId;
}
public String getFieldName() {
return _fieldName;
}
}
// isset id assignments
private static final int __IDENTIFIER_ISSET_ID = 0;
private byte __isset_bitfield = 0;
public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap;
static {
Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class);
tmpMap.put(_Fields.IDENTIFIER, new org.apache.thrift.meta_data.FieldMetaData("Identifier", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I64 , "id")));
tmpMap.put(_Fields.DESCRIPTION, new org.apache.thrift.meta_data.FieldMetaData("Description", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Task.class, metaDataMap);
}
public Task() {
this.Identifier = -1L;
}
public Task(
long Identifier,
String Description)
{
this();
this.Identifier = Identifier;
setIdentifierIsSet(true);
this.Description = Description;
}
/**
* Performs a deep copy on <i>other</i>.
*/
public Task(Task other) {
__isset_bitfield = other.__isset_bitfield;
this.Identifier = other.Identifier;
if (other.isSetDescription()) {
this.Description = other.Description;
}
}
public Task deepCopy() {
return new Task(this);
}
@Override
public void clear() {
this.Identifier = -1L;
this.Description = null;
}
/**
* A unique identifier for this task.
*/
public long getIdentifier() {
return this.Identifier;
}
/**
* A unique identifier for this task.
*/
public Task setIdentifier(long Identifier) {
this.Identifier = Identifier;
setIdentifierIsSet(true);
return this;
}
public void unsetIdentifier() {
__isset_bitfield = EncodingUtils.clearBit(__isset_bitfield, __IDENTIFIER_ISSET_ID);
}
/** Returns true if field Identifier is set (has been assigned a value) and false otherwise */
public boolean isSetIdentifier() {
return EncodingUtils.testBit(__isset_bitfield, __IDENTIFIER_ISSET_ID);
}
public void setIdentifierIsSet(boolean value) {
__isset_bitfield = EncodingUtils.setBit(__isset_bitfield, __IDENTIFIER_ISSET_ID, value);
}
/**
* A description of the task.
*/
public String getDescription() {
return this.Description;
}
/**
* A description of the task.
*/
public Task setDescription(String Description) {
this.Description = Description;
return this;
}
public void unsetDescription() {
this.Description = null;
}
/** Returns true if field Description is set (has been assigned a value) and false otherwise */
public boolean isSetDescription() {
return this.Description != null;
}
public void setDescriptionIsSet(boolean value) {
if (!value) {
this.Description = null;
}
}
public void setFieldValue(_Fields field, Object value) {
switch (field) {
case IDENTIFIER:
if (value == null) {
unsetIdentifier();
} else {
setIdentifier((Long)value);
}
break;
case DESCRIPTION:
if (value == null) {
unsetDescription();
} else {
setDescription((String)value);
}
break;
}
}
public Object getFieldValue(_Fields field) {
switch (field) {
case IDENTIFIER:
return getIdentifier();
case DESCRIPTION:
return getDescription();
}
throw new IllegalStateException();
}
/** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
}
switch (field) {
case IDENTIFIER:
return isSetIdentifier();
case DESCRIPTION:
return isSetDescription();
}
throw new IllegalStateException();
}
@Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof Task)
return this.equals((Task)that);
return false;
}
public boolean equals(Task that) {
if (that == null)
return false;
boolean this_present_Identifier = true;
boolean that_present_Identifier = true;
if (this_present_Identifier || that_present_Identifier) {
if (!(this_present_Identifier && that_present_Identifier))
return false;
if (this.Identifier != that.Identifier)
return false;
}
boolean this_present_Description = true && this.isSetDescription();
boolean that_present_Description = true && that.isSetDescription();
if (this_present_Description || that_present_Description) {
if (!(this_present_Description && that_present_Description))
return false;
if (!this.Description.equals(that.Description))
return false;
}
return true;
}
@Override
public int hashCode() {
List<Object> list = new ArrayList<Object>();
boolean present_Identifier = true;
list.add(present_Identifier);
if (present_Identifier)
list.add(Identifier);
boolean present_Description = true && (isSetDescription());
list.add(present_Description);
if (present_Description)
list.add(Description);
return list.hashCode();
}
@Override
public int compareTo(Task other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
}
int lastComparison = 0;
lastComparison = Boolean.valueOf(isSetIdentifier()).compareTo(other.isSetIdentifier());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetIdentifier()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.Identifier, other.Identifier);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetDescription()).compareTo(other.isSetDescription());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetDescription()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.Description, other.Description);
if (lastComparison != 0) {
return lastComparison;
}
}
return 0;
}
public _Fields fieldForId(int fieldId) {
return _Fields.findByThriftId(fieldId);
}
public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException {
schemes.get(iprot.getScheme()).getScheme().read(iprot, this);
}
public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Task(");
boolean first = true;
sb.append("Identifier:");
sb.append(this.Identifier);
first = false;
if (!first) sb.append(", ");
sb.append("Description:");
if (this.Description == null) {
sb.append("null");
} else {
sb.append(this.Description);
}
first = false;
sb.append(")");
return sb.toString();
}
public void validate() throws org.apache.thrift.TException {
// check for required fields
// check for sub-struct validity
}
private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException {
try {
write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out)));
} catch (org.apache.thrift.TException te) {
throw new java.io.IOException(te);
}
}
private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException {
try {
// it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor.
__isset_bitfield = 0;
read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in)));
} catch (org.apache.thrift.TException te) {
throw new java.io.IOException(te);
}
}
private static class TaskStandardSchemeFactory implements SchemeFactory {
public TaskStandardScheme getScheme() {
return new TaskStandardScheme();
}
}
private static class TaskStandardScheme extends StandardScheme<Task> {
public void read(org.apache.thrift.protocol.TProtocol iprot, Task struct) throws org.apache.thrift.TException {
org.apache.thrift.protocol.TField schemeField;
iprot.readStructBegin();
while (true)
{
schemeField = iprot.readFieldBegin();
if (schemeField.type == org.apache.thrift.protocol.TType.STOP) {
break;
}
switch (schemeField.id) {
case 1: // IDENTIFIER
if (schemeField.type == org.apache.thrift.protocol.TType.I64) {
struct.Identifier = iprot.readI64();
struct.setIdentifierIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 2: // DESCRIPTION
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.Description = iprot.readString();
struct.setDescriptionIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
default:
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
iprot.readFieldEnd();
}
iprot.readStructEnd();
// check for required fields of primitive type, which can't be checked in the validate method
struct.validate();
}
public void write(org.apache.thrift.protocol.TProtocol oprot, Task struct) throws org.apache.thrift.TException {
struct.validate();
oprot.writeStructBegin(STRUCT_DESC);
oprot.writeFieldBegin(IDENTIFIER_FIELD_DESC);
oprot.writeI64(struct.Identifier);
oprot.writeFieldEnd();
if (struct.Description != null) {
oprot.writeFieldBegin(DESCRIPTION_FIELD_DESC);
oprot.writeString(struct.Description);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}
}
private static class TaskTupleSchemeFactory implements SchemeFactory {
public TaskTupleScheme getScheme() {
return new TaskTupleScheme();
}
}
private static class TaskTupleScheme extends TupleScheme<Task> {
@Override
public void write(org.apache.thrift.protocol.TProtocol prot, Task struct) throws org.apache.thrift.TException {
TTupleProtocol oprot = (TTupleProtocol) prot;
BitSet optionals = new BitSet();
if (struct.isSetIdentifier()) {
optionals.set(0);
}
if (struct.isSetDescription()) {
optionals.set(1);
}
oprot.writeBitSet(optionals, 2);
if (struct.isSetIdentifier()) {
oprot.writeI64(struct.Identifier);
}
if (struct.isSetDescription()) {
oprot.writeString(struct.Description);
}
}
@Override
public void read(org.apache.thrift.protocol.TProtocol prot, Task struct) throws org.apache.thrift.TException {
TTupleProtocol iprot = (TTupleProtocol) prot;
BitSet incoming = iprot.readBitSet(2);
if (incoming.get(0)) {
struct.Identifier = iprot.readI64();
struct.setIdentifierIsSet(true);
}
if (incoming.get(1)) {
struct.Description = iprot.readString();
struct.setDescriptionIsSet(true);
}
}
}
}