Firestore timeout for android - java

I am currently building an app that saves the user's blog post in Firestore server. Everything is working fine, but I found out that the post was not uploaded under the unstable internet connection.
I tried to set a timeout to the Firestore instance, but it seems like there's no timeout option for Firestore library. The problem is, because there's no timeout setting, the app doesn't know when to dismiss the uploading screen (Spinner dialog).
I was thinking about creating a Handler or Observable or Thread and setting the timeout manually. After the specified timeout period, let the app dismiss the uploading screen. However, Firestore client will keep retrying the upload in the background even after the timeout. So this approach won't be suitable for this case...
Is there any solution for this? If I can set the timeout for the Firestore client itself, meaning letting the client to call onFailure() after the given timeout period, I can just save the post as draft in the local storage and try it again when the device is back to stable connection.

Firestore will immediately add the document to its local cache. It will then try to synchronize that document with the server. To detect whether it will be able to do so, have a look at Gastón's answer.
To detect when the document has been written to the server, use a SuccessListener. This example from the Firestore documentation on adding documents shows how:
// Add a new document with a generated id.
Map<String, Object> data = new HashMap<>();
data.put("name", "Tokyo");
data.put("country", "Japan");
db.collection("cities")
.add(data)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
Log.d(TAG, "DocumentSnapshot written with ID: " + documentReference.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error adding document", e);
}
});

The best way of doing this is attaching a boolean that will let you know if you have internet before doing something (this is just to dismiss your spinner, as firestore has offline features like realtime database)
public static boolean hasActiveInternetConnection(Context context) {
if (isNetworkAvailable(context)) {
try {
HttpURLConnection urlc = (HttpURLConnection) (new URL("http://www.google.com").openConnection());
urlc.setRequestProperty("User-Agent", "Test");
urlc.setRequestProperty("Connection", "close");
urlc.setConnectTimeout(1500);
urlc.connect();
return (urlc.getResponseCode() == 200);
} catch (IOException e) {
Log.e(LOG_TAG, "Error checking internet connection", e);
}
} else {
Log.d(LOG_TAG, "No network available!");
}
return false;
}
so you should check first if you have an active connection , this method will ping google.com and check for internet connection, if is not reachable after 1.5 seconds it will return false
so you should do something like this (pseudocode)
if(hasActiveInternetConnection)
doyourfirebaseuploadstuff
else
spinner.dismiss()
Toast(please check your internet connection and try again)
Remember to add your internet permissions in your manifest
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
EDIT:
Another cool method and maybe more readable for someones can be this one
public boolean isInternetWorking() {
boolean success = false;
try {
URL url = new URL("https://google.com");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10000);
connection.connect();
success = connection.getResponseCode() == 200;
} catch (IOException e) {
e.printStackTrace();
}
return success;
}
it just works like the other one , instead that this will just wait for 10 seconds untill it returns the internet status

Related

GCM register blocking AsyncTask until timeout occurs

