I am building an SDK.
The Scenario
From the app, the developer passes the JsonObject and URL in a method
and inside the SDK.
I add those values in SQLite database and start a JobScheduler.
The Jobscheduler takes the request at 0 indexes out of the database
executes it.
When I get the response, I delete that request from the database and now the request at 1 index comes to the 0 index and again I execute the same code where 0th index request is fired.
The Problem
When I get the response from a server inside the SDK, I need to send it to the developer using a callback.
I can take the callback as an argument when I take the JSON and URL from user, but I don't know how to proceed further because I cannot store it into the database
Suppose I have 5 requests in the database and scheduler executes it one by one, I don't know how to send the response back to the developer. I can not pass context in the jobscheduler. The only way to do that is I get the corresponding context for each row (request) in the database.
What I tried
I tried using a LocalBroadcastManager, but I can not create a generic class and get it's onreceive() method implemented, also the context passing was a problem
Tried using Realm as a database so that I can add Context type and use my model, but it is not working as Context is not supported.
Storing Activity name in the database with other details and then driving out the class from it and activity from class. And then typecasting the activity into my callback. but it throws ClassCastException as the class it tries to derive from the Name is on the developer's app and not in my SDK
..
The App's MainActivity Code
sdkClass.queueRequest(jo.toString(),"url1",12,"MainActivity");
sdkClass.queueRequest(jo2.toString(),"url2",13,"MainActivity");
sdkClass.queueRequest(jo.toString(),"url3",14,"MainActivity");
sdkClass.executeQueue();
The SDK class which adds and executes code
public void queueRequest(String payload, String URL, int requestId, String identifier){
RequestsDatabase database = new RequestsDatabase(context);
database.insertItem(TxnType,payload,url, String.valueOf(requestId), identifier);
}
public JobScheduler getQueueInstance(Context context){
if(jobScheduler==null){
jobScheduler = (JobScheduler)context.getSystemService(JOB_SCHEDULER_SERVICE);
}
return jobScheduler;
}
public void executeQueue(){
getQueueInstance(context);
ComponentName jobService = new ComponentName(context.getPackageName(), MyJobService.class.getName());
JobInfo jobInfo = new JobInfo.Builder(1,jobService).setPersisted(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY). build();
jobScheduler.schedule(jobInfo);
}
MyJobServiceCode
#Override
public boolean onStartJob(JobParameters params) {
// UtilityMethods.showToast(this,params.getExtras().getString("json"));
Toast.makeText(this,"test", Toast.LENGTH_SHORT).show();
database = RequestsDatabase.getInstance(this);
ALlRequests = database.getAllItemId();
executeCall();
return false;
}
public void executeCall(){
ALlRequests = database.getAllItemId();
if(ALlRequests.size()>0) {
requestModel = ALlRequests.get(0);
try {
String payload = getPayload(this, requestModel.getTxnType(), requestModel.getPayload());
SilverLineRequest req = new SilverLineRequest(this, requestModel.getUrl(), payload, Integer.parseInt(requestModel.getRequestId()), this);
req.executeToServer();
Toast.makeText(getApplicationContext(), "requested", Toast.LENGTH_LONG).show();
} catch (JSONException e) {
e.printStackTrace();
}
}
else{
Toast.makeText(this, "Queue empty", Toast.LENGTH_LONG).show();
}
}
#Override
public void onSuccess(String response, int requestId) {
if(requestId ==Integer.parseInt(requestModel.getRequestId())){
Toast.makeText(this,"responded", Toast.LENGTH_SHORT).show();
database.deleteItem(requestModel.getTxnType());
// HERE I WANT TO SEND THE USER RESPONSE LIKE
// listener.onTransactionSuccess(response)
// BUT I DON'T HAVE 'listener' SO IT SHOWS NULLPOINTER
SDKClass sl = new SDKClass(this);
sl.executeQueue();
}
}
Any help will be blessed right now.
You store requests in database, so it can has unique id (autoincrement).
Then you can store callbacks in memory with relation request id -> callback.
On response you can call it.
If you want, return id to developers. Then they can bind and unbind and get results when they need, even the response had got yesterday.
Take a look on TransferUtility from Amazon AWS SDK for example.
Related
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.
I am attempting to create a real-time communication capability for a Phonegap/Cordova app. I am using SignalR 2 to handle the communication.
The thing I am struggling with is getting a message to a particular user. Every single example out there shows saving Context.User.Identity.Name, which is useless to me because the remote site's User.Identity context is not shared by my phonegap app.
In essence, I am not authenticating a user in the traditional sense, so I need another way of linking the SignalR connectionID with the username I pass along.
Taken from the official ASP.NET signalr Examples, I have the following code which overrides the OnConnected event. Unfortunately it takes no parameters and expects User.Identity to be not null:
public override Task OnConnected()
{
using (var db = new UserContext())
{
// Retrieve user.
var user = db.Users
.Include(u => u.Rooms)
.SingleOrDefault(u => u.UserName == Context.User.Identity.Name);
// If user does not exist in database, must add.
if (user == null)
{
user = new User()
{
UserName = Context.User.Identity.Name
};
db.Users.Add(user);
db.SaveChanges();
}
else
{
// Add to each assigned group.
foreach (var item in user.Rooms)
{
Groups.Add(Context.ConnectionId, item.RoomName);
}
}
}
return base.OnConnected();
}
Now, maybe what I'd need is to have a version of this method that takes a string as a parameter and then I'd use that as my user identifier.
But how to go about that?
You need to create a new IUserIdProvider for the user and use dependency injection to register your provider and use it.
public interface IUserIdProvider
{
string GetUserId(IRequest request);
}
Register your provider with Global Host
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => new MyIdProvider());
Usage:
public class MyHub : Hub
{
public void Send(string userId, string message)
{
Clients.User(userId).send(message);
}
}
Taken from: http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections#IUserIdProvider
Please assist in this. I can't seem to create a suitable test for this method:
protected void startInterfacing() {
mLiveAuthClient.login(mView.context(), Arrays.asList(SCOPES), new LiveAuthListener() {
#Override
public void onAuthComplete(final LiveStatus liveStatus, final LiveConnectSession liveConnectSession,
Object o) {
// Login successful and user consented, now retrieve user ID and connect with backend server
getUserIdAndConnectWithBackendServer(liveConnectSession, mLiveAuthClient);
}
#Override
public void onAuthError(LiveAuthException e, Object o) {
// We failed to authenticate with auth service... show error
if (e.getError().equals("access_denied") ||
e.getMessage().equals("The user cancelled the login operation.")) {
// When user cancels in either the login or consent page, we need to log the user out to enable
// the login screen again when trying to connect later on
logUserOut(mLiveAuthClient, false);
} else {
onErrorOccured();
}
}
});
}
I'll explain abit what goes on here:
I'm trying to authenticate my client and log into OneDrive.
The method starts with a call to the Live SDK's login method. That SDK object is given to me from outside this class. So I can basically mock it.
Here's what I'm struggling with:
I do not need to test the call to the login method because it is not mine. I do need to test the call to getUserIdAndConnectWithBackendServer() inside onAuthComplete. But this method requires a liveConnectSession object. How do I provide that? It is given to me on the onAuthComplete method.
How do I mock the calls to onAuthComplete and onAuthError? I read about ArgumentCaptor but when I use that, I need to provide the arguments to those methods when I call the actual method.
For instance, argument.getValue().onAuthComplete() requires me to add arguments to this call. What do I actually provide here?
Here is the next method which is roughly the same but has its own issues:
protected void getUserIdAndConnectWithBackendServer(final LiveConnectSession liveConnectSession, final LiveAuthClient
authClient) {
final LiveConnectClient connectClient = new LiveConnectClient(liveConnectSession);
connectClient.getAsync("me", new LiveOperationListener() {
#Override
public void onComplete(LiveOperation liveOperation) {
// We got a result. Check for errors...
JSONObject result = liveOperation.getResult();
if (result.has(ERROR)) {
JSONObject error = result.optJSONObject(ERROR);
String code = error.optString(CODE);
String message = error.optString(MESSAGE);
onErrorOccured();
} else {
connectWithBackend(result, liveConnectSession, authClient);
}
}
#Override
public void onError(LiveOperationException e, LiveOperation liveOperation) {
// We failed to retrieve user information.... show error
onErrorOccured();
logUserOut(authClient, false);
}
});
}
In here I would like to mock the JSONObject for instance. But how do I call the onComplete method, or the onError method. And what would I provide as the arguments the methods provide me with. LiveOperation for instance?
Thank you!!
The solution I eventually used was to use mockito's doAnswer() structure.
This enabled me to get the callback argument and call one of its methods.
Another solution was to use an ArgumentCator.
I'm developing an Android Mobile Application and one of the most important functionality of the app itself is being able to talk with a third-party API Service.
The third party service, offering these API, wants a "beacon" to be included into every API request i made.
The "beacon" is a "long integer" and it must be unique and incremental for every request.
The problem is:
I'm firing a couple of these request and i do not know which of these requests will complete first so i'm running into a race condition: where the second request ends quickly before the first request invalidating the first request!
When a button is clicked the following action will be executed:
public void fireRequests(View view)
{
long first_beacon = System.nanoTime();
fireFirstRequest(view, first_beacon);
long second_beacon = System.nanoTime();
fireSecondRequest(view, second_beacon);
}
I'm using Volley in a proper way, setting up callback etc.. example here:
fireFirstRequest method:
public void fireFirstRequest(View view, long beacon)
{
final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());
api_lib.doOperationA(beacon, new ThirdPartyLib.MyOwnCallback()
{
#Override
public void update(JSONObject jsonObject)
{
try
{
JSONObject result = jsonObject.getJSONObject("response");
/* my code */
Log.d("doOperationA", result)
}
catch (JSONException e)
{
e.printStackTrace();
}
}
});
}
fireSecondRequest method:
public void fireSecondRequest(View view, long beacon)
{
final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());
api_lib.doOperationB(beacon, new ThirdPartyLib.MyOwnCallback()
{
#Override
public void update(JSONObject jsonObject)
{
try
{
JSONObject result = jsonObject.getJSONObject("response");
/* my code */
Log.d("doOperationB", result)
}
catch (JSONException e)
{
e.printStackTrace();
}
}
});
}
Here is the execution log:
03-12 14:26:56.252 18769-18769/it.example.app D/Volley: queued doOperationA
03-12 14:26:58.124 18769-18769/it.example.app D/Volley: queued doOperationB
03-12 14:26:59.433 18769-18769/it.example.app D/App: doOperationB: {
"error": false,
"payload": {
"foo": "bar"
}
}
03-12 14:27:04.181 18769-18769/it.example.app D/App: doOperationA: {
"error": true,
"errorMessage": "invalid beacon"
"payload": {}
}
The question is: what's the best way to keep track of beacon before firing an API request or to maintain a "execution order" separation even if we are talking of ASync request?
My rough solution is to call the fireSecondRequest() inside the callback of the fireFirstRequest() when i'm completely sure that first request is done.
I know, this is the best way to kill the awesome world of async requests.
modified action:
public void fireRequests(View view)
{
long first_beacon = System.nanoTime();
fireFirstRequest(view, first_beacon);
}
fireFirstRequest modified method with final View parameter:
public void fireFirstRequest(final View view, long beacon)
{
final ThirdPartyLib api_lib = new ThirdPartyLib(getActivity());
api_lib.doOperationA(beacon, new ThirdPartyLib.MyOwnCallback()
{
#Override
public void update(JSONObject jsonObject)
{
try
{
JSONObject result = jsonObject.getJSONObject("response");
/* my code */
Log.d("doOperationA", result)
/* fire second request */
// EDIT
fireSecondRequest(view, System.nanoTime());
}
catch (JSONException e)
{
e.printStackTrace();
}
}
});
}
You didn't add the part of your code which initiates the Volley RequestQueue, but I'm assuming you're creating the default way using:
RequestQueue requestQueue = Volley.newRequestQueue(context, stack);
When you do this, you get a request queue which allows for 4 concurrent requests by default. You can see this by looking at the constructor this method uses to create a request queue:
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
...
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
You can overcome this issue if instead of using the default method for creating a RequestQueue, you create your own RequestQueue with a thread pool size of 1. This way, there can be no 2 concurrent requests, and requests will be sent in the order they are dispatched.
The downside with this, of course, is that this can dramatically slow down your app. If all requests must wait until the previous request is finished, this creates a serious bottleneck in your app.
Perhaps consider using more than 1 request queue, and only use this special request queue for requests that rely on this special constraint.
Hope this helps.
In my android app, after sometime (hour or so.. not something determined) the connection and response to Google-AppEngine takes very long, something like 10 seconds or more.
After the first connection all other enpoint requests are done pretty quickly and this is why I believe this is SW issue and not internet connection issue.
Should I establish a 'dummy' connection as the app is loaded ?
Here is a sample code of an AsyncTask which tried to get User entity from AppEngine endpoint :
private class getUser extends AsyncTask<Void, Void, Boolean> {
long mTaskUserId = Constants.USER_ID_NO_ID_INFDICATOR;
String mIdInPlatform = Constants.USER_ID_NO_ID_INFDICATOR.toString();
Long mServerScore;
Context mContext;
String mUserName;
getUser(String idInPlatform, String userName, Context c) {
mIdInPlatform = idInPlatform;
mUserName = userName;
mContext = c;
}
#Override
protected Boolean doInBackground(Void... params) {
Userendpoint.Builder builder = new Userendpoint.Builder(
AndroidHttp.newCompatibleTransport(), new JacksonFactory(), null);
builder = CloudEndpointUtils.updateBuilder(builder);
Userendpoint endpoint = builder.build();
try {
User user = endpoint.getUser().execute();
} catch (IOException e) {
Log.e(TAG, "Error getting user details from server ", e);
return false;
}
this.mUserName = user.getUserName();
this.mServerScore = user.getScore();
this.mTaskUserId = user.getId();
return true;
}
#Override
protected void onPostExecute(Boolean result) {
if (result) {
setUserFacebookIdInPreferences(mIdInPlatform, mContext);
setUserIdInPreferences(this.mTaskUserId, mContext);
setScoreInPreferences(this.mServerScore, mContext);
setUserNameInPreferences(this.mUserName, mContext);
} else {
Toast.makeText(mContext, R.string.string_login_failed, Toast.LENGTH_SHORT).show();
}
// Restart login activity.
moveToLoginActivity(result);
super.onPostExecute(result);
}
}
Your application in Google App Engine uses two types of server instances: Dynamic instances and Resident instances. The difference is that dynamic instances are created in demand to serve traffic requests. Resident instances are always on.
When traffic stops, all your dynamic instances will shut down to save resources (and help you save money). The first time a request hits the server, a new dynamic instance will spin off to serve that request. The process of starting a new instance might take some time.
This is very likely what you are seeing in your application. To avoid that initial latency you can do two different things:
1) Optimize the time it takes for your code to load up.
2) Set up a Resident instance.
You can find more information on the Google documentation here:
https://developers.google.com/appengine/docs/adminconsole/instances#Introduction_to_Instances
You can warm-up your instances so that they're live before any query hits them - saving you this 10s delay. See documentation at:
https://developers.google.com/appengine/docs/adminconsole/instances#Warmup_Requests