Jersey Client on Android - NullPointerException

前端 未结 2 1468
忘掉有多难
忘掉有多难 2020-12-05 11:04

I am trying to create a simple Android client for a web service I have built, but something goes wrong at runtime.

Here is the Client:

public class M         


        
相关标签:
2条回答
  • 2020-12-05 11:39

    Following the code:

    com/sun/jersey/api/client/ClientResponse.getType()

    613    public MediaType getType() {
    614        String ct = getHeaders().getFirst("Content-Type");
    615        return (ct != null) ? MediaType.valueOf(ct) : null;
    616    }
    

    I'd check that the delegate used by MediaType is not null.

    javax/ws/rs/core/MediaType.valueOf(java.lang.String)

    118    public static MediaType valueOf(String type) throws IllegalArgumentException {
    119        return delegate.fromString(type);
    120    }
    

    It looks like maybe delegate is null for one client (Android) and not null for your other client.

    Take a look at this to get an idea for how the delegate is initialised. It may help your investigations.

    javax/ws/rs/ext/RuntimeDelegate.java

    0 讨论(0)
  • 2020-12-05 11:45

    Paul's suggestion is correct. I got a little bit deeper in that subject and found a reason.

    Android apk packaging tool (APKBuilder class from sdklib.jar) ignores different folders while it creates the package (source). One of those folders is META-INF - no matter if its in the attached library or in the project's source folder.

    Workaround for version 1.8 (and probably other also, but I didn't test it) of Jersey is to provide custom (default one looks up META-INF/services/ which is not present in android apk) implementation for ServiceFinder$ServiceIteratorProvider which has hard coded provider class names. I based my implementation on this implementation proposed by Lucas:

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.Iterator;
    
    import android.util.Log;
    
    import com.sun.jersey.spi.service.ServiceFinder.ServiceIteratorProvider;
    
    public class AndroidServiceIteratorProvider<T> extends ServiceIteratorProvider<T> {
    
        private static final String TAG = AndroidServiceIteratorProvider.class.getSimpleName();
        private static final String MESSAGE = "Unable to load provider";
    
        private static final HashMap<String, String[]> SERVICES = new HashMap<String, String[]>();
    
        private static final String[] com_sun_jersey_spi_HeaderDelegateProvider = {
                "com.sun.jersey.core.impl.provider.header.MediaTypeProvider",
                "com.sun.jersey.core.impl.provider.header.StringProvider"
        };
    
        private static final String[] com_sun_jersey_spi_inject_InjectableProvider = { 
        };
    
        private static final String[] javax_ws_rs_ext_MessageBodyReader = {
                "com.sun.jersey.core.impl.provider.entity.StringProvider",
                "com.sun.jersey.core.impl.provider.entity.ReaderProvider"
        };
    
        private static final String[] javax_ws_rs_ext_MessageBodyWriter = {
                "com.sun.jersey.core.impl.provider.entity.StringProvider",
                "com.sun.jersey.core.impl.provider.entity.ReaderProvider"
        };
    
        static {
            SERVICES.put("com.sun.jersey.spi.HeaderDelegateProvider",
                    com_sun_jersey_spi_HeaderDelegateProvider);
            SERVICES.put("com.sun.jersey.spi.inject.InjectableProvider",
                    com_sun_jersey_spi_inject_InjectableProvider);
            SERVICES.put("javax.ws.rs.ext.MessageBodyReader",
                    javax_ws_rs_ext_MessageBodyReader);
            SERVICES.put("javax.ws.rs.ext.MessageBodyWriter",
                    javax_ws_rs_ext_MessageBodyWriter);
            SERVICES.put("jersey-client-components", new String[]{});
            SERVICES.put("com.sun.jersey.client.proxy.ViewProxyProvider", new String[]{});
        }
    
        @SuppressWarnings("unchecked")
        @Override
        public Iterator<Class<T>> createClassIterator(Class<T> service,
                String serviceName, ClassLoader loader,
                boolean ignoreOnClassNotFound) {
    
            String[] classesNames = SERVICES.get(serviceName);
            int length = classesNames.length;
            ArrayList<Class<T>> classes = new ArrayList<Class<T>>(length);
            for (int i = 0; i < length; i++) {
                try {
                    classes.add((Class<T>) Class.forName(classesNames[i]));
                } catch (ClassNotFoundException e) {
                    Log.v(TAG, MESSAGE,e);
                }
            }
            return classes.iterator();
        }
    
        @Override
        public Iterator<T> createIterator(Class<T> service, String serviceName,
                ClassLoader loader, boolean ignoreOnClassNotFound) {
    
            String[] classesNames = SERVICES.get(serviceName);
            int length = classesNames.length;
            ArrayList<T> classes = new ArrayList<T>(length);
                for (int i = 0; i &lt; length; i++) {
                try {
                    classes.add(service.cast(Class.forName(classesNames[i])
                            .newInstance()));
                } catch (IllegalAccessException e) {
                    Log.v(TAG, MESSAGE,e);
                } catch (InstantiationException e) {
                    Log.v(TAG, MESSAGE,e);
                } catch (ClassNotFoundException e) {
                    Log.v(TAG, MESSAGE,e);
                }
            }
    
            return classes.iterator();
        }
    }
    

    I removed most of the classes since I didn't use them and also they were causing verification errors on dalvik vm...

    NOTE: It means that with this implementation you can use only subset of headers/types supported by Jersey (I needed only String). To support other types you have to add providers located in META-INF/services folder of jersey-client.jar (or from Lucas' implementation) and check if they don't cause verification errors on Android.

    The above code should be used as follows:

    ServiceFinder.setIteratorProvider(new AndroidServiceIteratorProvider());
    Client client = Client.create();
    

    EDIT:

    It seems that the problem have been fixed in android-maven-plugin.

    mosa...@gmail.com wrote:

    The pull request with the fix was merged into master and will be released with 3.2.1:

    0 讨论(0)
提交回复
热议问题