Augment实战-开发一个终端屏Android H5容器

这个案例比较简单,就是想用webview 封装下H5页面(背景是没有Android开发人员、客户端后面也不方便升级,但要安装在10寸屏上),自己开发也能做,但是自己一个个文件建和修改调试,估计也要一两小时,使用了Augment 也就花了10分钟写提示词,后面让它来写代码,基本上是3分钟一次搞定的,连项目规则都没写。

第一步,提示词

第一步主要目标是想做一个简单的技术方案,由是写了技术方案的提示词,让Gemini帮写了下技术方案,其实我感觉直接这提示词给Augment写代码估计也差不多,如果不行中间让他修改下,也差不多能完成。

提示词无非就背景、任务、要求三部分,不了解提示词怎么写的,可以去学习下提示词工程。

提示词

# 背景

- 现在有一个物联网项目,包括客户端10寸屏幕(安卓系统),和服务端两部分

- 考虑到团队资源不太熟悉安卓开发以及客户端不方便经常升级, 想在10寸屏开发一个webview包装H5页面的应用,业务只要开发H5页面内容就行了,同时因业务需求变化,只要开发H5页面就行了,不需要客户端升级应用

- 10寸屏硬件装的是安卓系统,版本不会很新,也不会太旧,硬件资源不是很丰富,但也不是很差

# 任务

- 基于以上背景,我需要开发一个客户端框架应用,提供给前端团队封装H5页面,请帮我做一个这样的技术方案,方便我让AI开发

- 技术方案包括一个wifi网络信息变更回调 或者 JS主动获取wifi网络信息的jsbridge

# 要求

- 因10寸屏安卓系统系统目前版本未知,建议选择不会太新也不会太旧的相关软件环境;

- 框架应用里面,后续应该还要加入其他少量功能开发,比如开发ASR(语音识别)相关的功能,提供jsBridge给 H5调用/回调

- 采用尽量简单的方案,不要太复杂的实现,但要考虑后续业务变化基本不需要升级客户端

- 安卓开发使用java,不使用kotlin,可以有少量的伪代码,但不要写太多代码,开发代码后面我会让AI开发

第二步 技术方案产出

考虑到Gemini对推理、代码能力比较强,技术方案应该也有优势,就让它给生成一份,用第一步的提示词喂给它,直接写就行了,返回的结果基本上99%是可以了,中间稍有两次对话,修改了一点东西,比如它给出方案有kotlin/java的实现,我就直接让他用java了。

以下是他最终的输出:

好的,这是一个为你量身定制的技术方案,旨在创建一个简单、可扩展的安卓 WebView 框架应用,用于封装 H5 页面,并能与原生功能(如 WiFi 信息、ASR)交互。

## 客户端框架应用技术方案 (安卓 WebView + H5)

### 1. 核心目标 🎯

- **快速迭代**:业务逻辑主要在 H5 端实现,实现快速更新,无需频繁升级客户端。
- **原生能力**:通过 JsBridge,H5 页面可以调用安卓原生功能。
- **扩展性**:框架设计应便于后续添加新的原生功能模块。
- **兼容性与简洁性**:选用稳定且广泛支持的技术,保持实现方案简单。

### 2. 技术选型 ⚙️

- **安卓开发语言**:**Java**
- **最低安卓版本 (minSdkVersion)**:API 21 (Android 5.0 Lollipop)。这个版本开始 WebView 基于 Chromium,性能和标准支持较好,且覆盖绝大多数设备。
- **目标安卓版本 (targetSdkVersion)**:API 30 (Android 11) 或 API 31 (Android 12)。选择一个相对较新的目标版本以符合 Google Play 政策,同时注意处理新版本带来的权限和行为变更。
- **开发工具**:Android Studio
- **JsBridge 实现**:使用安卓 WebView 自带的 `addJavascriptInterface` 方法,这是最简单直接的方式。