I'm trying to get my device registration ID with GCM. My code to do so is contained within an AsyncTask which is called from my main thread.
Main code
try
{
String deviceId = new Gcm().execute(this.activity).get(5, TimeUnit.SECONDS);
Log.i("Login", "User device id returned as " + deviceId);
return deviceId;
}
catch (Exception e)
{
Log.e("Login", "Exception", e);
}
GCM Class
public class Gcm extends AsyncTask<Activity,Void,String>
{
#Override
protected String doInBackground(Activity... params)
{
Log.i("GCM", "Attempting to get device id");
Activity activity = params[0];
try
{
Log.i("GCM", "Getting GCM instance");
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(activity.getApplicationContext());
Log.i("GCM", "Registering with GCM");
String regId = gcm.register(PROJECT_NUMBER);
Log.i("GCM", "Device registered, registration ID=" + regId);
return regId;
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
}
}
And here is my log dump
07-28 13:07:39.093 I/GCM﹕ Attempting to get device id
07-28 13:07:39.093 I/GCM﹕ Getting GCM instance
07-28 13:07:39.093 I/GCM﹕ Registering with GCM
07-28 13:07:44.103 E/Login﹕ Exception
java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask.get(FutureTask.java:176)
at android.os.AsyncTask.get(AsyncTask.java:507)
I/GCM﹕ Device registered, registration ID=XXXXXX
So for some reason, calling gcm.register() is blocking until my timeout exception is hit. Does anyone have any idea why that might be happening?
The reason is cause you're executing gcm with
.get(5, TimeUnit.SECONDS);
This call blocks the thread for 5 seconds, however due to different reasons like unstable network connection the registration process can take more than 5 seconds. It is not the best approach to do what you want.
Take a look at this example, taken from official GCM demo:
private void registerInBackground() {
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
// You should send the registration ID to your server over HTTP, so it
// can use GCM/HTTP or CCS to send messages to your app.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device will send
// upstream messages to a server that echo back the message using the
// 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return msg;
}
#Override
protected void onPostExecute(String msg) {
mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
Incorporate this example and everything should work.
Using an AsyncTask like that doesn't make sense. AsyncTasks are used to avoid blocking the thread, but by using the blocking get() call you are blocking the thread anyway. You may as well call register() directly in that case.
You are getting a timeout because you're blocking for 5s, but GCM is taking longer than 5s to register. This could be due to bad network conditions as it can take a while for a network request to time out. Or maybe it is just taking more than 5s to get the registration ID.

Java GET Request to PHP URL Not Sending

I am trying to get my Java program to send a GET request to the following (pseudo) PHP file:
www.domain.com/example.php
Here is the PHP script:
<?php
include "dbcredentials.php";
$query = "INSERT INTO accesscode (id) VALUES ('" . $_GET['auth'] . "')";
$result = mysql_query($query) or die(mysql_error()); // Check if query was successful
print ($_GET['auth']);
?>
The above php is fairly self explanatory. When a GET request is received, it prints the content of the request to the page and also adds it in the column "ID" in table named "accesscode".
Therefore if typing "www.domain.com/example.php?auth=Brad+Pitt", the PHP file will print Brad Pitt and add that to the table "accesscode".
Okay, simple enough. Except I want my Java program to be able to send a GET request too. So I devised the following function which is called by new phpGET().execute():
public class phpGET extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
try {
URL url = new URL("http://domain.com/example.php?auth=David+Cameron");
URLConnection connection = url.openConnection();
connection.connect();
} catch (Exception e) {
Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
}
return null;
}
}
Now just to confirm, the above does not catch an error. But for some reason, it must fail to send the GET request (http://domain.com/example.php?auth=David+Cameron) to the php file, on the basis that David Cameron is not added to the database after the function is called, as is intended.
I confirm I have the following Android manifest permissions:
<uses-permission android:name="android.permission.INTERNET" />
I'm pretty stumped on this one; any ideas?
Additional information: I am using Windows 7 x86, the latest Android Studio and testing on a Samsung Galaxy S5.
Also please note, I am aware, and it has been brought to my attention, that essentially I should be using PDO for database access, and that I should be taking additional action to prevent MySQL injection vulnerabilities that the code is riddled with. I would like to assure you that I intend on doing so upon solving the central problem discussed herein.
Okay,
So I'm not exactly too sure how good of a solution this is, but irrespective, it does exactly what I set out to achieve. I was able to successfully send a PHP GET request to the .php file using the following:
public class phpGET extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
try {
URL url = new URL("http://www.domain.com/example.php?auth=David+Cameron");
URLConnection conn = url.openConnection();
InputStream is = conn.getInputStream();
is.close();
} catch (Exception e) {
Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
}
return null;
}
}
replace
URL url = new URL("http://domain.com/example.php?auth=David+Cameron");
URLConnection connection = url.openConnection();
connection.connect();
with
URL obj = new URL("http://domain.com/example.php?auth=David+Cameron");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");

Android - Dropbox Datastore is "empty" until application restarts

When I sign in on my application and try to get the size from the datastore's table, it is empty. But after I restart the application, it works as it should and gives back the size of the datastore's table.
In my case, there are two activities. The activity, and in-turn the fragment, SettingsFragment.java has the sign-in part. The activity MainActivity.java has the part with getting the datastore's table size.
SettingsFragment.java (Sign-in).
// Dropbox Signin
mAccountManager = DbxAccountManager.getInstance(getActivity().getApplicationContext(), APP_KEY, SECRET_KEY);
mDropBoxPreference = findPreference("preference_sync_dbx");
mDropBoxPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (mAccountManager.hasLinkedAccount()) {
mAccountManager.unlink();
} else {
mAccountManager.startLink(getActivity(), REQUEST_LINK_TO_DBX);
}
return true;
}
});
MainActivity.java (Get datastore size).
// Dropbox Get Datastore Size
mAccountManager = DbxAccountManager.getInstance(getApplicationContext(), APP_KEY, SECRET_KEY);
if (mAccountManager.hasLinkedAccount()) {
DbxAccount mDbxAccount = mAccountManager.getLinkedAccount();
try {
mDbxStore = DbxDatastore.openDefault(mDbxAccount);
mDbxNotesTable = mDbxStore.getTable("notes");
mDbxNotesCount = mDbxNotesTable.query().count();
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
} catch (DbxException e) {
e.printStackTrace();
}
}
Let's say there are 3 records in the datastore.
After I sign in, this shows up from the Logcat.
E/mDbxNotesCount﹕ 0
Once I restart it, this shows up from the Logcat.
E/mDbxNotesCount﹕ 3
What's causing it to work like this?
Edit: Added listener after using smarx's answer, and it works! Here is what I did.
mDbxStore.addSyncStatusListener(new DbxDatastore.SyncStatusListener() {
#Override
public void onDatastoreStatusChange(DbxDatastore dbxDatastore) {
if (!dbxDatastore.getSyncStatus().isDownloading) {
try {
mDbxStore.sync();
mDbxNotesCount = mDbxNotesTable.query().count();
} catch (DbxException e) {
e.printStackTrace();
}
Log.e("mDbxNotesCount", Integer.toString(mDbxNotesCount));
}
}
});
It looks like you're trying to read the contents of the datastore before it's had a chance to sync. You'll need to wait until the initial sync is completed. I think one way to do that would be to add a listener via addSyncStatusListener and wait for store.getSyncStatus().isDownloading to be false.
But typically the pattern you'll want to follow is to always show the current information... so set up a listener and update your UI any time it changes. That way whether it's the initial sync bringing in new records or updates from a different device, your app will respond appropriately when the information changes.

