I am making a simple Android Wear app to control my thermostats, and I'm sending POST requests with Volley to control them. Everything works great in the Android Wear simulator (the request works), but, while the app does load on my Moto 360, the volley request gets called but invariably times out.
Why could my volley request be failing on my watch but working on the simulator? Other apps' requests succeed on my watch (for example, the built-in weather app can load up weather data in about 3 seconds). And, the weirdest part: I had the app working (successfully making volley requests) on my watch, and, about a day after I installed it to my watch from Android Studio, it suddenly stopped loading data for no apparent reason.
What I've tried so far:
- I have requested the Internet permission in my
manifest.xml. - I have increased the timeout to 30 seconds (see my code below), which didn't change anything.
- I have tried tethering my computer and the simulator to my phone's connection via Bluetooth (to replicate the Bluetooth connection my physical watch has to my phone), and the simulator made the request successfully still (albeit with a two-second delay), ruling out the possibility of Bluetooth being too slow.
- I made sure the API level is low enough for my Marshmallow-running watch (my watch and the app are both API level 23).
- I tried doing a quick test request to Google before the request to the company's servers with my thermostat data, and while the Google request returns the site's HTML code in the simulator, it times out on my watch (thirty seconds after the request is initiated).
- I tried putting some dummy data into the recycler view data should be loaded into, and the dummy data indeed showed up, ruling out that the recycler view is broken.
- I deleted the app from my watch and reinstalled it, and deleted the companion from my phone, reinstalled it, and deleted it again, all to no avail.
- A lengthy chat with Google Support did not produce anything meaningful.
Here's my code (from my main view's adapter):
public void refreshThermostatsRecyclerView(RequestQueue queue) { String url = "https://mobile.skyport.io:9090/login"; // login call to the thermostats server Skyport Log.w("myApp", "Starting /login call to Skyport"); // this gets called on simulator and watch // Request a string response from the provided URL. StringRequest stringRequest = new StringRequest(Request.Method.POST, url, Response.Listener<String>() { @Override public void onResponse(String response) { // Display the response string. Log.w("myApp", "Response is: " + response); // this gets called on the simulator but not the watch try { // there's some code to parse the data. } catch (JSONException e) { Log.w("myApp", "catching an error parsing the json."); // never gets called. e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.w("myApp", "Skyport request didn't work! " + error); // this always gets called on the watch, with the error being a timeout error (com.Android.Volley.timeouterror) but never gets called in the simulator } }) { @Override public Map<String, String> getHeaders() throws AuthFailureError { Map<String, String> m = new HashMap<>(); m.put("Referer", "app:/VenstarCloud.swf"); // here I put some more headers return m; } @Override protected Map<String, String> getParams() throws AuthFailureError { Map<String, String> m = new HashMap<>(); m.put("version", "3.0.5"); m.put("email", userEmail); m.put("password", userToken); return m; } }; // Add the request to the RequestQueue. int socketTimeout1 = 30000; // times out 30 seconds after the request starts on the watch RetryPolicy policy1 = new DefaultRetryPolicy(socketTimeout1, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT); stringRequest.setRetryPolicy(policy1); queue.add(stringRequest); } Which is called from the onCreate() method in my Main Activity with this code:
RequestQueue queue = Volley.newRequestQueue(this); refreshThermostatsRecyclerView(queue); If you'd like to view the logs created by running this in the simulator and on the watch, they're on Google Drive here.
Edit 1: A reboot of my watch fixes the issue temporarily and allows the watch to make HTTP Requests again, but it breaks again once the watch disconnects from Bluetooth, connects to WiFi, disconnects from WiFi, and reconnects to Bluetooth (so it breaks every time I go across my apartment without my phone and then return).
Edit 2: I switched the volley requests all over to HTTPURLConnection Requests in an Async thread, and the same issues occur as with volley.
tl;dr: My app's Volley requests are working in the simulator but not on my Android Wear watch anymore (though Play Store-downloaded apps' similar requests work), how can I get a volley request to work again on my app on the watch?
4 Answers
Answers 1
I am also using volley on an Android wear app I built and I am running it on a Moto 360, I have run into the same problem a couple o times. Try restarting the device. Go to Settings > Restart. It sounds silly but it has worked for me.
Answers 2
You could try an alternative to volley if you can rule out the connection as the problem:
compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.android.support:support-v4:23.1.0' compile 'com.android.support:design:23.1.0' compile 'com.google.code.gson:gson:2.2.4' compile 'com.google.api-client:google-api-client:1.20.0' The versions are important.
Then to your request:
Map<String, String> contentParams = new HashMap<>(); InputStream is = null; NetHttpTransport transport = null; HttpRequest request = null; HttpResponse resp = null; HttpHeaders headers = new HttpHeaders(); JSONObject json = null; try { transport = new NetHttpTransport(); HttpRequestFactory factory = transport.createRequestFactory(); request = factory.buildPostRequest(new GenericUrl(url), null); contentParams = getContentParameters(); headers.putAll(getHeaderParameters()); request.setHeaders(headers); request.getUrl().putAll(contentParams); resp = request.execute(); is = resp.getContent(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (is != null) { string = getJSONFromInputStream(is); json = new JSONObject(string); } } catch (Exception e) { e.printStackTrace(); } } transport.shutdown(); protected Map<String, String> getContentParameters() { Map<String, String> m = new HashMap<>(); m.put("version", "3.0.5"); m.put("email", userEmail); m.put("password", userToken); return m; } protected Map<String, String> getHeaderParameters() { Map<String, String> m = new HashMap<>(); m.put("Referer", "app:/VenstarCloud.swf"); return m; } protected String getJSONFromInputStream(InputStream is) { if (is == null) throw new NullPointerException(); //instantiates a reader with max size BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8 * 1024); StringBuilder sb = new StringBuilder(); try { //reads the response line by line (and separates by a line-break) String line; while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { //closes the inputStream is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } Then just execute your code from a thread/asynctask/have it delay your frontend slightly
Edit: Just in case there is a problem with appending a map:
for (Entry<String, String> entry : getHeaderParameters()) { headers.put(entry.getKey(), entry.getValue()); } for (Entry<String, String> entry : getContentParameters()) { request.getUrl().put(entry.getKey(), entry.getValue()); } Also as another note, make sure to change the return type from void on both those methods to Map
Answers 3
Is this not just the case of when the watch is connected to the phone via bluetooth the internet will not work, as wifi is turned off. If the watch is using wifi to connect to the phone then it will work.
I'm working on wear 2.0 app and just turn blueooth off on my phone for my watch to get internet connection.
Answers 4
Perhaps, your thermostat server https://mobile.skyport.io:9090 has restrictions on API key.
For example, Google APIs are restricted by IP addresses that can call them. Maybe you added IP address of your computer on which simulator is running but forgot to add IP of your watch.
Kind regards, Bala
0 comments:
Post a Comment