### 3. 框架应用结构 🏗️

```
app/
├── java/
│   └── com/inxvision/
│       ├── MainActivity.java           // 主活动,承载WebView
│       ├── WebViewConfig.java          // WebView的通用配置
│       ├── JsBridge.java               // JsBridge接口类,H5调用的Java方法在此定义
│       ├── modules/                    // 原生功能模块
│       │   ├── WifiModule.java         // WiFi信息处理模块
│       │   └── AsrModule.java          // (未来) ASR功能模块
│       └── receivers/
│           └── NetworkChangeReceiver.java // 网络状态变更广播接收器
│
├── res/
│   ├── layout/
│   │   └── activity_main.xml         // 主活动布局,包含WebView
│   └── values/
│       └── strings.xml               // 字符串资源,可配置H5加载URL
│
└── AndroidManifest.xml                 // 应用清单文件,声明权限等
```

### 4.核心组件实现要点

#### 4.1. WebView 配置 (`WebViewConfig.java` & `MainActivity.java`)

- **启用 JavaScript**:`webView.getSettings().setJavaScriptEnabled(true);`
- **DOM Storage 支持**:`webView.getSettings().setDomStorageEnabled(true);`
- **缓存策略**:考虑到 H5 经常更新,可以设置为 `webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);` 或 `LOAD_DEFAULT` 配合 H5 自身的缓存控制。
- **加载 H5 页面**:`webView.loadUrl("YOUR_H5_APP_URL");` URL 可以配置在 `strings.xml` 中。
- **处理内部跳转**:设置 `WebViewClient`,确保 H5 页面内部链接仍在 WebView 中打开。
- **JS 弹窗等支持**:设置 `WebChromeClient`。

**伪代码 (`MainActivity.java` onCreate):**

Java

```
// WebView webView = findViewById(R.id.my_webview);
// WebSettings webSettings = webView.getSettings();
// webSettings.setJavaScriptEnabled(true);
// webSettings.setDomStorageEnabled(true);
// webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); // 或根据需求调整

// // 设置WebViewClient以在应用内打开链接
// webView.setWebViewClient(new WebViewClient() {
//     @Override
//     public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
//         view.loadUrl(request.getUrl().toString());
//         return true; // 表示我们处理了这个URL加载
//     }
// });

// // JsBridge 注入
// JsBridge jsBridge = new JsBridge(this, webView);
// webView.addJavascriptInterface(jsBridge, "AndroidNative"); // "AndroidNative" 是H5调用时的对象名

// // 加载H5页面
// webView.loadUrl(getString(R.string.h5_url));
```

#### 4.2. JsBridge 实现 (`JsBridge.java`)

此类将包含所有暴露给 JavaScript 调用的 Java 方法。

**伪代码 (`JsBridge.java`):**

Java

```
// public class JsBridge {
//     private Context context;
//     private WebView webView;
//     private WifiModule wifiModule;
//     // private AsrModule asrModule; // 未来

//     public JsBridge(Context context, WebView webView) {
//         this.context = context;
//         this.webView = webView;
//         this.wifiModule = new WifiModule(context, webView);
//         // this.asrModule = new AsrModule(context, webView); // 未来
//     }

//     // ---------------- WiFi 相关 -----------------
//     @JavascriptInterface
//     public void getWifiInfo(String jsCallbackFunction) {
//         wifiModule.getCurrentWifiInfo(jsCallbackFunction);
//     }

//     // H5 主动注册 WiFi 变更监听的回调函数名
//     @JavascriptInterface
//     public void registerWifiChangeCallback(String jsCallbackFunction) {
//         wifiModule.setJsWifiChangeCallback(jsCallbackFunction);
//     }

//     // ---------------- ASR 相关 (未来) -----------------
//     // @JavascriptInterface
//     // public void startAsr(String jsCallbackFunction) {
//     //     asrModule.startRecognition(jsCallbackFunction);
//     // }

//     // @JavascriptInterface
//     // public void stopAsr() {
//     //     asrModule.stopRecognition();
//     // }

//     // 通用:Java 主动调用 JavaScript 方法
//     public void callJsMethod(String jsMethodName, String jsonData) {
//         if (webView != null) {
//             webView.post(() -> { // 确保在UI线程执行
//                 String script = String.format("javascript:%s(%s)", jsMethodName, jsonData == null ? "" : "'" + jsonData + "'");
//                 webView.evaluateJavascript(script, null);
//             });
//         }
//     }
// }
```

