2012-02-18 22 views
16

Próbuję utworzyć prostego klienta dla systemu Android dla usługi internetowej, którą zbudowałem, ale coś jest nie tak w czasie wykonywania.Klient sieci Jersey na system Android - NullPointerException

Oto Klient:

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"); 
} 

}

i usługi:

@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; 
} 
} 

otrzymuję ten StackTrace w 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 

nie rozumiem co za profesjonalista blem jest, ponieważ podobny klient działa na PC. Nie ma problemu z adresem IP, ponieważ mogę uzyskać dostęp do serwera z Android VirtualDevice.

Wersja Androida to wersja 4.0.3 interfejsu API 15. Dla klienta użyłem wersji Jersey 1.11.

EDIT

Próbowałem też usunąć całą sprawę z EditText, ponieważ ktoś powiedział mi, że nie jest używany tak, jak powinno być. Mimo to mam ten sam błąd. Po prostu nie mogę zrozumieć, co robię źle. Jeśli powinienem wyświetlić inne dodatkowe informacje, proszę dać mi znać. DOWOLNY pomysł jest dobry. Jestem nowy na Androida i mogłem popełnić głupie błędy.

+0

Twój problem jest NullPointer coś zrobić z 'wyniku String = wbr.queryParam ("numer", "10") .accept (MediaType.APPLICATION_JSON) .get (String.class);' – Blundell

+0

@Blundell Tak, myślałem, że może być problem , ale nie rozumiem DLACZEGO problem ten pojawia się, ponieważ prosty program zawierający ten kod jest działa idealnie. – Dragos

+0

@ L7ColWinters Nie rozumiem dokładnie, jak mam to zrobić. Proszę podać kod. – Dragos

Odpowiedz

18

Pawła sugestia jest prawidłowa. W tym temacie doszedłem trochę głębiej i znalazłem powód.

Narzędzie do pakowania apk na Androida (klasa APKBuilder z sdklib.jar) ignoruje różne foldery podczas tworzenia pakietu (source). Jednym z tych folderów jest META-INF - bez względu na to, czy znajduje się w dołączonej bibliotece, czy w folderze źródłowym projektu.

Obejście dla wersji 1.8 (i prawdopodobnie inne również, ale ja tego nie przetestowałem) z Jersey jest dostarczenie niestandardowego (domyślnie wyszukuje się META-INF/services/który nie jest obecny w apk Androida) implementacji dla ServiceFinder$ServiceIteratorProvider który ma zakodowane nazwy dostawców usług.Oparłem mój realizacja na this realizacji proponowanego przez 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(); 
    } 
} 

usunąłem większość klas od I nie korzystaliśmy z nich, a także były przyczyną błędów weryfikacji na Dalvik VM ...

UWAGA : Oznacza to, że przy tej implementacji można używać tylko podzbioru nagłówków/typów obsługiwanych przez Jersey (potrzebowałem tylko String). Aby obsługiwać inne typy, musisz dodać dostawców znajdujących się w folderze META-INF/services programu jersey-client.jar (lub z implementacji Lucasa) i sprawdzić, czy nie powodują one błędów weryfikacji w systemie Android.

Powyższy kod powinien być stosowany w sposób następujący:

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

EDIT:

Wydaje się, że problem był fixed w android-maven-plugin.

Mosa ... @ gmail.com napisał:

Żądanie wyciągnąć z poprawki został włączony do mistrza i ukaże się z 3.2.1:

1

Po kodzie:

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 } 

Chciałbym sprawdzić, czy delegat wykorzystywane przez MediaType nie jest 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 } 

Wygląda może delegate jest null dla jednego klienta (Android), a nie null dla innego klienta.

Spójrz na to, aby uzyskać informacje na temat inicjowania delegata. Może to pomóc w twoich dochodzeniach.

javax/ws/rs/ext/RuntimeDelegate.java

+0

Dziękuję, zobaczę, gdzie mogę to osiągnąć. – Dragos

Powiązane problemy