Android Azure Offline Sync - Not completing sync - android

I have an android app with Azure Mobile Services and implemented Offline Sync. The app works well but when syncing data it seems not to complete so there is always a few rows on tables which have not synced?
Anyone have any ideas what the problem might be. I believe that on the next try it would finish where it left off or am I wrong?
Thanks in advance

The app works well but when syncing data it seems not to complete so there is always a few rows on tables which have not synced?
I would recommend you use fiddler to capture the network traces when handling the sync operations.
For Incremental Sync, the request would be as follows:
Get https://{your-app-name}.azurewebsites.net/tables/TodoItem?$filter=(updatedAt%20ge%20datetimeoffset'2017-11-03T06%3A56%3A44.4590000%2B00%3A00')&$orderby=updatedAt&$skip=0&$top=50&__includeDeleted=true
For opting out of incremental sync, you would retrieve all records without the filter updatedAt.
Get https://{your-app-name}.azurewebsites.net/tables/TodoItem?$skip=0&$top=50&__includeDeleted=true
Note: If there are too many items, the SDK would send multiple requests to pull all items that match your given query from the associated remote table. Also, you need to make sure you specify the includeDeleted() in your query.
In summary, you need to make sure that all items could be retrieved via the above requests. Additionally, if the pull operation has pending local updates, then the pull operation would first execute a push operation. So, I assume that you could catch the exception when calling pull operation for handling the conflict resolution.

Bruce's answer is fine but I used a slightly different method without the need to use fiddler.
I change my connection from this
mClient = new MobileServiceClient("[AZUREWEBSITE]", cntxall);
mClient.setAndroidHttpClientFactory(new MyOkHttpClientFactory());
To this
mClient = new MobileServiceClient("[AZUREWEBSITE]", cntxall).withFilter(
new ServiceFilter() {
#Override
public ListenableFuture<ServiceFilterResponse> handleRequest(ServiceFilterRequest request, NextServiceFilterCallback nextServiceFilter) {
// Get the request contents
String url = request.getUrl();
String content = request.getContent();
if (url != null) {
Log.d("Request URL:", url);
}
if (content != null) {
Log.d("Request Content:", content);
}
// Execute the next service filter in the chain
ListenableFuture<ServiceFilterResponse> responseFuture = nextServiceFilter.onNext(request);
Futures.addCallback(responseFuture, new FutureCallback<ServiceFilterResponse>() {
#Override
public void onFailure(Throwable e) {
Log.d("Exception:", e.getMessage());
}
#Override
public void onSuccess(ServiceFilterResponse response) {
if (response != null && response.getContent() != null) {
Log.d("Response Content:", response.getContent());
}
}
});
return responseFuture;
}
}
);
This is the logging method for Azure connections and shows the request in the log.

Related

How do I cache content and data from a json API using Rx2AndroidNetworking?

I am retrieving my response from my API with rxjava with funcs as follows:
#Override
public Single<MyInfoResponse> getMyInfoApiCall() {
return Rx2AndroidNetworking.get(ApiEndPoint.ENDPOINT_MY_INFO)
.addHeaders(mApiHeader.getProtectedApiHeader())
.build()
.getObjectSingle(MyInfoResponse.class);
}
Now ,I am retrieving this data and using it in my UI code( with usual compositedisposables) as follows:
#Override
public void onViewPrepared() {
getCompositeDisposable().add(getDataManager()
.getMyInfoApiCall()
.subscribeOn(getSchedulerProvider().io())
.observeOn(getSchedulerProvider().ui())
.subscribe(myInfoResponse -> {
getDataManager().updateMyManagerInfo(myInfoResponse);
if (myInfoResponse != null && myInfoResponse.getData() != null) {
getMvpView().updateMyRepo(myInfoResponse.getData());
}
}, throwable -> {
if (!isViewAttached()) {
return;
}
getMvpView().hideLoading();
// handle the error here
if (throwable instanceof ANError) {
ANError anError = (ANError) throwable;
handleApiError(anError);
}
}));
}
Now everytime I have internet connectivity and it tries to retrieve data it works fine, but as soon as I lose internet connectivity, I am trying to cache this data so it still displays on the UI until the network connectivity is back and ready to update data real time. How do I go about this in the easiest and the most ideal way possible?
I suggest you to use offline support using android Room Persistence.
Have a look at the google sample from the below link
https://developer.android.com/topic/libraries/architecture/room
In this case
Prefetch a request (so that it can return from cache when required at instant)
AndroidNetworking.get("https://fierce-cove-29863.herokuapp.com/getAllUsers/{pageNumber}")
.addPathParameter("pageNumber", "0")
.addQueryParameter("limit", "30")
.setTag(this)
.setPriority(Priority.LOW)
.build()
.prefetch();
First of all the server must send cache-control in header so that is
starts working.
Response will be cached on the basis of cache-control
max-age,max-stale.
If internet is connected and the age is NOT expired it will return
from cache.
If internet is connected and the age is expired and if server returns
304(NOT MODIFIED) it will return from cache.
If internet is NOT connected if you are using
getResponseOnlyIfCached() - it will return from cache even it date is
expired.
If internet is NOT connected , if you are NOT using
getResponseOnlyIfCached() - it will NOT return anything.
If you are using getResponseOnlyFromNetwork() , it will only return
response after validation from server.
If cache-control is set, it will work according to the
max-age,max-stale returned from server.
If internet is NOT connected only way to get cache Response is by
using getResponseOnlyIfCached().
I just went through the docs:
Refer here