#### 4.3. WiFi 网络信息 (`WifiModule.java` & `NetworkChangeReceiver.java`)

**目标**:

1. JS 主动获取当前 WiFi 信息。
2. WiFi 网络变更时,主动回调通知 H5。

**`WifiModule.java` 伪代码:**

Java

```
// public class WifiModule {
//     private Context context;
//     private WebView webView; // 用于回调JS
//     private String jsWifiChangeCallback; // H5注册的回调函数名

//     public WifiModule(Context context, WebView webView) {
//         this.context = context;
//         this.webView = webView;
//     }

//     public void setJsWifiChangeCallback(String callbackName) {
//         this.jsWifiChangeCallback = callbackName;
//     }

//     // JS 主动获取
//     public void getCurrentWifiInfo(String jsCallbackFunction) {
//         WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
//         String resultJson;
//         if (wifiManager != null && wifiManager.isWifiEnabled()) {
//             WifiInfo wifiInfo = wifiManager.getConnectionInfo();
//             if (wifiInfo != null && wifiInfo.getSSID() != null) {
//                 // 构建包含SSID, BSSID, IP地址等信息的JSON字符串
//                 // e.g., {"ssid": "MyWifi", "bssid": "xx:xx:xx:xx:xx:xx", "ipAddress": "192.168.1.100"}
//                 resultJson = "{\"ssid\": \"" + wifiInfo.getSSID().replace("\"", "") + "\", \"ipAddress\": \"" + formatIpAddress(wifiInfo.getIpAddress()) + "\"}";
//             } else {
//                 resultJson = "{\"error\": \"Not connected to WiFi\"}";
//             }
//         } else {
//             resultJson = "{\"error\": \"WiFi not enabled or no permission\"}";
//         }
//         callJsMethod(jsCallbackFunction, resultJson);
//     }

//     // 由 NetworkChangeReceiver 调用,当网络变化时
//     public void onNetworkChanged() {
//         if (jsWifiChangeCallback != null && !jsWifiChangeCallback.isEmpty()) {
//             getCurrentWifiInfo(jsWifiChangeCallback); // 主动将最新信息推给JS
//         }
//     }

//     private void callJsMethod(String jsFunctionName, String data) {
//         if (webView != null && jsFunctionName != null && !jsFunctionName.isEmpty()) {
//             webView.post(() -> {
//                 String script = String.format("javascript:typeof %s === 'function' && %s(%s)", jsFunctionName, jsFunctionName, "'" + data + "'");
//                 webView.evaluateJavascript(script, null);
//             });
//         }
//     }

//     private String formatIpAddress(int ipAddress) {
//         // 辅助函数:将int格式的IP转为字符串
//         return (ipAddress & 0xFF) + "." +
//                ((ipAddress >> 8) & 0xFF) + "." +
//                ((ipAddress >> 16) & 0xFF) + "." +
//                (ipAddress >> 24 & 0xFF);
//     }
// }
```

**`NetworkChangeReceiver.java` 伪代码:**

Java