KSoap2 on Android freezing the device instead of making a webservice call

I'm trying to connect to .NET 4.0 webservice I created for receiving SOAP-calls from Android-devices, now hosted on local IIS for testing purposes.
I found out that ksoap2 would be an excellent class library for doing what i want to do. Downloaded the .jar package from https://code.google.com/p/ksoap2-android/ and started pounding the keyboard in ecstacy... with my fingers.
The amount of information being sent is from few kilobytes to few megabytes.
What is working
HttpTransportSE.call(String, SoapSerializationEnvelope)-method works perfectly while still in Eclipse's Android emulator, sending the call to webservice hosted in local IIS. Even tested that the webservice receives empty calls from trying to open the service address from a web browser in the same local area network.
What doesn't work
When I copy the .apk-file to an Android device, install it, start it and trying to make the call, the whole program freezes without making the call.
As you can see from a code block presented some lines after that possible errors are being taken into account: In emulated environment a successful call returns a SoapPrimitive-object or flows into the correct catch block generating an error message for the user according to the current situation.
Then on live Android device, program loses it's responsivity forever and has to be terminated from application menu.
What have i tried
I removed the call from the asynchronous method, and tried calling it straight from an anonymous inner function assigned for a button click-event.
Tried not trying to get a response, just making the call.
Tried getting a logcat-program for the device to see what's happening behind the UI, found two, they needed root access, which i don't have in the device. This is why i don't have any logcats to show you, and showing the emulator logcat would probably(?) be useless because it works fine there.
Not trying to connect to localhost.
Tried installing the program on older Lenovo-tablet running Android 4.2.2 and on brand new Samsung Galaxy Tab, both would have the same problem while otherwise working well.
The code
Here's the asynchronous method for making the call in device/emulator, where variables str_URL and soapRequest are a correct service address (checked) and a well formed SoapObject respectively:
#Override
protected WebServiceResult doInBackground(Void... v) {
WebServiceResult _ret;
SoapSerializationEnvelope soapEnvelope= new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.dotNet=true;
soapEnvelope.setAddAdornments(false);
soapEnvelope.setOutputSoapObject(soapRequest);
HttpTransportSE conn = new HttpTransportSE(str_URL);
conn.setXmlVersionTag("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
conn.debug = true;
try {
conn.call(str_ACTION, soapEnvelope);
SoapObject o = (SoapObject)soapEnvelope.getResponse();
_ret = new WebServiceResult(o, WebServiceResultEnum.ok);
} catch (NetworkOnMainThreadException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.keskeytys);
} catch (HttpResponseException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.httpVirhe);
} catch (XmlPullParserException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.vaara_muoto);
} catch (SocketTimeoutException e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.aikakatkaisu);
} catch (Exception e) {
_ret = new WebServiceResult(null, WebServiceResultEnum.keskeytys);
}
return _ret;
}
Thank you in advance!
Is it possible you are doing something like this:
YourAsyncTask task = new YourAsyncTask();
WebServiceResult result = task.doInBackground();
Because that would be wrong, completely wrong. If you call doInBackground() directly it will run in the same Thread and not in a new one. You need to start the AsyncTask with execute() like this:
YourAsyncTask task = new YourAsyncTask();
task.execute();
You need to implement the AsyncTask like this:
public class ExampleTask extends AsyncTask<Void, Void, WebServiceResult> {
public interface FinishedListener {
public void onFinished(WebServiceResult result);
}
private final FinishedListener finishedListener;
public ExampleTask(FinishedListener listener) {
this.finishedListener = listener;
}
#Override
protected WebServiceResult doInBackground(Void... params) {
WebServiceResult result = ...;
return result;
}
#Override
protected void onPostExecute(WebServiceResult result) {
super.onPostExecute(result);
if(this.finishedListener != null) {
this.finishedListener.onFinished(result);
}
}
}
And if you implemented it that way you can use it like this:
ExampleTask task = new ExampleTask(new ExampleTask.FinishedListener() {
#Override
public void onFinished(WebServiceResult result) {
// This will be called if the task has finished
}
});
task.execute();
It seems that I had declared the minimum SDK as 14 and target SDK as 17 in AndroidManifest.xml. I didn't use any fancy things in newer sdk's so i lowered the target SDK to the same level as minimum SDK, 14. I also had an Avast! Antivirus service running on the tablet which i removed.
This solved my problem. It could be that probably the Avast! antivirus-program wanted to block all communications from applications not downloaded from Play-store. I don't know if changing the target SDK had much effect really.
Well, I had the same question as you. When it goes to the method transport.call, it pauses, and for a while, it throws a timeout problem. At first, I thought maybe the network was poor, but the server logcat shows it is not the problem. The request was fine and the response was good. My business process is like below:
First, I get a list from the server through ksoap inner a child thread, then cycle the list, send a ksoap request based on every item of the list. It means it will send another list.size() request. When debugging in a real device the above problems occured. I solved it by starting a new child thread after getting the list and making all the list.size requests in the new child thread. So, ksoap use in android may cause thread block which leads to ioexception. So when you put it in a new thread, it escapes from the parent catch exception and works fine.

