I am trying to show this JSON in my app using retrofit by network calls.
Sample data:
{
total: 17727,
total_pages: 1773,
results: [
{
id: "qX9Ie7ieb1E",
created_at: "2016-01-26T08:17:45-05:00",
updated_at: "2018-05-18T13:04:07-04:00",
width: 4956,
height: 3304,
color: "#101617",
description: "Neon mural of woman wearing sunglasses and pink lipstick on brick wall in England",
urls: { raw: "", regular: "", full: "", small:"", thumb:""}
},
{}, {}, ...
]
}
I have created the interface like below:
public interface ApiService {
#GET("search/photos")
Call<ImagesResponse> getImages(
#Query("client_id") String clientId,
#Query("order_by") String orderBy,
#Query("query") String query,
#Query("page") int pageIndex
);
}
and the api:
public class UnsplashImageApi {
public static Retrofit retrofit = null;
private static OkHttpClient getOkHttpClient() {
return new OkHttpClient.Builder()
.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
.build();
}
public static Retrofit getRetrofitClient() {
if (retrofit != null) {
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(getOkHttpClient())
.baseUrl(Constants.UNSPLASH_BASE_URL)
.build();
}
return retrofit;
}
}
The model class is structured based on the response like below:
public class ImagesResponse {
#Expose
#SerializedName("total")
private Integer total;
#Expose
#SerializedName("total_pages")
private Integer totalPages;
#Expose
#SerializedName("results")
private List<Results> imagesList = new ArrayList<>();
Omitted getters and setters for brevity. The Results class is shown like this:
public class Results {
#Expose
#SerializedName("id")
public String imageId;
#Expose
#SerializedName("raw")
public String rawImg;
#Expose
#SerializedName("full")
public String fullImg;
#Expose
#SerializedName("regular")
public String regularImg;
#Expose
#SerializedName("small")
public String smallImg;
#Expose
#SerializedName("thumb")
public String thumbImg;
public boolean isFavorite;
In my fragment, I have tried calling the ApiService to load first page data like below:
apiService = UnsplashImageApi.getRetrofitClient().create(ApiService.class);
loadFirstPage();
but the app crashes on this line getRetrofitClient().create() with this error log:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object retrofit2.Retrofit.create(java.lang.Class)' on a null object reference
at com.jjoey.walpy.fragments.ArtFragment.onCreateView(ArtFragment.java:70)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2346)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1428)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1759)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1827)
I have omitted the code to load the first page as it's not the source of concern here. Can anyone please explain to me why it's crashing and with a possible solution? Thanks
You have written that retrofit != null within the if condition.
public static Retrofit getRetrofitClient(){
if (retrofit != null) { // Check this line
return new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(getOkHttpClient())
.baseUrl(Constants.UNSPLASH_BASE_URL)
.build();
}
return retrofit;
}
You have to write retrofit == null within the if condition. Otherwise, Retrofit will not get initialized.
The very first-time Retrofit is null and you have checked retrofit != null then. That's why the code within the if condition bypassed and it's creating the Null Pointer Exception as you are calling create() method from a non-initialized instance of Retrofit.
Modified code will be:
public static Retrofit getRetrofitClient(){
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(getOkHttpClient())
.baseUrl(Constants.UNSPLASH_BASE_URL)
.build();
}
return retrofit;
}
You have a NPE because your singleton logic is incorrect. Adjusted version below:
public static Retrofit getRetrofitClient(){
if (null == retrofit){
retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(getOkHttpClient())
.baseUrl(Constants.UNSPLASH_BASE_URL)
.build();
}
return retrofit;
}
Related
I'm having these lines of code
public class RetrofitClient {
private static Retrofit retrofit=null;
public static Retrofit getClient(String baseUrl)
{
if(retrofit==null)
{
retrofit = new Retrofit().Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
and i'm getting this error...
error: constructor Retrofit in class Retrofit cannot be applied to given types;
required: okhttp3.Call.Factory,HttpUrl,List,List,Executor,boolean
found: no arguments
reason: actual and formal argument lists differ in length
If i put my cursor on the error Retrofit i can see that it says
Retrofit(okhttp3.....)is not public in 'retrofit2.retrofit'.Cannot be accessed from outside package
Any help please?
You should be correct you code as below mentioned.
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient1(String baseUrl) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
Ok my bad... I should have used Retrofit.Builder() instead of Retrofit().Builder().
Below I prepared some of the code I use when I execute query to server.
Response from the server returns list with books in json format. One of the fields params contains list in json array.
How could I force Moshi to treat this field as a raw string?
Here is an explanation of my code.
This is a pseudo-class from where I call instance of Retrofit.
public final class RestApi {
private static final String API_ENDPOINT_KEY = "...";
private final MyApi myApi;
private RestApi() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
OkHttpClient client = builder.build();
final Retrofit retrofit = (new Retrofit.Builder())
.baseUrl(API_ENDPOINT_KEY)
.client(client)
.addConverterFactory(MoshiConverterFactory.create())
.build();
myApi = retrofit.create(MyApi.class);
}
public static RestApi getInstance() {
return RestApiGazeta.InstanceHolder.INSTASNCE;
}
private static final class InstanceHolder {
private static final RestApi INSTASNCE = new RestApi();
}
}
This is the Book class with prepared fields for Moshi.
#Generated("com.robohorse.robopojogenerator")
public class Book{
#Json(name = "pages")
private int pages;
#Json(name = "params")
private Params params;
}
This is the query for Retrofit for querying list of books.
#GET("android/getBooks.json")
Call<List<Book>> getBooks(
#Query("section") String section
);
What I have tried was I changed return type in Book class type Params to String but I was returning exception about Expected a string but was BEGIN_ARRAY at path $[0].params.
Okay, so I've read Moshi documentation and it seems to be easy to convert any field to json string. I don't know how to parse it on the fly but here is the solution how to do it after You get ready (in my case) Book object.
Moshi moshi = new Moshi.Builder().build();
JsonAdapter<Params> jsonAdapter = moshi.adapter(Params.class);
String json = jsonAdapter.toJson(body.getParams());
That's it.
Im using Retrofit2 converter-simplexml library,the code run successful when I using converter-gson,but when I add simplexmlConverter,I got a exception:
java.lang.IllegalArgumentException: Unable to create converter for java.util.List<com.rengwuxian.rxjavasamples.model.ZhuangbiImage>
Caused by: java.lang.IllegalArgumentException: Could not locate ResponseBody converter for java.util.List<com.rengwuxian.rxjavasamples.model.ZhuangbiImage>.
This is where I am trying to execute the retro http request:
private void search(String key) {
subscription = getZhuangbiApi()
.search(key)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
public static ZhuangbiApi getZhuangbiApi() {
if (zhuangbiApi == null) {
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(baseUrl)
.addConverterFactory(simpleXmlConverterFactory)
.addCallAdapterFactory(rxJavaCallAdapterFactory)
.build();
zhuangbiApi = retrofit.create(ZhuangbiApi.class);
}
return zhuangbiApi;
}
My interface which turned to be my API
public interface ZhuangbiApi {
#GET("merchant/list")
Observable<List<ZhuangbiImage>> search(#Query("app_code") String appCode);
}
And the ZhuangbiImage class
#Root(name = "item")
public class ZhuangbiImage {
#Element(name = "title")
public String title;
#Element(name = "merchant_id")
public String merchantId;
}
XML can only be deserialized to a concrete type and not a list of types like JSON. In this case, the body type should represent the <rss> tag and its children and not List<ZhuangbiImage>.
More details from official retrofit team Retrofit, XML and SimpleXmlConverterFactory problem
You have to check your dependencies in gradle-file. Also check that the version of retrofit and converter is the same.
I am using retrofit:2.0.0-beta4 for my android app.
I tried to add a user with Retrofit, the user is correctly created in Database, however I got the following error:
03-14 06:04:27.731 30572-30600/com.lehuo.lehuoandroid D/OkHttp: CALLING POST SP_User_CreateUser....your new user_id:48
{"data":{"user_id":"48","nickname":null,"password":null,"status":null},"status":1,"msg":"OK"}
03-14 06:04:27.731 30572-30600/com.lehuo.lehuoandroid D/OkHttp: <-- END HTTP (147-byte body)
03-14 06:04:27.732 30572-30600/com.lehuo.lehuoandroid E/My Jobs: error while executing job
com.google.gson.stream.MalformedJsonException: Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $
at com.google.gson.stream.JsonReader.syntaxError(JsonReader.java:1573)
at com.google.gson.stream.JsonReader.checkLenient(JsonReader.java:1423)
at com.google.gson.stream.JsonReader.doPeek(JsonReader.java:587)
at com.google.gson.stream.JsonReader.peek(JsonReader.java:429)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:202)
at com.google.gson.TypeAdapter.fromJson(TypeAdapter.java:260)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:32)
at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:23)
at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:213)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:177)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:87)
at com.lehuo.lehuoandroid.async.NetworkJob.callNet(NetworkJob.java:30)
at com.lehuo.lehuoandroid.async.CreateUserJob.onRun(CreateUserJob.java:34)
at com.path.android.jobqueue.BaseJob.safeRun(BaseJob.java:108)
at com.path.android.jobqueue.JobHolder.safeRun(JobHolder.java:60)
at com.path.android.jobqueue.executor.JobConsumerExecutor$JobConsumer.run(JobConsumerExecutor.java:201)
at java.lang.Thread.run(Thread.java:818)
The returned result from server is :
{"data":{"user_id":"48","nickname":null,"password":null,"status":null},"status":1,"msg":"OK"}
This is correct json format, I don't understand why I get such exception?
Here us my interface:
public class ApiResult<T> {
public T data;
public int status;
public String msg;
}
public interface ApiUsers {
#POST("/users/new")
public Call<ApiResult<User>> createUser(#Body User user);
}
public class User {
public int user_id;
public String registration;
public int registration_type;
public String avatar;
public String nickname;
public String password;
public String status;
}
public class Api {
// TODO modify the value
public static final String BASE_URL = "xxx";
private static Api instance = new Api();
public static Api getInstance() {
return instance;
}
private Api(){}
public Retrofit getRetrofit() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.retryOnConnectionFailure(true)
.connectTimeout(15, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit;
}
public <S> S createService(Class<S> serviceClass) {
return getRetrofit().create(serviceClass);
}
}
The caller code is:
ApiUsers api = Api.getInstance().createService(ApiUsers.class);
Call<ApiResult<User>> call = api.createUser(user);
CreateUserMessage message = new CreateUserMessage();
callNet(call, message);
Could anyone give any clue?
Finally I solved my problem which is not related to the json lenient mode, something wrong with my POST response (there some other non json output before the json data).
Here is the response from JakeWharton regarding how to set Gson lenient mode:
make sure that you have:compile 'com.google.code.gson:gson:2.6.1'
Gson gson = new GsonBuilder()
.setLenient()
.create();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
I solved the problem
Gson gson = new GsonBuilder().setLenient().create();
OkHttpClient client = new OkHttpClient();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://kafe.netai.net/")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.7'
I solved my problem by removing extra HTML text and other white spaces from my JSON file.
I'm writting a retrofit demo.
I have to use "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code" to get code.
when writing rest, I do it like this:
public interface WXService {
#GET("/access_token?grant_type=authorization_code")
Observable<AccessTokenModel> getAccessToken(#Query("appid") String appId,
#Query("secret") String secretId,
#Query("code") String code);
}
public class WXRest {
private static final String WXBaseUrl = "https://api.weixin.qq.com/sns/oauth2/";
private WXService mWXService;
public WXRest() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(WXBaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
mWXService = retrofit.create(WXService.class);
}
public void getAccessToken(String code) {
mWXService.getAccessToken(Constants.APP_ID, Constants.SECRET_ID, code)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<AccessTokenModel>() {
#Override
public void call(AccessTokenModel accessTokenModel) {
Log.e("WX", "accessToken:" + accessTokenModel.accessToken);
}
});
}
}
but I got an error:
java.lang.IllegalArgumentException: Unable to create call adapter for
rx.Observable
I think it's the way i transform the url wrong.But I don't know how to fix it.
i think you should include adapter-rxjava lib to your gradle dependencies.
compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta1'
and then add call adapter factory to your retrofit builder
public WXRest() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(WXBaseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
mWXService = retrofit.create(WXService.class);
}