```
// public class NetworkChangeReceiver extends BroadcastReceiver {
//     private WifiModule wifiModule; // 通过某种方式注入或获取 WifiModule 实例

//     // 构造函数或其他方式传递 WifiModule 实例
//     public NetworkChangeReceiver(WifiModule wifiModule) {
//         this.wifiModule = wifiModule;
//     }
//     // 空构造函数,用于 Manifest 注册,之后再关联 WifiModule
//     public NetworkChangeReceiver() {}
//     public void setWifiModule(WifiModule wifiModule) { this.wifiModule = wifiModule; }


//     @Override
//     public void onReceive(Context context, Intent intent) {
//         if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction()) ||
//             WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction())) {
//             if (wifiModule != null) {
//                 wifiModule.onNetworkChanged();
//             }
//         }
//     }
// }
```

**在 `MainActivity.java` 中注册和取消注册 `NetworkChangeReceiver`:**

Java

```
// private NetworkChangeReceiver networkChangeReceiver;
// private WifiModule wifiModuleInstance; // 从JsBridge或直接创建

// @Override
// protected void onCreate(Bundle savedInstanceState) {
//     // ... 其他初始化 ...
//     // jsBridge = new JsBridge(this, webView);
//     // wifiModuleInstance = jsBridge.getWifiModule(); // 假设JsBridge提供getter
//     // networkChangeReceiver = new NetworkChangeReceiver(wifiModuleInstance);
//     // 或者
//     // wifiModuleInstance = new WifiModule(this, webView);
//     // networkChangeReceiver = new NetworkChangeReceiver();
//     // networkChangeReceiver.setWifiModule(wifiModuleInstance); // 关联实例
//     // webView.addJavascriptInterface(new JsBridge(this, webView, wifiModuleInstance /*, asrModule*/), "AndroidNative");
//     // ...
//     // registerReceiver();
// }


// private void registerNetworkReceiver() {
//    networkChangeReceiver = new NetworkChangeReceiver();
//    // 假设 JsBridge 实例为 jsBridge, 并且它持有了 WifiModule 实例
//    // 你需要确保 NetworkChangeReceiver 能访问到 WifiModule 的 onNetworkChanged 方法
//    // 一种方式是让 JsBridge (或 WifiModule) 提供一个静态方法或单例来获取回调接口/实例
//    // 这里简化处理,假设 JsBridge 实例可被访问,并且它内部有 wifiModule
//    // JsBridge jsBridge = ((YourApplicationClass) getApplication()).getJsBridge(); // 通过Application共享
//    // if (jsBridge != null && jsBridge.getWifiModule() != null) {
//    //    networkChangeReceiver.setWifiModule(jsBridge.getWifiModule());
//    // }
//    // 或者,如果 JsBridge 在 Activity 中创建,直接传递
//    networkChangeReceiver.setWifiModule(this.jsBridge.getWifiModuleInstance()); // 假设 JsBridge 有此方法

//    IntentFilter filter = new IntentFilter();
//    filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
//    filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
//    registerReceiver(networkChangeReceiver, filter);
// }

// @Override
// protected void onResume() {
//     super.onResume();
//     // 确保 JsBridge 和 WifiModule 已初始化
//     if (jsBridge != null && jsBridge.getWifiModuleInstance() != null && networkChangeReceiver == null) {
//        networkChangeReceiver = new NetworkChangeReceiver(jsBridge.getWifiModuleInstance());
//        IntentFilter filter = new IntentFilter();
//        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
//        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
//        registerReceiver(networkChangeReceiver, filter);
//     }
// }


// @Override
// protected void onPause() {
//     super.onPause();
//     if (networkChangeReceiver != null) {
//         unregisterReceiver(networkChangeReceiver);
//         networkChangeReceiver = null; // 防止内存泄漏
//     }
// }
```

*Note: `NetworkChangeReceiver` 与 `WifiModule` 的交互需要妥善处理。可以在 `MainActivity` 中创建 `WifiModule` 实例,并将其传递给 `JsBridge` 和 `NetworkChangeReceiver`。*

#### 4.4. ASR (语音识别) 功能 (未来 - `AsrModule.java`)

