2012-02-18 26 views
16

Estoy intentando crear un cliente simple de Android para un servicio web que he creado, pero algo falla en tiempo de ejecución.Jersey Client en Android - NullPointerException

Aquí es el cliente:

public class MyClient extends AsyncTask<EditText, Integer, String> { 

private EditText text; 

protected String doInBackground(EditText... strings) { 

    RuntimeDelegate 
      .setInstance(new com.sun.ws.rs.ext.RuntimeDelegateImpl()); 
    WebResource wbr; 
    Client client = Client.create(); 
    wbr = client 
      .resource("http://my.ip:8080/MazeService/rest/service/hello"); 
    String result = wbr.queryParam("number", "10") 
      .accept(MediaType.APPLICATION_JSON).get(String.class); 
    text = strings[0]; 
    return result; 
} 

protected void onProgressUpdate(Integer... progress) { 
    // setProgressPercent(progress[0]); 
} 

protected void onPostExecute(String result) { 
    if (result != null) 
     this.text.setText(result); 
    else 
     Log.d("MyApp", "No Result"); 
} 

}

y el Servicio:

@Path("/service") 
public class AndroidService { 

@GET 
@Produces(MediaType.APPLICATION_JSON) 
@Path("/hello") 
public String getJSON(@QueryParam("number") String nr) 
{ 

    String s = "Hi there! The number is: "+nr; 
    return s; 
} 
} 

consigo este StackTrace en LogCat:

02-18 16:26:06.446: E/AndroidRuntime(1381): FATAL EXCEPTION: AsyncTask #1 
02-18 16:26:06.446: E/AndroidRuntime(1381): java.lang.RuntimeException: An error occured while executing doInBackground() 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at android.os.AsyncTask$3.done(AsyncTask.java:278) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.util.concurrent.FutureTask.setException(FutureTask.java:124) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.util.concurrent.FutureTask.run(FutureTask.java:137) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.lang.Thread.run(Thread.java:856) 
02-18 16:26:06.446: E/AndroidRuntime(1381): Caused by: java.lang.NullPointerException 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at javax.ws.rs.core.MediaType.valueOf(MediaType.java:119) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.sun.jersey.api.client.ClientResponse.getType(ClientResponse.java:615) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:532) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:506) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.sun.jersey.api.client.WebResource.handle(WebResource.java:674) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.sun.jersey.api.client.WebResource$Builder.get(WebResource.java:503) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.maze.client.MyClient.doInBackground(MyClient.java:25) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at com.maze.client.MyClient.doInBackground(MyClient.java:1) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at android.os.AsyncTask$2.call(AsyncTask.java:264) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305) 
02-18 16:26:06.446: E/AndroidRuntime(1381):  ... 5 more 

no entiendo que pro blem es, ya que un cliente similar funciona en PC. No hay problema con la dirección IP, ya que puedo acceder al servidor desde el dispositivo virtual Android.

La versión de Android es 4.0.3 API versión 15. Para el cliente utilicé Jersey 1.11.

EDITAR

También he tratado de eliminar todo el asunto con el EditarTexto, ya que alguien me dijo que no se utiliza como debe ser. Aún así, tengo el mismo error. Simplemente no puedo entender qué estoy haciendo mal. Si debo mostrar cualquier otra información adicional, házmelo saber. Además, CUALQUIER idea es buena. Soy nuevo en Android y podría haber cometido algunos errores estúpidos.

+0

Su problema es una NullPointer algo que ver con 'String result = wbr.queryParam (" number "," 10 ") .accept (MediaType.APPLICATION_JSON) .get (String.class);' – Blundell

+0

@Blundell Sí, pensé que ese podría ser el problema , pero no entiendo POR QUÉ surge este problema, ya que un programa simple que contiene ese código es funcionando perfectamente – Dragos

+0

@ L7ColWinters No entiendo exactamente cómo se supone que debo hacer eso. Por favor proporciona un código. – Dragos

Respuesta

18

sugerencia de Pablo es correcta. Me metí un poco más en ese tema y encontré una razón.

La herramienta de empaquetado Android apk (clase APKBuilder de sdklib.jar) ignora las diferentes carpetas mientras crea el paquete (source). Una de esas carpetas es META-INF, sin importar si está en la biblioteca adjunta o en la carpeta de origen del proyecto.

La solución para la versión 1.8 (y probablemente también otra, pero no la probé) de Jersey es proporcionar implementación personalizada (por defecto se busca META-INF/services/que no está presente en Android) para ServiceFinder$ServiceIteratorProvider que tiene nombres de clase de proveedor codificados.Basé mi aplicación en this aplicación propuesto por 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(); 
    } 
} 

me quita la mayor parte de las clases ya que no hizo uso de ellos y también que estaban causando errores de verificación en Dalvik VM ...

NOTA : Significa que con esta implementación puede usar solo un subconjunto de encabezados/tipos admitidos por Jersey (solo necesitaba String). Para admitir otros tipos, debe agregar proveedores ubicados en la carpeta META-INF/services de jersey-client.jar (o de la implementación de Lucas) y verificar si no causan errores de verificación en Android.

El código anterior se debe utilizar de la siguiente manera:

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

EDIT:

Parece que el problema ha sido fixed en android-maven-plugin.

mosa ... @ gmail.com escribió:

La solicitud de extracción con la solución se fusionó con maestría y se dará a conocer con 3.2.1:

1

Siguiendo el código:

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 } 

comprobaría que el delegado utilizado por MediaType no es 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 } 

Parece que tal vez es delegatenull para un cliente (Android) y no null para su otro cliente.

Eche un vistazo a esto para tener una idea de cómo se inicializa el delegado. Puede ayudar a sus investigaciones.

javax/ws/rs/ext/RuntimeDelegate.java

+0

Gracias, veré dónde puedo conseguir esto. – Dragos

Cuestiones relacionadas