我的Thrift客户端库和我的应用程序项目中很少生成thrift java类。需要帮助弄清楚为什么未使用的Thrift客户端库和生成的类没有被Proguard剥离。
我已确认已启用代码收缩
minifyEnabled true
proguardFiles file('proguard-android.txt')
我完全注释掉了我的应用程序中的Thrift用法。[早期在我的代码中我只使用了一个带Thrifts TDeserializer和TSerializer类的生成类,并期望Proguard不保留TServer等类,因为它们没有被使用。但是我注意到它仍然存在。为了确保它不是间接保留Thrift客户端类的生成类,我没有对它进行全部评论]
即使没有人实际调用我的应用程序代码中的com.test.thrift.Task,seeds.txt仍然显示其中的com.test.thrift.Task。
我尝试调试未移除类的原因:
使用
-verbose
-whyareyoukeeping class com.offerup.android.thrift.Task { public * ; }
它唯一显示的是com.offerup.android.thrift.Task 由配置中的指令保存。
我试图删除下面的一些候选人。似乎没有任何区别。除了蛮力删除所有东西并搞清楚配置条款的方法之外,还有更好的方法来了解为什么这些类被保留了吗?
#-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配置看起来像:
# 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生成的未使用的类看起来像:
/**
* 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);
}
}
}
}
答案 0 :(得分:3)
我有类似的问题,但ProGuard没有告诉你哪个指令强制保留类。但是,通过查看您的配置,我怀疑这一行可能是罪魁祸首:
-keep class !android.support.v7.view.menu.**,!android.support.design.internal.NavigationMenu,!android.support.design.internal.NavigationMenuPresenter,!android.support.design.internal.NavigationSubMenu,** {*;}
我怀疑最后**
可能会强制保留所有课程,包括你的课程。
此外,您可以使用ClassyShark分析APK中的类和方法,而无需安装APK,因此您可以做的一件事是通过规则进行二元搜索以查看哪个正在制作上课。