- **接口**:提供 `startRecognition(jsCallbackFunction)` 和 `stopRecognition()`。
- **实现**:使用安卓内置的 `SpeechRecognizer` API。
- **回调**:识别结果(成功文本、错误信息)通过 `jsCallbackFunction` 回调给 H5。
- **权限**:需要在 `AndroidManifest.xml` 中声明 `RECORD_AUDIO` 权限,并进行运行时权限请求。

**伪代码 (`AsrModule.java`):**

Java

```
// public class AsrModule {
//     private Context context;
//     private WebView webView;
//     private SpeechRecognizer speechRecognizer;
//     private String currentAsrCallback;

//     public AsrModule(Context context, WebView webView) {
//         this.context = context;
//         this.webView = webView;
//         // 初始化 SpeechRecognizer
//     }

//     public void startRecognition(String jsCallbackFunction) {
//         this.currentAsrCallback = jsCallbackFunction;
//         // 检查权限
//         // 配置 Intent, 设置 RecognitionListener
//         // speechRecognizer.startListening(intent);
//         // RecognitionListener 的 onResults, onError 等方法中调用 callJsMethod 通知H5
//         // e.g., callJsMethod(currentAsrCallback, "{\"text\": \"识别结果\"}");
//         // e.g., callJsMethod(currentAsrCallback, "{\"error\": \"识别错误码\"}");
//     }

//     public void stopRecognition() {
//         // if (speechRecognizer != null) speechRecognizer.stopListening();
//     }

//     private void callJsMethod(String jsFunctionName, String data) {
//          // 与 WifiModule 中的 callJsMethod 类似
//     }
// }
```

### 5. AndroidManifest.xml 配置 📜

XML

```
<?xml version="1.0" encoding="utf-f"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yourpackage">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />


    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:usesCleartextTraffic="true"> <activity android:name=".MainActivity"
                  android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        </application>
</manifest>
```

*动态注册 `BroadcastReceiver` (如 `NetworkChangeReceiver`) 通常更灵活,因为它允许你在 Activity 的生命周期内控制其行为,并且可以方便地传递 Activity/WebView 相关的引用。*

### 6. H5 端交互示例 (JavaScript) 💻

HTML

```
<!DOCTYPE html>
<html>
<head>
    <title>H5 App</title>
    <script type="text/javascript">
        // --- WiFi 信息 ---
        function handleWifiInfo(jsonString) {            console.log("WiFi Info received:", jsonString);            const wifiData = JSON.parse(jsonString);            document.getElementById('wifiStatus').innerText = wifiData.ssid ? `Connected to: ${wifiData.ssid}` : (wifiData.error || "WiFi status unknown");        }        function requestWifiInfo() {            if (window.AndroidNative && typeof window.AndroidNative.getWifiInfo === 'function') {                // "handleWifiInfo" 是在JS中定义的全局函数名,Java端会回调这个函数
                window.AndroidNative.getWifiInfo("handleWifiInfo");            } else {                console.log("AndroidNative bridge not available for getWifiInfo.");                document.getElementById('wifiStatus').innerText = "Native bridge not available.";            }        }        // 用于WiFi变更自动回调
        function onWifiNetworkChanged(jsonString) {            console.log("WiFi Info CHANGED:", jsonString);            const wifiData = JSON.parse(jsonString);            document.getElementById('wifiStatus').innerText = `WiFi Updated: ${wifiData.ssid || wifiData.error || "WiFi status unknown"}`;            // 可以在这里做更多UI更新
        }        function registerForWifiChanges() {            if (window.AndroidNative && typeof window.AndroidNative.registerWifiChangeCallback === 'function') {                // "onWifiNetworkChanged" 是JS中定义的全局函数名,Java端网络变化时会回调它
                window.AndroidNative.registerWifiChangeCallback("onWifiNetworkChanged");                console.log("Registered for WiFi changes.");            } else {                console.log("AndroidNative bridge not available for registerWifiChangeCallback.");            }        }        // --- ASR (未来) ---
        function handleAsrResult(jsonString) {            console.log("ASR Result:", jsonString);            const asrData = JSON.parse(jsonString);            if (asrData.text) {                document.getElementById('asrResult').innerText = `Recognized: ${asrData.text}`;            } else if (asrData.error) {                document.getElementById('asrResult').innerText = `ASR Error: ${asrData.error}`;            }        }        function startListening() {            if (window.AndroidNative && typeof window.AndroidNative.startAsr === 'function') {                // "handleAsrResult" 是JS中定义的全局函数名
                window.AndroidNative.startAsr("handleAsrResult");                document.getElementById('asrResult').innerText = "Listening...";            } else {                console.log("AndroidNative bridge not available for startAsr.");                document.getElementById('asrResult').innerText = "ASR bridge not available.";            }        }        // 页面加载完成后自动注册WiFi变更监听
        window.onload = function() {            registerForWifiChanges();            requestWifiInfo(); // 页面加载时获取一次当前状态
        };    </script>
</head>
<body>
    <h1>My H5 IoT App</h1>

    <div>
        <h2>WiFi Status</h2>
        <p id="wifiStatus">Loading WiFi status...</p>
        <button onclick="requestWifiInfo()">Refresh WiFi Info</button>
    </div>

    </body>
</html>
```

