How to wait for the retrofit call response - android

I have my own rest api for application so when I create post request to api I want to wait for the response and then do something. how I can wait for the call response.
insert TicketList
public long insertApi(TicketListTable ticketListTable) throws IOException {
Call<TicketListTable> ticketListTableCall = dataService.createTicketList(ticketListTable);
ticketListTableCall.enqueue(new Callback<TicketListTable>() {
#Override
public void onResponse(Call<TicketListTable> call, Response<TicketListTable> response) {
}
#Override
public void onFailure(Call<TicketListTable> call, Throwable t) {
}
});
// want to wait for the call to complete then return the value
//ticketListTableCall.execute();
Log.d("CODE", "insertApi: "+ ticketListTableCall.isExecuted());
return repo.insert(ticketListTable);
}
this method is called when button is clicked
public void saveListToDb(String[] split, String regexData){
String newListName = listName.getText().toString();
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
TicketListTable newTicketListTable = new TicketListTable();
newTicketListTable.setTicketListName(newListName);
newTicketListTable.setTicketListCreated(sd.format(Calendar.getInstance().getTime()));
newTicketListTable.setTicketListUpdated(sd.format(Calendar.getInstance().getTime()));
long ticketTableListId = 0;
try {
ticketTableListId = ticketTableVm.insert(newTicketListTable);
} catch (IOException e) {
e.printStackTrace();
}
if(ticketTableListId<=0){
throw new ArithmeticException();
}
for (String s : split) {
String[] values = s.split(regexData);
String number = values[0].replace("\n", "").replace("\r", "");
String info = values[1];
String warningNote = values[2];
String warning = values[3];
String customer = values[4];
int useable = Integer.parseInt(values[5].replaceAll("\\s+", "").replace("\n", "").replace("\r", ""));
TicketTable ticketTable = new TicketTable(number, customer, info, warningNote, useable, ticketTableListId,warning);
ticketTableVm.insert(ticketTable);
}
}
insert Ticket api call, before calling this I want to verify that the previous call has been successfully executed
public void insert(TicketTable ticketTable) throws IOException {
Call<TicketTable> ticketCall = dataService.createTicket(ticketTable);
//Response resp = ticketCall.execute();
ticketCall.enqueue(new Callback<TicketTable>() {
#Override
public void onResponse(Call<TicketTable> call, Response<TicketTable> response) {
repo.insert(ticketTable);
}
#Override
public void onFailure(Call<TicketTable> call, Throwable t) {
}
});

Related

How to return the result from get (okhttp3)

I get a result by using okhttp3 get method.
And Now, I want to return the result to MainActivity.
I tried using intent, but I'm failed.
Also I read this okhttp3 how to return value from async GET call. But I confused about where I have to write that code.
public interface GetLastIdCallback {
void lastId(String id);
}
my MainActivity:
getMaskInfo info = new getMaskInfo(this);
info.requestGet(latitude, longitude);
getMaskInfo Activity (I want to return JSONObject or JSONArray):
package com.example.buymaskapp;
public class getMaskInfo {
OkHttpClient client = new OkHttpClient();
public static Context mContext;
public getMaskInfo(Context context){
mContext = context;
}
public void requestGet(double lat, double lng){
String url = "https://8oi9s0nnth.apigw.ntruss.com/corona19-masks/v1/storesByGeo/json";
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
urlBuilder.addEncodedQueryParameter("lat", Double.toString(lat));
urlBuilder.addEncodedQueryParameter("lng", Double.toString(lng));
urlBuilder.addEncodedQueryParameter("m", "1000");
String requestUrl = urlBuilder.build().toString();
Request request = new Request.Builder().url(requestUrl).build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.d("error", "Connect Server Error is " + e.toString());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
try{
JSONObject jsonObject = new JSONObject(response.body().string());
JSONArray totalStore = jsonObject.getJSONArray("stores");
System.out.println(jsonObject);
}catch (JSONException e){
//
}
}
});
}
}
Instead of returning void from requestGet() method, return a LiveData
public LiveData<JSONObject> requestGet(double lat, double lng) {
LiveData<JSONObject> result = MutableLiveData<JSONObject>();
/* reqeust builder & url builder code here */
client.newCall(request).enqueue(new Callback() {
/* override other methods here */
public void onResponse(Call call, Response response) throws IOException {
try{
JSONObject jsonObject = new JSONObject(response.body().string());
((MutableLiveData) result).postValue(jsonObject);
}catch (JSONException e){
/* catch and do something */
}
}
});
return result;
}
Observe the livedata in mainactivity
info.requestGet(latitude, longitude).observe(getViewLifeCycleOwner, new Observer() {
#Override
public void onCanged(JSONObject result) {
/* code to use result */
}
});
Otherwise, you can also implement interface on mainactivity and use its instance in getMaskInfo or in requestGet method to send back data.
Create a callback in MainActivity:
public void onResult(JSONArray stores)
or whatever you want to return from the call. Since you now know that your mContext is actually MainActivity, you can make a cast and call that method
((MainActivity)mContext).onResult(totalStore).
If you need to use getMaskInfo with other activities as well, you can put method onResult into an interface, make MainActivity implement that interface and pass the interface as an argument to getMaskInfo.
Interface class
public interface GetLastIdCallback {
void lastId(String id);
void getJSONCallback(JSONObject object);
}
Update the onResponse function
#Override
public void onResponse(Call call, Response response) throws IOException {
try{
JSONObject jsonObject = new JSONObject(response.body().string());
JSONArray totalStore = jsonObject.getJSONArray("stores");
System.out.println(jsonObject);
((GetLastIdCallback )(mContext)).getJSONCallback(jsonObject); //Return here
}catch (JSONException e){
//
}
}
});
Calling activity must implement GetLastIdCallback interface
public class Main2Activity extends AppCompatActivity implements GetLastIdCallback{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
#Override
public void lastId(String id) {
}
#Override
public void getJSONCallback(JSONObject object) {
//Here you can use response according to your requirements
}
}

