質問

I use the Restlet 2.1.0 Java library for HTTP and HTTPS communication in my Android app.

Until now I've configured Proguard to leave these libraries alone, using these keep and ignore rules:

-keep class org.restlet.** { *; }
-dontwarn org.restlet.**
-dontnote org.restlet.**

Since org.restlet.jar is 726KB and contains 5761 methods, I'm now trying to turn on Proguard to remove all the code that my app doesn't use, and safely optimize it.

Unfortunately, I'm struggling to write suitable Proguard rules, as my attempts result in Exceptions, because the Restlet library uses reflection.

These parts of my rules seem safe enough:

-keep class org.restlet.engine.connector.Connection

# Ignore these Restlet warnings, as we have never included the
# org.jsslutils.* classes in our build
-dontwarn org.restlet.ext.ssl.**

I've also tried to port the rules that I saw on this blog:

-keep class org.restlet.engine.log.LoggerFacade
-keep class org.restlet.Application
-keep class org.restlet.Client
-keep class org.restlet.Context
-keep class org.restlet.* extends org.restlet.Client { *; }
-keep class org.restlet.* extends org.restlet.Context { *; }

Unfortunately I've been hitting java.lang.NoSuchMethodExceptions like this:

10-18 18:22:12.975  3142  3165 W System.err: Exception during the instantiation of the client connector.
10-18 18:22:12.975  3142  3165 W System.err: java.lang.NoSuchMethodException: a(Client)
10-18 18:22:12.975  3142  3165 W System.err:    at java.lang.Class.getMatchingConstructor(Class.java:643)
10-18 18:22:12.975  3142  3165 W System.err:    at java.lang.Class.getConstructor(Class.java:472)
10-18 18:22:12.975  3142  3165 W System.err:    at org.restlet.engine.e.a(Engine.java:510)
10-18 18:22:12.975  3142  3165 W System.err:    at org.restlet.Client.<init>(Client.java:96)
10-18 18:22:12.975  3142  3165 W System.err:    at org.restlet.Client.<init>(Client.java:120)
10-18 18:22:12.975  3142  3165 W System.err:    at org.restlet.Client.<init>(Client.java:140)
10-18 18:22:12.975  3142  3165 W System.err:    at org.restlet.d.a.j(ClientResource.java:830)
10-18 18:22:12.975  3142  3165 W System.err:    at org.restlet.d.a.a(ClientResource.java:1045)
10-18 18:22:12.975  3142  3165 W System.err:    at org.restlet.d.a.a(ClientResource.java:1454)
10-18 18:22:12.985  3142  3165 W System.err:    at org.restlet.d.a.a(ClientResource.java:1400)

I'm working through them one-by-one by using my Proguard mapping.txt and the Restlet source code, but I thought I would check now in case somebody already knows the answer!

役に立ちましたか?

解決

First I used the -dontobfuscate flag to run Proguard without obfuscation. My exception still happened, but it was easier to see the cause:

10-20 08:43:06.725  4475  4499 W System.err: Error while handling an HTTP client call
10-20 08:43:06.725  4475  4499 W System.err: java.lang.NoSuchMethodException: ClientAdapter(Context)
10-20 08:43:06.725  4475  4499 W System.err:    at java.lang.Class.getMatchingConstructor(Class.java:643)
10-20 08:43:06.725  4475  4499 W System.err:    at java.lang.Class.getConstructor(Class.java:472)
10-20 08:43:06.725  4475  4499 W System.err:    at org.restlet.engine.adapter.HttpClientHelper.getAdapter(HttpClientHelper.java:100)
10-20 08:43:06.725  4475  4499 W System.err:    at org.restlet.engine.adapter.HttpClientHelper.handle(HttpClientHelper.java:111)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.Client.handle(Client.java:180)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.routing.Filter.doHandle(Filter.java:159)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.routing.Filter.handle(Filter.java:206)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.resource.ClientResource.handle(ClientResource.java:1137)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.resource.ClientResource.handleOutbound(ClientResource.java:1226)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.resource.ClientResource.handle(ClientResource.java:1069)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.resource.ClientResource.handle(ClientResource.java:1045)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.resource.ClientResource.post(ClientResource.java:1454)
10-20 08:43:06.735  4475  4499 W System.err:    at org.restlet.resource.ClientResource.post(ClientResource.java:1400)

