java - Android HTTP 请求在模拟器中工作但不在磨损设备上

标签 java android android-volley wear-os

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

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

到目前为止我尝试了什么:

  • 我已在我的 manifest.xml 中请求互联网许可。
  • 我已将超时时间增加到 30 秒(请参阅下面的代码),但没有任何改变。
  • 我已尝试通过蓝牙将我的计算机和模拟器与手机连接(以复制我的物理 watch 与手机的蓝牙连接),并且模拟器仍然成功发出请求(尽管有两秒的延迟) ,排除蓝牙太慢的可能性。
  • 我确保 API 级别对于我的 Marshmallow watch 来说足够低(我的 watch 和应用程序都是 API 级别 23)。
  • 在使用我的恒温器数据向公司服务器发出请求之前,我尝试向 Google 发出快速测试请求,虽然 Google 请求在模拟器中返回网站的 HTML 代码,但它在我的 watch 上超时(请求后 30 秒已启动)。
  • 我尝试将一些虚拟数据放入应该加载数据的回收站 View 中,虚拟数据确实出现了,排除了回收站 View 被破坏的可能性。
  • 我从 watch 上删除了应用程序并重新安装,并从手机上删除了伴侣,重新安装并再次删除,都无济于事。
  • 与 Google 支持人员的长时间交谈没有产生任何有意义的结果。

这是我的代码(来 self 的主视图的适配器):

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);
    }

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

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

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


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

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


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

最佳答案

根据下面这两个对话,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 <2.0,那么:

在 Wearable 上,在 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 访问您的 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。有关入门的更多信息,请参阅 Accessing the Wearable Data LayerSyncing Data Items

希望这对您有所帮助。

关于java - Android HTTP 请求在模拟器中工作但不在磨损设备上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41770992/

相关文章:

java - Object.hashCode() 是 31 位有什么原因吗?

java - HTMLWorker.parseToList 忽略背景颜色

java - 如何在处理中添加外部库

android - 带有 SlidingNavigationDrawer 的 OnItemClickListener

java - 警告 :The Jack toolchain is deprecated Android studio

java - 如何在 JPA 2.1 中映射来自 NameStoredProcedure 的非实体结果集?

android - 为什么 volley 的响应字符串使用的编码与响应 header 中的编码不同?

java - 使用 volley 将图像上传到服务器,并在发布请求正文中包含多部分数据

java - 如何在尝试执行注册 Activity 时修复 Android 中的 "com.android.Volley.TimeoutError"

android - 如何在 Android Jetpack Compose 中没有可组合包装器的情况下将一项放在列的末尾?