Particular overload of Azure Mobile Service invokeApi is not working while calling custom API

Where is the documentation/sample for all overloads of invokeApi function for Azure Mobile Service client SDK for Android?
I found this article and tried following code, which does not work. There are no compile time or run time errors, invokeApi gets called, but it does not come back to onSuccess or onFailure. If I call invokeApi without order object, everything works as expected
PizzaOrder order = new PizzaOrder();
order.Size = "Large";
order.Flavor = "Four cheeses";
order.UserPhone = "555-555-1234";
ListenableFuture<PizzaOrderResponse> testresult = mClient.invokeApi("bookservice", order, PizzaOrderResponse.class);
Futures.addCallback(testresult, new FutureCallback<PizzaOrderResponse>() {
#Override
public void onFailure(Throwable exc) {
// failure handling code here
}
#Override
public void onSuccess(PizzaOrderResponse testresult) {
// success handling code here
}
});
One of the properties in the data object being returned by the custom API had incorrect data type. I am still not sure where the good documentation is and why custom API call did not fail but at least it is working now.

Unexpected HTTP 400 status code from NanoHTTPD on Android

Friends!
I'm getting occasional and unexpected HTTP 400 responses from nanohttpd in my Android app. The error is following a specific pattern. I've been looking at this for some time now but I've come to the point where I need a different angle or some other help pointing me in the right direction.
Could you please have a look and share your thoughts or even direct points and suggestions?
Why am I getting this HTTP 400 status code?
And why only under the given circumstances? (I don't want it at all!)
Some Background
I'm running nanohttpd in my Android project as a temporary isolation layer (due to server side not being mature enough yet). I have isolated the nanohttpd server in an Android Service, which I start from my custom Application object once it's created. This way nanohttpd is not bound to the lifecycle of any particular Activity but can live rather independent of the overall application logic and component life cycles.
The Problem
Now, (almost) everything is working nice and dandy: I can start nanohttpd and perform some initial login requests, my expected mock response is even delivered. When I perform my first "GET" request, though, nanohttpd throws a 400 Bad request status at me, but only the first time. If I back out of the Activity being responsible for the particular "GET" request, and launch it again (from the home screen), it delivers the expected payload with a 200 status, flawlessly.
What Have I Done So Far
I have had a closer look at the nanohttpd source code, trying to track down where and why this 400 status is set. It's not that many places this status code is used. Roughly speaking only here, here and here. Since I'm not dealing with multipart content, I'm left with the first and third "here". But - of course - I can not for my life find neither the root cause of the 400 status, nor which exact block is causing the state for me. When I debug the code, everything works just peachy.
Some Code
This is roughly what my nanohttpd Service (MyNanoHttpdService) looks like:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_START.equals(intent.getAction())) {
String errorMessage = null;
if (myNanoHttpd == null) {
String hostUrl = intent.getStringExtra(EXTRA_HOST);
Uri uri = Utils.notEmpty(hostUrl) ? Uri.parse(hostUrl) : Uri.EMPTY;
myNanoHttpd = new MyNanoHttpd(this, uri.getHost(), uri.getPort(), null);
}
if (!myNanoHttpd.isAlive()) {
try {
myNanoHttpd.start();
} catch (IOException e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
errorMessage = stringWriter.toString();
stopSelf();
}
}
final ResultReceiver resultReceiver = intent.getParcelableExtra(EXTRA_RESULT_LISTENER);
if (resultReceiver != null) {
int status = myNanoHttpd.isAlive() ? CODE_SUCCESS : CODE_FAILURE;
Bundle bundle = new Bundle();
bundle.putString(EXTRA_MESSAGE, errorMessage);
resultReceiver.send(status, bundle);
}
}
return Service.START_STICKY;
}
And this is how I start the service from my custom Application object, initialize my client side state and fetch some content:
#Override
public void onCreate() {
super.onCreate();
// Yes, that is a Java 8 Lambda you see there!
MyNanoHttpdService
.start(this, "http://localhost:8080")
.withStartupListener((status, message) -> {
if (status == 0) {
// POST REQUEST: Works like a charm
myNetworkHelper.login();
// GET REQUEST: Always fails on first launch
myNetworkHelper.getContent();
} else {
Log.e("LOG_TAG", "Couldn't start MyNanoHttpd: " + message);
}
});
}
It's safe to assume that the wrapping convenience code (the .withStartupListener(...) - which essentially wraps a ResultReceiver used by the above Service - and the myNetworkHelper object) works as expected. Also, in production, the getContent() call would be made from an Activity or Fragment, but for the sake ease I have moved it to the Application for now.
I may have found the root cause for my issue, and possibly even a workaround for the moment.
If I'm correct in my investigation, the issue was caused by unconsumed data from a previous (POST) request, contaminating the current (POST) request.
This line in the NanoHTTPD code base (the header parsing block in the NanoHTTPD.HTTPSession.execute() method, just before calling through to any custom serve(...) method - the third "here" in my question above) was the very line where the HTTP 400 status code was thrown, and just as the code suggests, there was no proper value for the "method" header.
The value - which I expected to be "POST" in clear text - was contaminated with parts of the JSON content body from the previous request. As soon as I realized this, I tried to consume the entire request body in my custom MyNanoHttpd.serve(IHTTPSession session) method, like so:
#Override
public Response serve(IHTTPSesion session) {
InputStream inputStream = session.getInputStream();
inputStream.skip(inputStream.available());
// or
// inputStream.skip(Long.MAX_VALUE);
// or even
// inputStream.close();
...
}
This didn't work, though, as I kept getting various exceptions. I ended up gently modifying the NanoHTTPD code, safely closing the input stream in the finally block of the very NanoHTTPD.HTTPSession.execute() method instead.
I'm, nonetheless, considering reaching out to the NanoHTTPD community to discuss a suitable and sustainable solution.