Android SIP registration failed (-9 IN_PROGRESS)

Here is my registration code:
protected void initializeManagerOpen(){
consoleWrite("initializeOpen");
if(mSipManager==null) {
return;
}
SipProfile.Builder builder;
try {
builder = new SipProfile.Builder("13", "10.0.0.4");
builder.setPassword("13");
builder.setPort(5062);
builder.setProtocol("UDP");
mSipProfile = builder.build();
try {
Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, Intent.FILL_IN_DATA);
mSipManager.open(mSipProfile, pendingIntent, null);
mSipManager.setRegistrationListener(mSipProfile.getUriString(), new SipRegistrationListener() {
public void onRegistering(String localProfileUri) {
mNotificationTask.endNotification();
mNotificationTask.createNotification(R.drawable.ic_stat_connecting,"Test","Connecting");
consoleWrite("Registering with SIP Server...");
}
public void onRegistrationDone(String localProfileUri, long expiryTime){
mNotificationTask.endNotification();
mNotificationTask.createNotification(R.drawable.ic_stat_connected,"Test","Connected");
consoleWrite("Ready");
}
public void onRegistrationFailed(String localProfileUri, int errorCode, String errorMessage){
mNotificationTask.endNotification();
mNotificationTask.createNotification(R.drawable.ic_stat_disconnected,"Test","Failed to connect:"+errorCode);
consoleWrite("Registration failed. Please check settings.");
consoleWrite(""+errorCode);
consoleWrite(errorMessage);
}
});
} catch (SipException e) {
e.printStackTrace();
}
} catch (ParseException e) {
e.printStackTrace();
}
}
Though sometimes it registered successfully, most time I got a error code -9:
Registration failed. Please check settings.
-9
0
I found this description on reference site:
public static final int IN_PROGRESS
The client is in a transaction and cannot initiate a new one.
Constant Value: -9 (0xfffffff7)
What does it means exactly? I don't have any other SIP application running on my phone.
PS. First time when i am trying to connect, it is working. But second time it returns -9. Maybe i not close connection correctly? I think i have problem because i am trying to close connection but it is not closing...
public void closeLocalProfile() {
if(mSipManager==null){
return;
}
try{
if(mSipProfile!=null){
mSipManager.close(mSipProfile.getUriString());
consoleWrite("mSipManager Closed - "+mSipProfile.getUriString());
}
}catch(Exception e){
consoleWrite("Failed to close local profile. - "+e);
}
}
Delete all SIP account from Call Parameter and retry :
Call App->Parameter->Call account internet
Delete all account
PS: Sorry for the name menu, my phone isn't in english
I've faced the same issue and zicos22's comment helped me to solve it. The problem starts because of unclosed SipProfiles, so you need to run closeLocalProfile() inside onPause() method (it does not work when called inside onDestroy()). Actually, i think i have to run this sip stuff in separate thread, but for now i'm just closing profile in onPause. On my android phone with ZenUI i'am able to close currently opened sip profiles manually in Settings -> Call Settings -> Phone Account Settings -> SIP accounts.

Categories

Resources