I am trying to make generic implementation for adding and showing progress bar in fragment / activity when there are multiple calls.
Anyone have any better solution rather than taking two references of progress bar and toggling its visibility? The implementation should be generic which can be applied to any views.
Add this interface in your Project....
public interface RetrofitCallback {
<T> void getSuccessCallBack(Response<T> response, int position);
void getErrorCallBack(String message, int position);
}
add these methods in your Utility or RetrofitUtility.
public static <T> void callRetrofit(final Context context, final Fragment fragment, Call<T> call, final int position, final RetrofitCallback callback) {
final ProgressDialog pDialog = CommonMethod.showProgressDialog(context, context.getResources().getString(R.string.loading));// progress for whole application
call.enqueue(new Callback<T>() {
#Override
public void onResponse(Call<T> call, Response<T> response) {
pDialog.dismiss();
if (response.isSuccessful()) {
ResultBody result = (ResultBody) response.body();
if (result.isSuccess())
callback.getSuccessCallBack(response, position);
else
callback.getErrorCallBack(result.getMessage(), position);
} else
Toast.makeText(context, response.message(), Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<T> call, Throwable t) {
pDialog.dismiss();
if (CommonMethod.checkconnection(context)) { // checking internet connection
Toast.makeText(context, "Server_error", Toast.LENGTH_SHORT).show();
} else {
CommonMethod.showconnectiondialog(context, fragment);
}
}
});
}
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl("XXXXXXXXXXXXX")
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
All Retrofit API
public interface RetrofitApi {
#POST("allData")
Call<UserData> getAllData();
}
Implement RetrofitCallback in your Activity and Fragment
Call Api like this
Call<UserData> call = ApiClient.getClient().create(RetrofitApi.class).getAllData();
ApiClient.callRetrofit(context, this, call, 0, this);
and you will get data from RetrofitCallback override methods below
#Override
public <T> void getSuccessCallBack(Response<T> response, int position) {
UserData userData = ((UserData) response.body());
// do your operation over here
}
#Override
public void getErrorCallBack(String message, int position) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
Here is all models file....
ResultBody.class
public class ResultBody {
private String message;
private boolean success;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
UserData.class
public class UserData extends ResultBody {
private User userData;
public User getUserData() {
return userData;
}
public void setUserData(User user) {
this.userData = user;
}
}
Let me know if you stuck anywhere.....
Relative Posts or Questions
Android:dynamically pass model class to retrofit callback
Related
I want to show Json result in textview using retrofit2, but I can't get the desired result.
json
{
"rows" : [ {
"playerId" : "f8a49812eb9c3e5c8e738c3664e0ebd1",
"nickname" : "Yuichuan",
"grade" : 89
} ]
}
Model.class
public class Model {
public String playerId;
public String nickname;
public int grade;
public String getPlayerId() {
return playerId;
}
public void setPlayerId(String playerId) {
this.playerId = playerId;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
PlayerModel.class
public class PlayerModel {
public List<Model> rows;
}
**RetrofitFactory.class**
public class RetrofitFactory {
private static String BASE_URL="https://api.neople.co.kr/";
public static RetrofitService create(){
Retrofit retrofit=new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonC`enter code here`onverterFactory.create()).build();
return retrofit.create(RetrofitService.class);
}
}
RetrofitService.interface
public interface RetrofitService {
#GET("cy/players/")
Call<PlayerModel> getlist(#Query("nickname") String nickname,
#Query("apikey") String apikey);
}
MainActivityt
public class MainActivity extends AppCompatActivity {
private static final String API_KEY = "8yTuVMwy2DirHl2sWaZ3C8IJLslOfTpQ";
private static final String TAG ="Main" ;
TextView textView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView=findViewById(R.id.test);
RetrofitService networkService=RetrofitFactory.create();
networkService.getlist("Yuichuan",API_KEY)
.enqueue(new Callback<PlayerModel>() {
#Override
public void onResponse(Call<PlayerModel> call, Response<PlayerModel> response) {
if(!response.isSuccessful()){
textView.setText(response.code());
return;
}
Log.d(TAG, "onResponse: ConfigurationListener::"+call.request().url());
}
#Override
public void onFailure(Call<PlayerModel> call, Throwable t) {
textView.setText(t.getMessage());
}
});
}
}
I want to print out these json files, but it doesn't work as I want, so I want you to give me some help or advice.It seems like a simple problem, but it is very difficult for me because I am not used to json.
The textView.setText(response.code()); give you a code of response. To get your data model you need a body of response response.body()
I've added some code to make my question more clear.
Retrofit interface:
public interface JsonPlaceHolderAPI {
public static final String BASE_URL = "https://jsonplaceholder.typicode.com/";
#GET("todos/{number}")
Call<ResponseBody> getJsonResponse(#Path("number") String number);
}
The repository: --> fetchResponse() takes Viewmodel's MutableLiveData as parameter and uses it to update its value and then trigger View to change its UI.
public class Repository {
private final JsonPlaceHolderAPI api;
public Repository() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.build();
api = retrofit.create(JsonPlaceHolderAPI.class);
}
public void fetchResponse(String number, final MutableLiveData<CharSequence> mld){
final MutableLiveData<CharSequence> ml = new MutableLiveData<>();
api.getJsonResponse(number).enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
mld.setValue(response.body().string());
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {}
});
}
}
The viewModel:
public class MainActivityViewModel extends AndroidViewModel {
MutableLiveData<CharSequence> response = new MutableLiveData<>();
Repository repository;
public MainActivityViewModel(#NonNull Application application) {
super(application);
repository = new Repository();
}
public void fetchData(String number) {
response.setValue("Loading data");
repository.fetchResponse(number, response);
}
public LiveData<? extends CharSequence> getLiveData() {
return response;
}
}
The View:
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
initViews();
viewModel.getLiveData().observe(this, new Observer<CharSequence>() {
#Override
public void onChanged(CharSequence charSequence) {
if (charSequence != null) {
txt.setText(charSequence);
}
}
});
}
...
I am not sure if I should pass the MutableLiveData from the viewModel to the Repository.
Is there any recommended way to let viewModel know that data is ready to be published from Repository??
I have read a lot of questions and articles and still I don't get it. I would love if somebody explain to me a nice way to achieve it!
Api
public interface TodoApi {
#GET("todos/")
Call<List<Todo>> getTodos();
#GET("todos/{id}")
Call<Todo> getTodo(#Path("id") long id);
}
Respository
public class TodoRepository {
private static final String TAG = "TodoRepository";
private static final TodoRepository ourInstance = new TodoRepository();
private TodoApi api;
private MutableLiveData<List<Todo>> todoListLiveData = new MutableLiveData<>();
private MutableLiveData<Todo> todoLiveData = new MutableLiveData<>();
public static TodoRepository getInstance() {
return ourInstance;
}
private TodoRepository() {
api = ApiBuilder.create(TodoApi.class);
}
public LiveData<List<Todo>> getTodos() {
api.getTodos().enqueue(new Callback<List<Todo>>() {
#Override
public void onResponse(#NonNull Call<List<Todo>> call, #NonNull Response<List<Todo>> response) {
todoListLiveData.setValue(response.body());
}
#Override
public void onFailure(#NonNull Call<List<Todo>> call, #NonNull Throwable t) {
Log.d(TAG, "onFailure: failed to fetch todo list from server");
}
});
return todoListLiveData;
}
public LiveData<Todo> getTodo(long id) {
api.getTodo(id).enqueue(new Callback<Todo>() {
#Override
public void onResponse(#NonNull Call<Todo> call, #NonNull Response<Todo> response) {
todoLiveData.setValue(response.body());
}
#Override
public void onFailure(#NonNull Call<Todo> call, #NonNull Throwable t) {
Log.d(TAG, "onFailure: failed to get todo");
}
});
return todoLiveData;
}
}
ViewModel
public class MainActivityViewModel extends ViewModel {
private static final String TAG = "MainActivityViewModel";
private TodoRepository repository = TodoRepository.getInstance();
private MutableLiveData<Boolean> isLoading = new MutableLiveData<>();
private LiveData<List<Todo>> todoLiveData;
public MainActivityViewModel() {
super();
isLoading.setValue(true);
todoLiveData = repository.getTodos();
}
#Override
protected void onCleared() {
super.onCleared();
}
public MutableLiveData<Boolean> getIsLoading() {
return isLoading;
}
public LiveData<List<Todo>> getTodoLiveData() {
return todoLiveData;
}
}
View
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
todoListRecyclerView = findViewById(R.id.todo_recycler_view);
loadingIndicator = findViewById(R.id.todo_loading_indicator);
mViewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
getSupportActionBar().setTitle("Todos");
mViewModel.getIsLoading().observe(this, new Observer<Boolean>() {
#Override
public void onChanged(Boolean isLoading) {
if (isLoading) loadingIndicator.setVisibility(View.VISIBLE);
else loadingIndicator.setVisibility(View.GONE);
}
});
mViewModel.getTodoLiveData().observe(this, new Observer<List<Todo>>() {
#Override
public void onChanged(List<Todo> todos) {
mViewModel.getIsLoading().postValue(false);
initRecyclerView(todos);
}
});
}
For full sample
https://github.com/AnvarNazar/RetrofitTypicodeApiExample
I am using MVP to decouple my view and model in my android application. I need to know how the model should feedback the result of the business logic to the view.
If for example a button is pressed to login, the activity would be like this, using butterknife #OnClick annotation:
#OnClick(R.id.login_button)
public void login() {
String email = mEmailEditText.getText().toString();
String password = mPasswordEditText.getText().toString();
LoginCredentials loginCredentials = new LoginCredentials(email, password);
mPresenter.loginWithEmail(loginCredentials);
}
The presenter would then validate and make a request to the repository:
public void loginWithEmail(LoginCredentials loginCredentials) {
boolean isEmailValid = AuthValidator.validateEmail(loginCredentials.getUsername());
boolean isPasswordValid = AuthValidator.validatePassword(loginCredentials.getPassword());
if(isEmailValid && isPasswordValid) repository.loginEmailUser(loginCredentials);
if (!isEmailValid) view.handleInvalidEmail();
if (!isPasswordValid) view.handleInvalidPassword();
}
The repository would then execute the business logic:
#Override
public void loginEmailUser(LoginCredentials loginCredentials) {
Call<Token> call = userServiceApi.loginInToken(loginCredentials);
call.enqueue(new Callback<Token>() {
#Override
public void onResponse(#NonNull Call<Token> call, #NonNull Response<Token> response) {
if (response.isSuccessful()) {
// handle successful login
} else {
// Handle unsuccessful login
}
}
#Override
public void onFailure(#NonNull Call<Token> call, #NonNull Throwable t) {
// Handle failed request
}
});
Where the comments say // handle unsuccessful something, how does the model feedback to the view the outcomes of the business logic so that the view can handle these outcomes?
Thank you.
You can use a interface as callback, for example :
public interface RepositoryCallback {
void onLoginEmailUserSuccess(/*paramaters if you need*/);
void onLoginEmailUserError(/*paramaters if you need*/);
void onRequestFailed(/*paramaters if you need*/)
}
In the repository defined the listener
public class MyRepository {
private RepositoryCallback mListener;
#Override
public void loginEmailUser(LoginCredentials loginCredentials) {
Call<Token> call = userServiceApi.loginInToken(loginCredentials);
call.enqueue(new Callback<Token>() {
#Override
public void onResponse(#NonNull Call<Token> call, #NonNull Response<Token> response) {
if (response.isSuccessful()) {
// handle successful login
if (mListener != null) {
mListener.onLoginEmailUserSuccess()
}
} else {
// Handle unsuccessful login
if (mListener != null) {
mListener.onLoginEmailUserError()
}
}
}
#Override
public void onFailure(#NonNull Call<Token> call, #NonNull Throwable t) {
// Handle failed request
if (mListener != null) {
mListener.onRequestFailed()
}
}
});
public void setRepositoryCallback(RepositoryCallback listener) {
mListener = listener;
}
}
Then set the presenter as listener :
public class MyPresenter implements RepositoryCallback {
public void loginWithEmail(LoginCredentials loginCredentials) {
repository.setRepositoryCallback(this) // here or in the presenter constructor
boolean isEmailValid = AuthValidator.validateEmail(loginCredentials.getUsername());
boolean isPasswordValid = AuthValidator.validatePassword(loginCredentials.getPassword());
if(isEmailValid && isPasswordValid) repository.loginEmailUser(loginCredentials);
if (!isEmailValid) view.handleInvalidEmail();
if (!isPasswordValid) view.handleInvalidPassword();
}
#Override
public void onLoginEmailUserSuccess(//paramaters if you need){
// your code
}
#Override
public void onLoginEmailUserError(//paramaters if you need){
// your code
}
#Override
public void onRequestFailed(//paramaters if you need){
// your code
}
}
Hope this helps.
Sorry for my english.
I have some api that contains JSON with partner names.
I've set up interface and pojo model. I'll post below my code.
Here is my POJO model Partner:
public class Partner {
#SerializedName("name")
#Expose
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Now here is my interface:
public interface APIService {
#GET("Partner")
Call<List<Partner>> getPartners();
}
And here is my APIHelper:
public class APIHelper {
public static final String PARTNERS_URL = "https://part-of-url.herokuapp.com/partners.json/";
public static APIService apiService;
public static APIService getApiService() {
if (apiService == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(PARTNERS_URL)
.addConverterFactory(GsonConverterFactory.create()).build();
apiService = retrofit.create(APIService.class);
}
return apiService;
}
}
And this is Fragment which contains Button where onClick method needs to get data from web.
public class DownloadMain extends Fragment implements Callback<Partner> {
private static final String TAG = DownloadMain.class.getSimpleName();
private Button dloadPartners;
private Call callPartners;
public DownloadMain() {}
public DownloadMain newInstance() { return new DownloadMain(); }
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.download_main, container, false);
dloadPartners = (Button) view.findViewById(R.id.downloadPartners);
dloadPartners.setOnClickListener(btnListener);
callPartners = APIHelper.getApiService().getPartners();
return view;
}
Button.OnClickListener btnListener = (new Button.OnClickListener() {
#Override
public void onClick(View v) {
callPartners.clone().enqueue(DownloadMain.this);
}
});
#Override
public void onResponse(Call call, Response response) {
if(response.body() == null) {
try {
response.errorBody().string();
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(getActivity(), "No Partners!", Toast.LENGTH_SHORT).show();
} else {
List<String> partners = (List<String>) response.body();
Log.d(TAG, "Number of partners received: " + partners.size());
Toast.makeText(getActivity(), "Partners downloaded!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call call, Throwable t) {
}
}
This is all my code for getting Partners from web.
I'm getting this error on server side and can't resolve it:
No route matches [GET] "/partners.json/Partner"
QUESTION: Can someone look at this code and say what is wrong and why I'm not getting data from web and also how should I resolve this no route error?
change your get path like so
public interface APIService {
#GET("partners.json")
Call<List<Partner>> getPartners();
}
and remove that path from your base url.
public class APIHelper {
public static final String PARTNERS_URL = "https://part-of-url.herokuapp.com/";
public static APIService apiService;
public static APIService getApiService() {
if (apiService == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(PARTNERS_URL)
.addConverterFactory(GsonConverterFactory.create()).build();
apiService = retrofit.create(APIService.class);
}
return apiService;
}
}
I am trying to implement the server connections with retrofit library.
Everything seems fine but when I receive the data on success callback it crashes with the below exception.
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to aandroid.com.retrofitframework.requestData.DataList
at cuiserve.com.volleyframework.activity.RetrofitActivity$1.onDataReceived(RetrofitActivity.java:18)
at cuiserve.com.volleyframework.httpConnection.ConnectionHelper.success(ConnectionHelper.java:44)
at retrofit.CallbackRunnable$1.run(CallbackRunnable.java:45)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
here is the activity class making the server calls
public class RetrofitActivity extends SuperActivity {
private ConnectionHelper.ServerListener<DataList> httpListener = new ConnectionHelper.ServerListener<DataList>() {
#Override
public void onDataReceived(DataList data) {
hideProgressBar();
Log.d("ANSH",data.getBalance());
}
#Override
public void onErrorReceived(String errorMsg) {
hideProgressBar();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
showProgressBar();
ConnectionHelper<DataList> helper = new ConnectionHelper<DataList>(HttpRequestConstant.LOGIN_REQUEST,httpListener);
helper.getResponse();
}
#Override
public View getLayoutResource() {
return null;
}
#Override
protected void internetAvailable() {
}
}
ConnectionHelper.class making the request
public class ConnectionHelper<T> implements Callback<T> {
private int requestType;
private ServerListener<T> listener;
public ConnectionHelper(int requestType, ServerListener listener) {
this.requestType = requestType;
this.listener = listener;
}
public void getResponse() {
switch (requestType) {
case HttpRequestConstant.LOGIN_REQUEST:
IServerConnection<T> connection = restAdapter.create(IServerConnection.class);
connection.login(this);
break;
}
}
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://www.json-generator.com/api")
.setLogLevel(RestAdapter.LogLevel.FULL)
.setConverter(new JacksonConverter(Mapper.get()))
.build();
#Override
public void success(T t, Response response) {
listener.onDataReceived(t);
// success callback false here with the exception
// as ClassCastException and says LinkedHashMap can not be cast to DataList(which i pass as class that the response has to be mapped to)
}
#Override
public void failure(RetrofitError error) {
}
public interface ServerListener<T> {
public void onDataReceived(T data);
public void onErrorReceived(String errorMsg);
}
}
The interface and the method with retrofit annotation
public interface IServerConnection<T> {
#GET(HttpRequestConstant.JACKSON_FETCH)
void login(Callback<T> cb);
}
The custom JacksonConverter which is my suspicion
public class JacksonConverter implements Converter {
private ObjectMapper mapper = new ObjectMapper();
public JacksonConverter(ObjectMapper objectMapper) {
this.mapper = objectMapper;
}
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
JavaType javaType = mapper.getTypeFactory().constructType(type);
try {
return mapper.readValue(body.in(), javaType);
} catch (IOException e) {
throw new ConversionException(e);
}
}
#Override
public TypedOutput toBody(Object object) {
try {
String charset = "UTF-8";
String json = mapper.writeValueAsString(object);
return new JsonTypedOutput(json.getBytes(charset));
} catch (IOException e) {
throw new AssertionError(e);
}
}
private static class JsonTypedOutput implements TypedOutput {
private final byte[] jsonBytes;
JsonTypedOutput(byte[] jsonBytes) {
this.jsonBytes = jsonBytes;
}
#Override
public String fileName() {
return null;
}
#Override
public String mimeType() {
return "application/json; charset=UTF-8";
}
#Override
public long length() {
return jsonBytes.length;
}
#Override
public void writeTo(OutputStream out) throws IOException {
out.write(jsonBytes);
}
}
}
and the DataList
package cuiserve.com.volleyframework.requestData;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Created by ansh on 5/4/15.
*/
#JsonIgnoreProperties(ignoreUnknown = true)
public class DataList extends SomeClass{
private String _id;
private int index;
private String guid;
private boolean isActive;
private String balance;
private String picture;
private int age;
public String get_id() {
return _id;
}
public void set_id(String _id) {
this._id = _id;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getGuid() {
return guid;
}
public void setGuid(String guid) {
this.guid = guid;
}
public boolean isActive() {
return isActive;
}
public void setActive(boolean isActive) {
this.isActive = isActive;
}
public String getBalance() {
return balance;
}
public void setBalance(String balance) {
this.balance = balance;
}
public String getPicture() {
return picture;
}
public void setPicture(String picture) {
this.picture = picture;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Now the problem is when i do it without using generics(which i have done in the code) things work fine and don't give any exception but in case of generics it fails.
Why am I getting LinkedHashMap classCastException where nothing is related to that in my code.Please help.
By using a generic parameter the actual type information is completely lost to the runtime and cannot be inferred. It essentially ends up being the same as Object. When Gson sees that you want an Object type, it uses a Map to place the JSON information in. This way, if you re-serialize that object instance the data will be retained.
You cannot use generic interfaces with Retrofit. An exception has been added when you try to do this rather than letting it fail in this manner for the next release.
Here is what I do to handle such cases:
import retrofit.Callback;
import retrofit.RetrofitError;
import retrofit.client.Response;
public abstract class ModelCallback<Model> implements Callback<Object> {
Class classOfT;
public ModelCallback(Class modelClass){
this.classOfT = modelClass;
}
#Override
public void success(Object model, Response response) {
Gson gson = new Gson();
onSuccess((Model) gson.fromJson(gson.toJson(model),classOfT));
}
#Override
public void failure(RetrofitError error) {
onError(new Exception(error.getMessage()));
}
public abstract void onSuccess(Model model);
public abstract void onError(ClyngException e);
}