Android模拟器中的HTTP请求可以正常工作,但在Wear设备上无法工作

5
我正在制作一个简单的Android Wear应用程序来控制我的温度调节器,我使用Volley发送POST请求来控制它们。在Android Wear模拟器中一切正常(请求有效),但是,在我的Moto 360上加载应用程序时,volley请求被调用但总是超时。
为什么我的手表上volley请求失败了,而在模拟器上可以成功呢?其他应用程序的请求在我的手表上都能成功(例如内置的天气应用程序可以在约3秒钟内加载天气数据)。最奇怪的是:我曾经在我手表上让这个应用程序(成功地发起了volley请求),并且在从Android Studio安装到手表后的一天左右,它突然无法加载数据,原因不明。
我已经尝试过以下解决方法:
- 在我的manifest.xml中请求了Internet权限。 - 我将超时时间增加到30秒(见下面的代码),但没有改变任何东西。 - 我尝试通过蓝牙连接将我的计算机和模拟器连接到手机的连接上(以复制物理手表与手机之间的蓝牙连接),模拟器仍然可以成功地进行请求(虽然有两秒的延迟),排除了蓝牙速度太慢的可能性。 - 我确保API级别足够低,适用于运行Marshmallow的手表(我的手表和应用程序都是API级别23)。 - 我尝试在请求公司服务器上的温度调节器数据之前对Google进行了快速测试请求,虽然模拟器中的Google请求返回站点的HTML代码,但它在我的手表上超时了(在请求启动后30秒)。 - 我尝试将一些虚拟数据放入应该加载数据的回收视图中,虚拟数据确实出现了,排除了回收视图损坏的可能性。 - 我从手表中删除了应用程序并重新安装了它,并从我的手机中删除了陪同应用程序,重新安装了它,并再次将其删除,但都没有成功。 - 与Google支持团队的长时间交谈没有产生任何有意义的结果。
下面是我从主视图的适配器中提取的代码:
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);
    }

以下是从我的主活动中的onCreate()方法调用的代码:

RequestQueue queue = Volley.newRequestQueue(this);
refreshThermostatsRecyclerView(queue);

如果您想查看在模拟器和手表上运行时创建的日志,可以在这里的Google Drive中找到。


编辑1:我的手表重新启动能够暂时解决问题并允许手表再次进行HTTP请求,但一旦手表断开蓝牙连接,连接WiFi,断开WiFi并重新连接蓝牙(因此每次我没有手机穿过公寓然后返回时都会出现问题)它就会再次出现问题。

编辑2:我将所有的volley请求都切换到Async线程中的HTTPURLConnection请求,但与volley相同的问题仍然存在。


tl;dr:我的应用程序的Volley请求在模拟器中工作,但在我的Android Wear手表上不再工作(尽管下载自Play Store的应用程序的类似请求可以工作),我该如何使我的应用程序的Volley请求在手表上再次工作?


@MelissaRojas的下面的答案暂时解决了问题,但并没有永久解决,正如您在我的问题的编辑1中所看到的那样。因此,我仍在寻找答案。 - owlswipe
你尝试过禁用缓存吗? - Anis LOUNIS aka AnixPasBesoin
1
@AnixPasBesoin 应用程序的缓存还是httpurlconnection的缓存? - owlswipe
Volley的缓存。 - Anis LOUNIS aka AnixPasBesoin
@HarisQureshi,确实这样做了。标准的POST HTTPURLConnection请求问题仍然存在。请参见我的问题的Edit 2部分。有没有什么方法可以让HTTPURLConnection工作? - owlswipe
显示剩余4条评论
4个回答

2

我也在我开发的 Android Wear 应用中使用了 Volley,并且在 Moto 360 上运行,我遇到了同样的问题几次。尝试重新启动设备。前往设置 > 重新启动。听起来有些蠢,但它对我有效。


我很高兴它能够正常工作。下次再出现这种情况,只需重新启动设备即可。 - Melissa Rojas
所以问题又回来了。重启手表可以暂时解决这个问题,但几个小时后它又会出现。有没有永久性的解决方法,让我的应用程序能够正常工作而无需每次都重新启动手表? - owlswipe
我没有找到其他处理方法。我认为这是Moto 360的互联网连接问题,因为它不仅影响我的应用程序,而且影响每个安装了使用互联网的应用程序。我在使用翻译和地图应用程序时也遇到了同样的问题。试一试,你会发现当你的应用程序无法工作时,它也无法工作。 - Melissa Rojas
现在这个问题已经有赏金了,顺便说一下。正在寻找一个可以永久修复这个问题的解决方案! - owlswipe
我将我的Moto 360升级到了Android Wear 2.0,然后在上面运行了我的应用程序,两天过去了一切仍然完美无缺!感谢所有的帮助 - 其他人只需要将手表更新到AW2.0即可解决问题。 - owlswipe
显示剩余4条评论

2
根据以下两个对话,似乎WiFi连接只允许Android Wear通过WiFi连接到手机而不是直接连接到互联网。但是,Android Wear 2.0可以使用常规网络API。(Android Wear上的直接互联网连接?)(Android Wear是否支持直接访问互联网?)因此,在Android Wear 2.0+中,来自可穿戴应用程序的Volley请求应该有效。如果要使用Android Wear<2.0,则需在可穿戴设备的onCreate()中添加一个键,指示电话是否应开始收集数据。
PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/shouldStart");
putDataMapReq.getDataMap().putBoolean(SHOULD_START_KEY, true);
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
PendingResult pendingResult = Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);