Android Parse SDK issue

I'm using parse to store my data and do a lot of queries while using my program.
The issue is that after about +/-20 similar queries, parse findInBackground() or getFirstInBackground() doesn't return a callback and app stuck at that possition.
My query code:
ParseQuery<OptionCodeDTO> mQuery;
mQuery = ParseQuery.getQuery(OptionCodeDTO.class);
mQuery.whereEqualTo("code", prCode);
mQuery.getFirstInBackground(new GetCallback<OptionCodeDTO>() {
#Override
public void done(OptionCodeDTO optionCodeDTO, ParseException e) {
if (isVisible()) {
if (e == null) {
OptionCode opCode = new OptionCode(optionCodeDTO);
mCodes.push(opCode);
printCodes();
prDescrLayout.setVisibility(View.VISIBLE);
prDescProgress.setVisibility(View.GONE);
mPRLable.setVisibility(View.GONE);
} else {
if (e.getCode() == ParseException.CONNECTION_FAILED) {
mPrDescr.setText(R.string.dtc_lookup_check_network);
} else if (e.getCode() == ParseException.OBJECT_NOT_FOUND) {
mPrDescr.setText(R.string.pr_lookup_code_not_found);
} else {
mPrDescr.setText(R.string.dtc_lookup_other_problems);
}
prDescrLayout.setVisibility(View.VISIBLE);
prDescProgress.setVisibility(View.GONE);
}
}
}
});
First of all, if your app ANRs (application not responding) because of something from UI thread that relies on background threads, that is incorrect architecture.
Probably you have to optimize your app's interaction with Parse. Generally it is a bad practice to make lots of saveInBackground, for example from inside a loop. You can add objects, those need to be saved, to a list and then use ParseObject.saveAllInBackground(objectList)
Also an idea to optimize is to use local storage - android's built in SQLite. For example if your app relies on something being saved to Parse, the logic is like this:
When saving object first you save to local DB and run a saveInBackground method.
When fetching objects you first fetch from your local DB and then run a getInBackground method, which inside a callback persists the information to your local DB.
This way you will make your app usable without internet connection.

Network query returns local datastore pointer

I have recently managed to switch to Local Datastore, moving all the needed parts of the database to the client. Furthermore I send a push notification whenever data changes so that the client data stays up to date.
Now my problem is that one of the pointers in my data keeps returning the old pointer after updating.
Here is the code that I use:
public ParseQuery<CircuitUnit> circuitUnits() {
ParseQuery<CircuitUnit> query = CircuitUnit.getQuery();
query.include(CircuitUnit.circuits);
query.include(CircuitUnit.guard);
query.setLimit(1000);
return query;
}
circuitUnits().findInBackground(
new FindCallback<CircuitUnit>() {
#Override
public void done(List<CircuitUnit> objects, ParseException e) {
Log.d(TAG, "Pinning circuitUnits " + objects.size());
// debugging loop of results
for (CircuitUnit circuitUnit : objects) {
if (circuitUnit.getObjectId().equals("TXEZDch6wK")) {
Guard guard = circuitUnit.getGuard();
Log.d(TAG, "Guard: " + guard.getObjectId());
}
}
// This function updates the Local datastore with the newly fetched objects
// updateServerDataPin(objects, PinsGlobal.CIRCUIT_UNITS,
e, callback);
}
});
The Logged output is: "Guard: L440gHXKTY" where it should be SDDg23rR4h
I have added a screenshot of the CircuitUnit table showing that SDDg23rR4h is indeed the expected objectId of the pointer.
I tried to create a sample project showing the issue as part of the bug report for parse.com, but that returned the correct pointer.
My theory is then that this problem lies in having data in the Local Datastore which somehow interferes with the result. Not that I understand why, cause I am clearly querying the network and not locally.
It seems as if the include statements are ignored and simply filled with the known data from the Local Datastore.
Has anyone experienced something similar or have a possible explanation to this behaviour?

Categories

Resources