【问题标题】:Android HTTP Requests Working In Simulator But Not On Wear DeviceAndroid HTTP 请求在模拟器中工作但不在 Wear 设备上
【发布时间】:2017-04-08 19:33:38
【问题描述】:

我正在制作一个简单的 Android Wear 应用来控制我的恒温器,并且我正在使用 Volley 发送 POST 请求来控制它们。在 Android Wear 模拟器中一切正常(请求有效),但是,当应用程序在我的 Moto 360 上加载时,volley 请求被调用但总是超时。

为什么我的 volley 请求在我的手表上失败但在模拟器上工作?其他应用在我的手表上请求成功(例如,内置天气应用可以在大约 3 秒内加载天气数据)。而且,最奇怪的部分:我的手表上有应用程序工作(成功发出截击请求),并且在我从 Android Studio 将它安装到手表后大约一天,它突然停止加载数据没有明显的原因。

到目前为止我所做的尝试:

  • 我已在我的manifest.xml 中请求互联网许可。
  • 我已将超时时间增加到 30 秒(请参阅下面的代码),这并没有改变任何内容。
  • 我已尝试通过蓝牙将我的计算机和模拟器连接到我的手机连接(复制我的物理手表与我的手机的蓝牙连接),模拟器仍然成功发出请求(尽管有两秒的延迟) ,排除蓝牙太慢的可能。
  • 我确保我的 Marshmallow 手表的 API 级别足够低(我的手表和应用程序都是 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);
    }

使用以下代码从我的 Main Activity 中的 onCreate() 方法调用:

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

如果您想查看通过在模拟器和手表上运行此创建的日志,它们是on Google Drive here


编辑 1: 重新启动我的手表可以暂时解决此问题并允许手表再次发出 HTTP 请求,但一旦手表与蓝牙断开连接、连接到 WiFi、与 WiFi 断开连接,它就会再次中断,然后重新连接到蓝牙(所以每次我不带手机穿过公寓然后返回时它都会中断)。

编辑 2:我在异步线程中将 volley 请求全部切换到 HTTPURLConnection Requests,并且出现与 volley 相同的问题。


tl;dr:我的应用程序的 Volley 请求在模拟器中有效,但在我的 Android Wear 手表上不再有效(尽管 Play 商店下载的应用程序的类似请求有效),我怎样才能获得 volley请求在手表上再次使用我的应用程序?

【问题讨论】:

  • @MelissaRojas 在下面的回答暂时但不是永久地解决了这个问题,正如您将在我的问题的编辑 1 中看到的那样。所以我仍在寻找答案。
  • 你试过禁用缓存吗?
  • @AnixPasBesoin 应用程序或 httpurlconnection 的缓存?
  • Volley 的缓存。
  • @AnixPasBesoin 现在使用 HTTPURLConnection,所以设置myURLConnection.setUseCaches(false);,这没有帮助。还尝试在应用以this 关闭时自动清除应用缓存,但无济于事。还有什么我可以尝试的吗?

标签: java android android-volley wear-os


【解决方案1】:

根据下面这两个对话,WiFi 连接似乎只允许 Android Wear 通过 WiFi 连接到手机,而不是直接连接到互联网。不过,Android Wear 2.0 允许您使用常规网络 API。

Direct internet connection on Android Wear?

Does Android Wear support direct access to the Internet?

因此,对于 Android Wear 2.0+,来自可穿戴应用的 Volley 请求应该可以工作。

如果你想使用Android Wear

在可穿戴设备上,在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 应该将数据传回Wearable。

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 访问可穿戴设备中的数据并将其存储在模型中,以便将其传递给适配器:

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。更多入门信息,请参考Accessing the Wearable Data LayerSyncing Data Items

希望这会有所帮助。

