Android App连接OneNET物联网平台实战:用OkHttp3获取MQTTS设备数据(附完整代码)

张开发
2026/4/15 11:47:52 15 分钟阅读

分享文章

Android App连接OneNET物联网平台实战:用OkHttp3获取MQTTS设备数据(附完整代码)
Android App连接OneNET物联网平台实战用OkHttp3获取MQTTS设备数据附完整代码在物联网应用开发中设备数据的实时获取与展示是核心需求之一。本文将手把手带你完成一个完整的Android应用开发实战通过OkHttp3库连接OneNET物联网平台获取MQTTS协议设备数据并展示在UI上。无论你是刚接触物联网开发的Android程序员还是需要快速集成OneNET平台的经验开发者这篇指南都能提供可直接复用的代码模块和避坑经验。1. 项目准备与环境配置在开始编码前我们需要完成基础环境配置。不同于简单的网络请求物联网应用需要特别注意安全配置和权限管理。首先在AndroidManifest.xml中添加网络权限uses-permission android:nameandroid.permission.INTERNET /对于Android 9.0及以上版本还需要配置网络安全策略。在res/xml目录下创建network_security_config.xml文件?xml version1.0 encodingutf-8? network-security-config base-config cleartextTrafficPermittedtrue trust-anchors certificates srcsystem / /trust-anchors /base-config /network-security-config然后在AndroidManifest.xml的application标签中引用这个配置android:networkSecurityConfigxml/network_security_config提示虽然我们允许明文传输(cleartextTrafficPermittedtrue)用于开发测试但生产环境建议使用HTTPS加密通信。2. 依赖引入与基础架构我们将使用OkHttp3进行网络请求在app/build.gradle中添加依赖implementation com.squareup.okhttp3:okhttp:4.9.3最新版本建议查看OkHttp官网。相比原文使用的3.10.0版本4.x系列在性能和安全性上都有显著提升。基础Activity布局(activity_main.xml)可以这样设计LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/android android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical android:padding16dp Button android:idid/btnFetchData android:layout_widthmatch_parent android:layout_heightwrap_content android:text获取设备数据 / TextView android:idid/tvData1 android:layout_widthmatch_parent android:layout_heightwrap_content android:layout_marginTop16dp android:textSize16sp / !-- 可根据需要添加更多TextView展示不同数据点 -- /LinearLayout3. OneNET平台认证机制解析OneNET平台提供两种主要认证方式认证方式适用协议特点有效期API-KeyHTTP/HTTPS简单直接在header中传递永久有效AuthorizationMQTTS需要生成token安全性更高通常3小时关键避坑点对于MQTTS协议设备必须使用Authorization token认证API-Key方式将无法获取数据这是官方文档中未明确说明的重要细节。Token生成需要使用官方提供的算法以下是Java版token生成工具类import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; public class TokenUtil { public static String generateToken(String productId, String deviceId, String accessKey, long expiry) { try { String version 2022-05-01; String resource String.format(products/%s/devices/%s, productId, deviceId); String signatureMethod md5; String data String.join(\n, version, resource, String.valueOf(expiry), signatureMethod); Mac hmac Mac.getInstance(HmacSHA1); SecretKeySpec secretKey new SecretKeySpec( accessKey.getBytes(StandardCharsets.UTF_8), HmacSHA1); hmac.init(secretKey); byte[] hash hmac.doFinal(data.getBytes(StandardCharsets.UTF_8)); String signature Base64.getEncoder().encodeToString(hash); return String.format(version%sres%set%dmethod%ssign%s, version, resource, expiry, signatureMethod, signature); } catch (Exception e) { e.printStackTrace(); return null; } } }注意官方提供的在线token计算工具可能存在兼容性问题建议使用代码生成token以确保可靠性。4. 数据请求与UI更新实现现在我们可以实现核心的数据获取逻辑。在MainActivity中public class MainActivity extends AppCompatActivity { private static final String TAG OneNETDemo; private static final String DEVICE_ID your_device_id; private static final String PRODUCT_ID your_product_id; private static final String ACCESS_KEY your_access_key; private OkHttpClient client; private TextView tvData1; Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); client new OkHttpClient(); tvData1 findViewById(R.id.tvData1); Button btnFetch findViewById(R.id.btnFetchData); btnFetch.setOnClickListener(v - fetchDeviceData()); } private void fetchDeviceData() { new Thread(() - { try { // 生成有效期为1小时的token long expiry System.currentTimeMillis() / 1000 3600; String token TokenUtil.generateToken( PRODUCT_ID, DEVICE_ID, ACCESS_KEY, expiry); if (token null) { runOnUiThread(() - Toast.makeText(this, Token生成失败, Toast.LENGTH_SHORT).show()); return; } String url String.format( https://api.heclouds.com/devices/%s/datapoints?limit1, DEVICE_ID); Request request new Request.Builder() .url(url) .addHeader(Authorization, token) .build(); Response response client.newCall(request).execute(); if (!response.isSuccessful()) { throw new IOException(Unexpected code response); } String responseData response.body().string(); JSONObject json new JSONObject(responseData); JSONArray datastreams json.getJSONObject(data) .getJSONArray(datastreams); // 解析第一个数据流 JSONObject datastream datastreams.getJSONObject(0); String id datastream.getString(id); JSONArray datapoints datastream.getJSONArray(datapoints); String value datapoints.getJSONObject(0).getString(value); String displayText String.format(%s: %s, id, value); runOnUiThread(() - tvData1.setText(displayText)); } catch (Exception e) { Log.e(TAG, 请求失败, e); runOnUiThread(() - Toast.makeText(this, 请求失败: e.getMessage(), Toast.LENGTH_SHORT).show()); } }).start(); } }5. 高级功能与优化建议5.1 多数据流处理实际项目中设备通常会有多个数据流。我们可以优化代码处理这种情况private void processMultipleDatastreams(JSONArray datastreams) { StringBuilder sb new StringBuilder(); for (int i 0; i datastreams.length(); i) { JSONObject datastream datastreams.getJSONObject(i); String id datastream.getString(id); String value datastream.getJSONArray(datapoints) .getJSONObject(0).getString(value); sb.append(String.format(%s: %s\n, id, value)); } runOnUiThread(() - tvData1.setText(sb.toString())); }5.2 数据刷新机制实现定时自动刷新数据private final Handler handler new Handler(); private final Runnable refreshRunnable new Runnable() { Override public void run() { fetchDeviceData(); handler.postDelayed(this, 5000); // 每5秒刷新一次 } }; // 在onResume中启动刷新 Override protected void onResume() { super.onResume(); handler.post(refreshRunnable); } // 在onPause中停止刷新 Override protected void onPause() { super.onPause(); handler.removeCallbacks(refreshRunnable); }5.3 错误处理与重试机制健壮的网络请求需要完善的错误处理private void fetchWithRetry(int retryCount) { new Thread(() - { int attempts 0; while (attempts retryCount) { try { fetchDeviceData(); return; } catch (Exception e) { attempts; if (attempts retryCount) { runOnUiThread(() - Toast.makeText(this, 最终请求失败, Toast.LENGTH_SHORT).show()); } else { try { Thread.sleep(2000); // 等待2秒后重试 } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } } } }).start(); }6. 性能优化与安全建议OkHttpClient复用避免为每个请求创建新实例使用单例模式管理连接池配置优化HTTP连接管理缓存策略对不常变的数据启用缓存HTTPS证书校验生产环境应严格校验服务器证书敏感信息保护不要将设备ID和密钥硬编码在代码中示例优化后的OkHttpClient配置private OkHttpClient createHttpClient() { return new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .connectionPool(new ConnectionPool(5, 5, TimeUnit.MINUTES)) .addInterceptor(new HttpLoggingInterceptor().setLevel( BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE)) .build(); }在实际项目中设备认证信息应该通过安全的方式获取如后台API或Android Keystore系统。

更多文章