在手机端的onDataChanged方法中,检查手表是否要开始收集数据。如果是,则发起Volley请求。

for (DataEvent event : dataEvents) {
        if (event.getType() == DataEvent.TYPE_CHANGED) {
            // DataItem changed
            DataItem item = event.getDataItem();
            if (item.getUri().getPath().compareTo("/shouldStart") == 0) {
                DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
                boolean shouldStart = dataMap.getBoolean(SHOULD_START_KEY));
                if(shouldStart) {
                    Volley.newRequestQueue(this).add(request);
                }

            }
        } else if (event.getType() == DataEvent.TYPE_DELETED) {
            // DataItem deleted
        }
    }

接下来,你的Volley请求的onResponse应该将数据传回到可穿戴设备。

public void onResponse(String response) {

    PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/data");
    putDataMapReq.getDataMap().putString(DATA_KEY, true);
    PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
    PendingResult pendingResult = Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);

 }

最后,您可以使用onDataChanged访问Wearable中的数据,并将其存储在模型中以便传递给适配器:

for (DataEvent event : dataEvents) {
    if (event.getType() == DataEvent.TYPE_CHANGED) {
         // DataItem changed
         DataItem item = event.getDataItem();
         if (item.getUri().getPath().compareTo("/data") == 0) {
             DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
             parseAndpassToAdapter(dataMap.getString(DATA_KEY));
         }
    } else if (event.getType() == DataEvent.TYPE_DELETED) {
            // DataItem deleted
    }
}

你需要使用 Wearable.API 实现此功能,你的类应该实现 DataApi.DataListener 接口。有关更多信息,请参阅访问可穿戴设备数据层同步数据项。希望这能帮到你。

抱歉!手表的WiFi应该是开启的。而且,手机的WiFi和蓝牙应该是关闭的。 - BhalchandraSW
1
请尝试使用此指南:https://www.sitepoint.com/connecting-to-web-services-with-android-wear/ 希望能对您有所帮助。 - BhalchandraSW
@MelissaRojas 希望如此!考虑到它应该会在几周内到达我的手表,我想我会等待它的到来。 - owlswipe
感谢您的帮助! - owlswipe
我将我的Moto 360手表升级到了Android Wear 2.0,并在上面运行了我的应用程序,两天来一切都运行得非常完美!感谢所有的帮助 - 其他人要解决问题所需要做的就是将手表更新到AW2.0。 - owlswipe
显示剩余9条评论

1

如果您可以排除连接问题,可以尝试使用Volley的替代方案:

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'

版本很重要。接下来是您的请求:
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();
}

然后只需要从一个线程/异步任务中执行您的代码,或稍微延迟前端执行。
编辑:如果附加地图存在问题:
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());
}

另外需要注意的是,确保将这两个方法的返回类型从void更改为Map。

我尝试使用HTTPURLConnection作为volley的替代方案,你认为你这里的代码会更好吗?不过,我会尝试并回复你。 - owlswipe
我在想这可能与Volley的版本不兼容Android Wear的版本有关。希望它能正常工作! - KoalaKoalified
在"以防万一"部分,我大约遇到了四个错误。您介意自己测试一下,看看是否有错误吗?例如,entry没有.getKey()方法。 - owlswipe
@KoalaKoalified,这些头信息在Volley和HTTPURLConnection的等效请求中都可以正常工作,因此我不认为这是API的问题,尽管它确实会给出OK消息,但这很奇怪。那么,在NetHTTPTransport中有什么我可以尝试让这些头信息工作的方法,或者我应该放弃这条路并希望Android Wear 2修复这些问题? - owlswipe
1
我希望 Wear 2 可以解决这个问题。但听起来像是 API 的头部正则表达式出了问题,所以我不确定。 - KoalaKoalified
显示剩余9条评论

0

这不就是当手表通过蓝牙连接手机时,因为Wi-Fi被关闭,因此无法使用互联网吗?如果手表使用Wi-Fi连接手机,则可以使用互联网。

我正在开发wear 2.0应用程序,只需关闭手机上的蓝牙即可使手表获得互联网连接。


不,它最初是通过蓝牙工作的,但在切换到WiFi并返回后就无法工作了。手表通常通过蓝牙连接到互联网,这就是应用程序获取网络连接的方式。例如——当我的手表连接到蓝牙时,地图应用程序加载地图,是否有一种类似的方法来加载我的应用程序的互联网数据? - owlswipe
地图应用程序将通过向手机发送请求以获取地图数据来建立网络连接。它不仅仅使用网络调用。如果您查看穿戴式配套库,那么这就是获取网络数据的方法。检查WiFi是否可用,如果不可用,则将其传递给手机以获取数据。或者从我脑海中想到的类似的东西。 - Craig J
有趣。如果我想为Android Wear 2.0制作一个standalone=true应用程序,并且需要进行网络请求,有什么方法可以做到这一点吗? - owlswipe
如果您给出的答案告诉我如何在手表应用程序打开时从手机发起网络请求并将响应发送到手表,那么您就可以赢得奖励。 - owlswipe
@owlswipe 谢谢更新,我现在要将我的Moto360升级到Android Wear 2.0!!! - Melissa Rojas
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接