Клиент Джерси на Android - NullPointerException
Я пытаюсь создать простой Android-клиент для созданного мной веб-сервиса, но во время выполнения что-то идет не так.
Вот клиент:
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");
}
}
и Сервис:
@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;
}
}
Я получаю эту трассировку стека в 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
Я не понимаю, в чем проблема, поскольку аналогичный клиент работает на ПК. С IP-адресом проблем нет, так как я могу получить доступ к серверу из Android VirtualDevice.
Версия Android - 4.0.3 API версии 15. Для клиента я использовал Jersey 1.11.
РЕДАКТИРОВАТЬ
Я также пытался удалить все это с помощью EditText, так как кто-то сказал мне, что он не используется в должном порядке. Тем не менее, я получил ту же ошибку. Я просто не могу понять, что я делаю не так. Если мне нужно отобразить любую дополнительную информацию, пожалуйста, дайте мне знать. Кроме того, ЛЮБАЯ идея хороша. Я новичок в Android и, возможно, допустил несколько глупых ошибок.
2 ответа
Предложение Павла верно. Я немного углубился в эту тему и нашел причину.
Инструмент упаковки Android apk (класс APKBuilder из sdklib.jar) игнорирует разные папки при создании пакета ( источника). Одна из этих папок - META-INF - независимо от того, находится ли она в прикрепленной библиотеке или в исходной папке проекта.
Обходной путь для версии 1.8 (и, вероятно, другой, но я ее не тестировал) Джерси состоит в том, чтобы предоставить пользовательскую (по умолчанию ищущую META-INF/services/, которая отсутствует в Android apk) реализацию для ServiceFinder$ServiceIteratorProvider, которая имеет жестко закодированные имена классов провайдеров. Я основал свою реализацию на этой реализации, предложенной Лукасом:
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 < 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();
}
}
Я удалил большинство классов, так как не использовал их, а также они вызывали ошибки проверки на dalvik vm...
ПРИМЕЧАНИЕ. Это означает, что в этой реализации вы можете использовать только подмножество заголовков / типов, поддерживаемых Джерси (мне нужна была только строка). Для поддержки других типов вы должны добавить провайдеров, расположенных в папке META-INF / services jersey-client.jar (или из реализации Lucas), и проверить, не вызывают ли они ошибки проверки на Android.
Приведенный выше код должен использоваться следующим образом:
ServiceFinder.setIteratorProvider(new AndroidServiceIteratorProvider());
Client client = Client.create();
РЕДАКТИРОВАТЬ:
Похоже, проблема была исправлена в android-maven-plugin.
Мозаика...@gmail.com пишет:
Запрос на удаление с исправлением был объединен с мастером и будет выпущен с 3.2.1:
После кода:
ком / ВС / Джерси / API / клиент /ClientResponse.getType()
613 public MediaType getType() {
614 String ct = getHeaders().getFirst("Content-Type");
615 return (ct != null) ? MediaType.valueOf(ct) : null;
616 }
Я бы проверил, что делегат, используемый MediaType, не null
,
javax / WS /RS / ядро /MediaType.valueOf(java.lang.String)
118 public static MediaType valueOf(String type) throws IllegalArgumentException {
119 return delegate.fromString(type);
120 }
Похоже, может быть delegate
является null
для одного клиента (Android), а не null
для вашего другого клиента.
Взгляните на это, чтобы получить представление о том, как инициализируется делегат. Это может помочь вашим расследованиям.