While fetching data and storing it into sqlite db the UI of my app freezes

Hello My app is freezes ui for some seconds while it is fetching data from network and stores it in db and then shows it in recyclerview. For fetching data from network I am using retrofit and for storing it and fetching form db Room library. Both with the help of MVVM pattern. Is there a way to remoove the UI freeze?
Here is my code:
In the Mainactivity when clicking download btn
downloadBtn.setOnClickListener(v ->
eventsViewModel.insertEvents(this));
Viewmodel class:
public void insertEvents(Context context){
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String token = preferences.getString("token", "");
final Map<String,String> queryData = new HashMap<>();
queryData.put("token", token);
Call<EventsResponse> call = RetrofitClient.getmInstance().getApi().getEvents(queryData);
call.enqueue(new Callback<EventsResponse>() {
#Override
public void onResponse(Call<EventsResponse> call, Response<EventsResponse> response) {
if (response.code() == 401){
String email = preferences.getString("email", "");
String password = preferences.getString("password", "");
Call<LoginResponse> call1 = RetrofitClient.getmInstance().getApi().loginuser(email, password);
call1.enqueue(new Callback<LoginResponse>() {
#Override
public void onResponse(Call<LoginResponse> call, Response<LoginResponse> response) {
if (response.code() == 200){
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); // 0 - for private mode
SharedPreferences.Editor editor = pref.edit();
editor.putString("token", response.body().getToken());
editor.apply();
insertEvents(context);
}
else {
}
}
#Override
public void onFailure(Call<LoginResponse> call, Throwable t) {
}
});
}
if (response.code() == 200){
eventList = response.body().getData();
EventsTable eventsTable = new EventsTable();
TicketDatesTable ticketDatesTable = new TicketDatesTable();
for (int i = 0; i < eventList.size(); i++) {
eventsTable.setEvent_id(eventList.get(i).getId());
eventsTable.setTitle_tk(eventList.get(i).getTitle_tk());
eventsTable.setTitle_ru(eventList.get(i).getTitle_ru());
eventsTable.setImageURL("https://bilettm.com/" + eventList.get(i).getImage_url());
eventsTable.setStart_date(eventList.get(i).getStart_date());
eventsTable.setEnd_date(eventList.get(i).getEnd_date());
eventsTable.setSales_volume(eventList.get(i).getEnd_date());
eventsTable.setOrganiser_fees_volume(eventList.get(i).getOrganiser_fees_volume());
eventsTable.setViews(eventList.get(i).getViews());
eventsTable.setSales_volume(eventList.get(i).getSales_volume());
eventsTable.setIs_live(eventList.get(i).getIs_live());
if (!eventList.get(i).getTicket_dates().isEmpty()) {
showTimeList = eventList.get(i).getTicket_dates();
int b = 0;
while (b < showTimeList.size()) {
ticketDatesTable.setEvent_id(showTimeList.get(b).getEvent_id());
ticketDatesTable.setTicket_date(showTimeList.get(b).getTicket_date());
insertTicketDates(ticketDatesTable);
try {
Thread.sleep(150);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
b++;
}
}
insert(eventsTable);
try {
Thread.sleep(150);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
#Override
public void onFailure(Call<EventsResponse> call, Throwable t) {
}
});
}
public void insert(EventsTable data){
repository.insertEvents(data);
}
public void insertTicketDates(TicketDatesTable ticketDatesTable){
repository.insertTicketDates(ticketDatesTable);
Here is my repository :
public void insertEvents(EventsTable data){
new EventInsertion(eventsDAO).execute(data);
}
private static class EventInsertion extends AsyncTask<EventsTable, Void, Void> {
private EventsDAO eventsDAO;
private EventInsertion(EventsDAO eventsDAO) {
this.eventsDAO = eventsDAO;
}
#Override
protected Void doInBackground(EventsTable... eventsTables) {
eventsDAO.insertEvents(eventsTables[0]);
return null;
}
}
public void insertTicketDates(TicketDatesTable data){
new TicketDatesInsertion(eventsDAO).execute(data);
}
private static class TicketDatesInsertion extends AsyncTask<TicketDatesTable, Void, Void> {
private EventsDAO eventsDAO;
private TicketDatesInsertion(EventsDAO eventsDAO) {
this.eventsDAO = eventsDAO;
}
#Override
protected Void doInBackground(TicketDatesTable... ticketDatesTables) {
eventsDAO.insertTicketDates(ticketDatesTables[0]);
return null;
}
}
Here is my DAO:
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertEvents(EventsTable data);
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertTicketDates(TicketDatesTable datesTable);
I think it freezes when it is storing it into sqlite db
I found my problem. It was initializing entity before starting for loop:
BEFORE:
EventsTable eventsTable = new EventsTable();
for (int i = 0; i < eventList.size(); i++) {
INSERT();
}
AFTER:
for (int i = 0; i < eventList.size(); i++) {
EventsTable eventsTable = new EventsTable();
INSERT();
}
A better solution would be to collect all your required objects in an ArrayList and then pass it on to the AsyncTask and from there to DAO for bulk insertion.
And remove all Thread.sleep(150) statements as they serve no purpose.
why you are using this Thread.sleep(150);Call is already a background task in retrofit

Android Retrofit get response before cursor next loop

#OnClick(R.id.btnCheckStockId) void callCheck() {
MyDatabaseHelper dbHelper = new MyDatabaseHelper(ScanBarcodeActivity.this);
final SQLiteDatabase sql = dbHelper.getWritableDatabase();
String query = "SELECT barcode FROM stockopname where lokasi_item = ?";
cursor = sql.rawQuery(query, new String[]{lokasiItem});
try {
while (cursor.moveToNext()) {
final String stockId = cursor.getString(cursor.getColumnIndex("barcode"));
//API RETROFIT
ApiService api_check = ApiClient.getClient().create(ApiService.class);
Call<String> call = api_check.check_barcode(stockId);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
//get result
String sync_result = response.body();
String set_sync = "";
if (sync_result == "true") {
set_sync = "true";
} else {
set_sync = "false";
}
//update data on sqlite
ContentValues updatecolumn = new ContentValues();
updatecolumn.put("is_sync", set_sync);
sql.update("stockopname", updatecolumn, "barcode = ?", new String[]{stockId});
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
So the logic is, get a list from stock_id from the local database then check the server with API, if true the stock_id already exists on the server, if false the stock_id data does not yet exist. and the results of the responses need to be updated back to the local database with the column is_sync = true/false
when debugging, the pointer does not enter into onResponse but enters the next loop
I want to ask and need advice on how to get a response from the server immediately before the next loop in the cursor (using sqlite database), is there a solution?
Update, My Solution after reconstruct the code :
#OnClick(R.id.btnCheckStockId) void callCheck() {
MyDatabaseHelper dbHelper = new MyDatabaseHelper(ScanBarcodeActivity.this);
SQLiteDatabase sql = dbHelper.getWritableDatabase();
String query = "SELECT barcode FROM stockopname where lokasi_item = ?";
cursor_checksync = sql.rawQuery(query, new String[]{lokasiItem});
if (cursor_checksync != null) {
//more to the first row
cursor_checksync.moveToFirst();
//iterate over rows
for (int i = 0; i < cursor_checksync.getCount(); i++) {
callCheckSync(cursor_checksync);
cursor_checksync.moveToNext();
}
//close the cursor
cursor.close();
}
}
public void callCheckSync(Cursor csr_checksync)
{
final String stockId = csr_checksync.getString(csr_checksync.getColumnIndex("barcode"));
//API RETROFIT
ApiService api_check = ApiClient.getClient().create(ApiService.class);
Call<String> call = api_check.check_barcode(stockId);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
//get result
if(response.isSuccessful())
{
String sync_result = response.body();
String set_sync = "";
if (sync_result == "true") {
set_sync = "true";
} else {
set_sync = "false";
}
//update data on sqlite
update(stockId, set_sync);
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
public void update(String Barcode, String value)
{
MyDatabaseHelper dbHelper = new MyDatabaseHelper(ScanBarcodeActivity.this);
SQLiteDatabase sql = dbHelper.getWritableDatabase();
ContentValues updatecolumn = new ContentValues();
updatecolumn.put("is_sync", value);
sql.update("stockopname", updatecolumn, "barcode = ?", new String[]{Barcode});
}
I don't know if this is the best approach, but it solves my problem.
Call this method on onClick after defining initial cursor. Sorry for syntax I dont have IDE.
public void callMethod(Cursor cursor){
if (cursor.moveToNext()) {
//Retrofit
onResponse: () -> {
callMethod(cursor);
}
}else {
cursor.close();
}
}

How to retry the request every hour for 5 attempts in rxjava and retrofit

I am trying to figure out how i can resubscribe the same observable for every hour when we have got an error from server for 5 attempts.I know about retryWhen but really not able to understand how i can use it in my case.I am using retrofit for server calls and rxjava to subscribe.
Here is the method where i am making a call using retrofit.Please help with this.
#Override
public Observable<Integer> uploadFileToServer(FileUploadData fileUploadData, File file) {
// log.i(TAG, "uploadFileToServer");
FileUploadEndpoint fileUploadEndpoint = null;
try {
fileUploadEndpoint = retrofitServiceFactory.getService(FileUploadEndpoint.class);
} catch (BaseUrlNotFoundException e) {
e.printStackTrace();
// log.i(TAG, "uploadFileToServer" + e.getMessage());
return Observable.just(FileUploadConstants.EXCEPTION_FILE_UPLOAD);
}
// create RequestBody instance from file
RequestBody requestFile =
RequestBody.create(okhttp3.MultipartBody.FORM, file);
// MultipartBody.Part is used to send also the actual file name
MultipartBody.Part body =
MultipartBody.Part.createFormData("uploadfile", file.getName(), requestFile);
// add another part within the multipart request
String descriptionString = "file upload";
RequestBody description =
RequestBody.create(
okhttp3.MultipartBody.FORM, descriptionString);
Map<String, String> queryMap = new HashMap<>();
queryMap.put("SENDER", fileUploadData.getSender());
queryMap.put("SOURCE", fileUploadData.getSource());
queryMap.put("SCHEMEID", fileUploadData.getSchemeId());
queryMap.put("ISPROCESSINGREQ", "false");
queryMap.put("ISENCRYPTED", "true");
queryMap.put("UID", fileUploadData.getSchemeId());
queryMap.put("METADATA", fileUploadData.getMetaData());
final Observable<FileUploadResponse> requestObservable = fileUploadEndpoint.upload(queryMap, description, body);
return requestObservable.map(new Function<FileUploadResponse, Integer>() {
#Override
public Integer apply(FileUploadResponse fileUploadResponse) throws Exception {
if (fileUploadResponse != null) {
int code = fileUploadResponse.getStatusCode();
switch (code) {
case 100:
return FileUploadConstants.FILE_UPLOAD_SUCCESSFUL;
}
}
return FileUploadConstants.EXCEPTION_FILE_UPLOAD;
}
}).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
#Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
return throwableObservable.zipWith(Observable.range(1, 5), new BiFunction<Throwable, Integer, FileUploadResponse>() {
#Override
public FileUploadResponse apply(Throwable throwable, Integer integer) throws Exception {
return null;//not able to write the logic :(
}
});
}
});
}
#Override
public void setBaseUrl(String baseUrl) {
retrofitServiceFactory.setBaseUrl(baseUrl);
}
private interface FileUploadEndpoint {
#Multipart
#POST("da/appupload/file")
Observable<FileUploadResponse> upload(#QueryMap Map<String, String> additionValues,
#Part("description") RequestBody description,
#Part MultipartBody.Part file);
}
Here's a recipe for you.
public class RetryWithDelay implements
Func1<Observable<? extends Throwable>, Observable<?>> {
private static final String TAG = "RetryWithDelay";
private static final int DEFAULT_RETRY_COUNT = 5;
private static final int DEFAULT_RETRY_DELAY = 1000 * 60;
private final int maxRetries;
private final int retryDelayMillis;
private int retryCount;
public RetryWithDelay() {
this.maxRetries = DEFAULT_RETRY_COUNT;
this.retryDelayMillis = DEFAULT_RETRY_DELAY;
this.retryCount = 0;
}
public RetryWithDelay(final int maxRetries, final int retryDelayMillis) {
this.maxRetries = maxRetries;
this.retryDelayMillis = retryDelayMillis;
this.retryCount = 0;
}
#Override
public Observable<?> call(Observable<? extends Throwable> attempts) {
return attempts.flatMap(new Func1<Throwable, Observable<?>>() {
#Override
public Observable<?> call(Throwable throwable) {
if (throwable instanceof HttpException) {
LOGD(TAG, "Caught http exception.");
}
if (throwable instanceof IOException) {
LOGD(TAG, "Network error");
}
if (++retryCount < maxRetries) {
// When this Observable calls onNext, the original
// Observable will be retried (i.e. re-subscribed).
return Observable.timer(retryDelayMillis, TimeUnit.MILLISECONDS);
}
// Max retries hit. Just pass the error along.
return Observable.error(throwable);
}
});
}
}
Then in your code use it like this
// Leave constructor empty for default values
.retryWhen(new RetryWithDelay());
// Or setup different values
// In this case retry 3 times, with 5s delay
.retryWhen(new RetyryWithDelay(3, 5000));

Testing RX java

public void getTerms(boolean showDialog) {
service.getTermsFromServer().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new SingleSubscriber<String>() {
#Override
public void onSuccess(String value) {
try {
JSONObject jsonObject = new JSONObject(value);
JSONObject data = jsonObject.getJSONObject("data");
String content = data.getString("content");
String id = data.getString("id");
if (showDialog) {
***signUpView.showDialog(content, id)***;
} else {
agreeTerms(id);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onError(Throwable error) {
Log.e(getClass().getName(), "Error : " + new Gson().toJson(error.getStackTrace()));
ErrorCheck.processError(error, gson, signUpView);
}
});
}
Please help me in testing this code. I have attached the method which i want to test. Here I want to verify that showDialog method gets called
Attaching the Unit test code also
#Test
public void testGetTermsCalled(){
String terms= "{\"data\":{\"id\":\"67f07c7a482542\",\"content\":\"<h3>Part of the test</h3>\",\"timestamp\":1484768675815,\"timestampFormatted\":\"2017-01-18T19:44:35\"},\"metadata\":null,\"version\":{\"id\":\"v1\",\"versionStatus\":\"candidate\",\"message\":null}}";
TestSubscriber<String> testSubscriber = new TestSubscriber<>();
signUpService.getTermsFromServer().just(terms).subscribe(testSubscriber);
signUpPresenter.getTerms(true);
Mockito.verify(signUpView).showDialog("<h3>Part of the test</h3>","67f07c71-1707-4b7a-a168-d7d05a482542");
}
Thanks!!!
Use RxJavaPlugins.setInitIoSchedulerHandler and RxAndroidPlugins.registerSchedulersHook to specify your own TestScheduler, then use its advanceTimeBy method to make some time pass, then verify that the expected calls happened.

Categories

Resources