### 7. 后续扩展考虑 🧩

- **模块化**:每个原生功能(如 WiFi, ASR, 蓝牙等)封装在独立的 `Module` 类中,`JsBridge` 类负责路由和调用。
- **异步处理**:原生耗时操作应在子线程执行,结果通过 `webView.post()` 或 `runOnUiThread()` 回到主线程,再调用 `evaluateJavascript` 通知 H5。
- **错误处理与日志**:在 JsBridge 和原生模块中加入充分的错误处理和日志记录。
- **安全性**:警惕 `addJavascriptInterface` 可能带来的安全风险 (尤其在加载不受信任的 H5 内容时)。确保 H5 内容来源可靠。对于 API 17 以下,`@JavascriptInterface` 注解不是必需的,所有 public 方法都会暴露,需特别注意。由于你选用的 `minSdkVersion` 是 21,此问题已缓解。
- **H5 URL 配置**:提供本地配置 H5 入口 URL 的功能,通过长按屏幕右下角区域 3 秒,弹出密码验证对话框,验证通过后显示 URL 配置界面,可以修改 H5 页面地址或重置为默认地址。配置信息保存在 SharedPreferences 中,应用启动时自动读取最新配置的 URL。这种方式既方便现场调试和环境切换,又不会被普通用户误操作。

这个方案提供了一个基础但可扩展的框架。AI 可以基于此方案的伪代码和结构来生成具体的 Java 和 XML 代码。

第三步 让Augment写代码

很简单,新建一个工程文件夹,把上面的写技术方案提示词和技术方案 分别存到工程根目录下为文件,“需求说明.md”、“技术方案.md”。

打开IDEA,然后打开Augment对话框,开到Agent Auto模式。

直接一句话,附上两个文件,它就可以做事了:

@需求说明.md @技术方案.md 这是一个新的项目,请按项目说胆和技术方案文档,帮我完成开发

然后打开Android Studio软件,配置好环境,连上手机,打包验证,打包出了错,提示两个图标找不到,这个是Augment写完代码唯一有问题的地方,丢给Augment,它理解问题,马上就开始画图了,包括各尺寸的图标,画出来的png结果是打不开的,网上也看别人介绍说Augment画不了图,我直接让Gemini画了个标图放进去,打包运行,一切OK,后面还让它增加了项功能,就是H5地址不写死,可以随时设置,这样一次打包就不分环境可用了,

License:  CC BY 4.0

©2025 AI全书. Some rights reserved.

    备案号: 浙ICP备06043869号-8