【讨论】:

  • 所以你是说 AW 2.0 会神奇地解决我的问题?我需要在我的代码中添加什么特别的东西吗?
  • 请您测试以下内容:关闭手机的WiFi、蓝牙。然后,尝试将您的手表连接到 WiFi(如果您需要在手机中输入密码也可以)。下载全新的东西。例如,请您的朋友向您发送消息或类似的东西。当您收到消息时,您的手机应该连接到 wifi 并且手机的 wifi 蓝牙应该关闭。请您在此处发布此测试的结果。
  • 对不起!手表的 WiFi 应该打开。并且,手机的 WiFi、蓝牙应该是关闭的。
  • 确实,我的手表在连接到 WiFi 而不是蓝牙时可以正常工作。 就像连接到蓝牙时一样,我的手表接收通知,在应用程序中加载数据,并在 WiFi 上正常访问 Google。
  • sitepoint.com/connecting-to-web-services-with-android-wear 请尝试本指南。希望这会有所帮助。
【解决方案2】:

我还在我构建的 Android 穿戴应用程序上使用 volley,我在 Moto 360 上运行它,我遇到过几次同样的问题。尝试重新启动设备。转到设置 > 重新启动。这听起来很傻,但它对我有用。

【讨论】:

  • 感谢您的建议:D。我的手表没电了,插上电源后又重新启动,所以我认为重新启动是多余的。然而,不知何故,这些是不同的,重新启动只是神奇地修复了它。
  • 我很高兴它成功了。下次发生这种情况时,只需重新启动设备。
  • 所以问题又回来了。重新启动我的手表可以修复几个小时的凌空,然后它又坏了。是否有永久修复,让我的应用无需每次都重新启动手表即可运行?
  • 我还没有找到其他方法来处理它。我认为这是 Moto 360 的互联网连接问题,因为它不仅会影响我的应用程序,还会影响安装的每个使用互联网的应用程序。我在使用翻译和地图应用程序时遇到了同样的问题。试一试,你会发现它在你的应用无法运行的同时也无法运行。
  • 嗯!这对我来说不是这样:我的应用程序的 volley 请求不起作用,但地图加载正常,我也刚刚加载了天气预报。
【解决方案3】:

如果您可以排除连接问题,您可以尝试使用 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 的替代方案,您认为您的代码在这里会更好地工作吗?尽管如此,我会尽力回复您。
  • 我认为这可能与 volley 的版本控制与 Android Wear 的版本控制不兼容有关。希望它有效!
  • 值得一试
  • @KoalaKoalified 尝试从 Map 设置标题,得到 java.lang.IllegalArgumentException: field com.google.api.client.http.HttpHeaders.contentType has type java.util.List, got java.lang.String 我该如何解决这个问题并正确设置标题?
  • 我希望 Wear 2 能解决这个问题。对我来说,这听起来像是 api 的标头正则表达式有问题,尽管如此 idk。
【解决方案4】:

这不仅仅是手表通过蓝牙连接到手机时互联网无法工作的情况,因为 wifi 已关闭。如果手表使用wifi连接手机,则可以正常工作。

我正在开发 Wear 2.0 应用,只需关闭手机上的蓝牙,让手表连接互联网。

【讨论】:

  • 不,它最初通过蓝牙工作,只是在切换到 WiFi 后又返回。手表通常通过蓝牙连接到互联网,这就是应用程序获取网络连接的方式。例如,当我的手表连接到蓝牙时,地图应用程序会加载地图,有没有办法通过我的应用程序类似地加载互联网数据?
  • 地图应用程序将通过手机建立网络连接,同时向手机发送请求以获取地图数据。它不会只是使用网络调用。如果你看一下穿戴伴侣库,那么这就是获取网络数据的方法。检查WiFi是否可用,如果没有,则将其传递给手机以获取数据。或者类似的东西在我的脑海中。
  • 有趣。如果我想为 Android Wear 2.0 制作一个standalone=true 应用程序并且我需要发出网络请求怎么办?有没有办法做到这一点?
  • 好吧,如果你给我一个答案,告诉我如何在手表应用程序打开时从手机发起网络请求并将响应发送到手表,你就能赢得赏金。
  • 您必须通过数据层传输互联网数据。虽然如果你只想支持 android wear 2.0,你可以忘记我刚才所说的一切。从 2.0 开始,您可以请求使用 wifi,请参阅 google 文档:网络访问和同步 developer.android.com/training/wearables/data-layer/… 他们今天刚刚更新
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-08-19
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 2012-12-21
相关资源
最近更新 更多