I used JD-GUI to open the bin/proguard/obfuscated.jar and confirmed that the constructor had been removed. The missing constructor was in the Adapter super class.

public Adapter(Context context) {
    this.context = context;
}

The fix for that specific problem was to add this Proguard rule:

-keep class org.restlet.engine.adapter.Adapter { *; }

Rebuild and testing with a simple HTTP connect revealed more rules I needed.

Using the rules below allows me to make successful HTTP and HTTPS connections.

# Preserve a minimal number of Restlet classes.  It is particularly
# difficult to get these keep rules correct as Restlet uses quite a
# lot of reflection. 
-keep class org.restlet.engine.log.LoggerFacade
-keep class org.restlet.Application
-keep class org.restlet.Client
-keep class org.restlet.Context
-keep class org.restlet.Connector
-keep class org.restlet.Request
-keep class org.restlet.Response
-keep class org.restlet.Restlet
-keep class org.restlet.data.Status
-keep class org.restlet.engine.adapter.Adapter { *; }
-keep class org.restlet.engine.adapter.ClientCall
-keep class org.restlet.engine.adapter.ClientAdapter { *; }
-keep class org.restlet.engine.adapter.HttpClientHelper { *; }
-keep class org.restlet.engine.connector.Connection
-keep class org.restlet.engine.ClientHelper { *; }
-keep class org.restlet.engine.header.Header { *; }

-keep class org.restlet.* extends org.restlet.Client { *; }
-keep class org.restlet.* extends org.restlet.Context { *; }
-keep class org.restlet.* extends org.restlet.Connector { *; }
-keep class org.restlet.* extends org.restlet.engine.ClientHelper { *; }
-keep class org.restlet.* extends org.restlet.resource.Resource { *; }

# We use constants like Disposition.NAME_FILENAME
-keepclassmembers class org.restlet.data.Disposition { public static final *; }

# Ignore these Restlet warnings, as we have never included the
# org.jsslutils.* classes in our build
-dontwarn org.restlet.ext.ssl.**

These rules might not work for you if you use the Restlet library differently to me, but they might be a useful starting point.

他のヒント

After much trial and error, this is what I'm using:

-keep class org.restlet.Application { *; }
-keep class org.restlet.Client { *; }
-keep class org.restlet.Connector { *; }
-keep class org.restlet.Context { *; }
-keep class org.restlet.Request { *; }
-keep class org.restlet.Response { *; }
-keep class org.restlet.Restlet { *; }
-keep class org.restlet.Server { *; }
-keep class org.restlet.Uniform { *; }
-keep class org.restlet.data.AuthenticationInfo { *; }
-keep class org.restlet.data.MediaType { *; }
-keep class org.restlet.data.CacheDirective { *; }
-keep class org.restlet.data.ChallengeRequest { *; }
-keep class org.restlet.data.ChallengeResponse { *; }
-keep class org.restlet.data.ChallengeScheme { *; }
-keep class org.restlet.data.CharacterSet { *; }
-keep class org.restlet.data.ClientInfo { *; }
-keep class org.restlet.data.Conditions { *; }
-keep class org.restlet.data.Digest { *; }
-keep class org.restlet.data.Disposition { *; }
-keep class org.restlet.data.Encoding { *; }
-keep class org.restlet.data.Expectation { *; }
-keep class org.restlet.data.Form { *; }
-keep class org.restlet.data.Language { *; }
-keep class org.restlet.data.Metadata { *; }
-keep class org.restlet.data.Method { *; }
-keep class org.restlet.data.Range { *; }
-keep class org.restlet.data.Reference { *; }
-keep class org.restlet.data.ReferenceList { *; }
-keep class org.restlet.data.Parameter { *; }
-keep class org.restlet.data.Protocol { *; }
-keep class org.restlet.data.ServerInfo { *; }
-keep class org.restlet.data.Status { *; }
-keep class org.restlet.data.Tag { *; }
-keep class org.restlet.engine.adapter.* { *; }
-keep class org.restlet.engine.log.LoggerFacade { *; }
-keep class org.restlet.engine.resource.AnnotationInfo { *; }
-keep class org.restlet.representation.Representation { *; }
-keep class org.restlet.representation.RepresentationInfo { *; }
-keep class org.restlet.representation.Variant { *; }
-keep class org.restlet.resource.* { *; }
-keep class org.restlet.security.Enroler { *; }
-keep class org.restlet.security.User { *; }
-keep class org.restlet.security.Verifier { *; }
-keep class org.restlet.service.ConnectorService { *; }
-keep class org.restlet.service.ConnegService { *; }
-keep class org.restlet.service.ConverterService { *; }
-keep class org.restlet.service.DecoderService { *; }
-keep class org.restlet.service.EncoderService { *; }
-keep class org.restlet.service.MetadataService { *; }
-keep class org.restlet.service.RangeService { *; }
-keep class org.restlet.service.StatusService { *; }
-keep class org.restlet.service.TaskService { *; }
-keep class org.restlet.service.TunnelService { *; }
-keep class org.restlet.util.Series { *; }
-keep class org.restlet.util.SelectionListener { *; }

Response from Dan J (sorry, this doesn't fit in the comment section):

Thanks for the suggestion. I just tried the rules above and I get these 3 Notes:

 [proguard] Note: the configuration keeps the entry point 'org.restlet.engine.connector.ConnectionPool { void clear(org.restlet.engine.connector.Connection); }', but not the descriptor class 'org.restlet.engine.connector.Connection'
 [proguard] Note: the configuration keeps the entry point 'org.restlet.representation.Representation { void setListener(org.restlet.util.ReadingListener); }', but not the descriptor class 'org.restlet.util.ReadingListener'
 [proguard] Note: the configuration keeps the entry point 'org.restlet.util.SelectionListener { void onSelected(org.restlet.util.SelectionRegistration); }', but not the descriptor class 'org.restlet.util.SelectionRegistration'

Testing a simple HTTP connect results in a slightly different stack trace:

10-19 16:21:04.175  4042  4065 W System.err: Exception during the instantiation of the client connector.
10-19 16:21:04.175  4042  4065 W System.err: java.lang.NoSuchMethodException: a(Client)
10-19 16:21:04.175  4042  4065 W System.err:    at java.lang.Class.getMatchingConstructor(Class.java:643)
10-19 16:21:04.175  4042  4065 W System.err:    at java.lang.Class.getConstructor(Class.java:472)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.engine.e.a(Engine.java:514)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.Client.<init>(Client.java:96)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.Client.<init>(Client.java:120)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.Client.<init>(Client.java:140)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.resource.ClientResource.createNext(ClientResource.java:518)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.resource.ClientResource.getNext(ClientResource.java:830)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.resource.ClientResource.handleOutbound(ClientResource.java:1222)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.resource.ClientResource.handle(ClientResource.java:1069)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.resource.ClientResource.handle(ClientResource.java:1045)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.resource.ClientResource.post(ClientResource.java:1454)
10-19 16:21:04.185  4042  4065 W System.err:    at org.restlet.resource.ClientResource.post(ClientResource.java:1400)

I'm guess maybe we are using different Restlet versions, or different base Proguard configuration? I notice you are using a single * instead of ** (e.g. org.restlet.engine.adapter.*), so some of your rules won't descend into sub packages (docs).

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top