1. 创建项目并安装依赖
npm init @capacitor/app@latest
cd my-app
npm install
2. 安装依赖
本文以 Android 为例,iOS 操作查看文档。
npm install @capacitor/android
npx cap add android
3. 配置 APP 启动加载地址
在 capacitor.config.json 中增加 server.url 配置,并将 plugins.SplashScreen.launchAutoHide 改为 true。更多配置查看文档。
修改后的配置:
{
"appId": "com.deepseek.chat",
"appName": "DeepSeek",
"webDir": "dist",
"server": {
"url": "https://chat.deepseek.com"
},
"plugins": {
"SplashScreen": {
"launchAutoHide": true
}
}
}
4. 编译 APP
npm run build
npx cap sync android
5. 运行 APP
本文以 Android 为例,iOS 操作查看文档。
用 Android Studio 打开项目下的 android 目录,点击运行即可。
无法加载时用模拟器的浏览器测试能否打开网站。Web View 加载时显示白屏,超时后显示报错页面。
6.1 自定义 Web View 错误页面
在 capacitor.config.json 中增加 server.errorPath 节点。
修改后的配置:
{
"appId": "com.deepseek.chat",
"appName": "DeepSeek",
"webDir": "dist",
"server": {
"url": "https://chat.deepseek.com",
"errorPath": "error.html"
},
"plugins": {
"SplashScreen": {
"launchAutoHide": true
}
}
}
6.2 创建 error.html
在 dist 目录下创建 error.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>连接错误</title>
</head>
<body>
<div class="error-container">
<h1>无法连接到服务器</h1>
<p>请检查您的网络连接,然后重试。</p>
</div>
</body>
</html>
7. 修改闪屏时间
在 capacitor.config.json 中增加 plugins.SplashScreen.launchShowDuration 配置。
修改后的配置:
{
"appId": "com.deepseek.chat",
"appName": "DeepSeek",
"webDir": "dist",
"server": {
"url": "https://chat.deepseek.com",
"errorPath": "error.html"
},
"plugins": {
"SplashScreen": {
"launchAutoHide": true,
"launchShowDuration": 500
}
}
}
8. 限制 Web View 可加载地址,禁用 debug,禁止加载 http
在 capacitor.config.json 中增加 server.androidScheme、server.allowNavigation、android.allowMixedContent、android.webContentsDebuggingEnabled。
修改后设为配置:
{
"appId": "com.deepseek.chat",
"appName": "DeepSeek",
"webDir": "dist",
"server": {
"url": "https://chat.deepseek.com",
"androidScheme": "https",
"allowNavigation": [
"https://chat.deepseek.com",
"https://*.deepseek.com"
],
"errorPath": "error.html"
},
"android": {
"allowMixedContent": false,
"webContentsDebuggingEnabled": false
},
"plugins": {
"SplashScreen": {
"launchAutoHide": true,
"launchShowDuration": 500
}
}
}
9. 修改 Web View 超时时间
修改 MainActivity.java。
package com.deepseek.chat;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.webkit.WebView;
import com.getcapacitor.BridgeActivity;
import com.getcapacitor.Bridge;
public class MainActivity extends BridgeActivity {
private static final String TAG = "MainActivity";
private static final int TIMEOUT_MS = 5000; // 5秒超时
private Handler timeoutHandler;
private Runnable timeoutRunnable;
private WebView webView;
private long pageLoadStartTime = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
timeoutHandler = new Handler(Looper.getMainLooper());
// 在 onCreate 中设置超时检测,因为页面在应用启动时就开始加载
setupTimeout();
}
@Override
public void onStart() {
super.onStart();
// 等待 Bridge 初始化完成后再开始监控
getBridge().getWebView().postDelayed(new Runnable() {
@Override
public void run() {
webView = getBridge().getWebView();
if (webView != null) {
// 开始页面加载监控
startPageLoadMonitoring();
}
}
}, 1000);
}
private void setupTimeout() {
timeoutRunnable = new Runnable() {
@Override
public void run() {
if (webView != null) {
Log.w(TAG, "Page load timeout after " + TIMEOUT_MS + "ms");
// 检查当前 URL,如果还在加载目标网站,则显示错误页面
String currentUrl = webView.getUrl();
if (currentUrl == null || currentUrl.contains("chat.deepseek.com") || currentUrl.isEmpty()) {
String errorUrl = "https://localhost/error.html";
webView.post(new Runnable() {
@Override
public void run() {
webView.loadUrl(errorUrl);
}
});
}
}
}
};
}
private void startPageLoadMonitoring() {
if (webView == null) return;
// 记录开始时间
pageLoadStartTime = System.currentTimeMillis();
// 启动超时检测
timeoutHandler.postDelayed(timeoutRunnable, TIMEOUT_MS);
Log.d(TAG, "Started page load monitoring with " + TIMEOUT_MS + "ms timeout");
// 定期检查页面加载状态,如果加载完成则取消超时
final Handler checkHandler = new Handler(Looper.getMainLooper());
final Runnable checkRunnable = new Runnable() {
@Override
public void run() {
if (webView != null && pageLoadStartTime > 0) {
String url = webView.getUrl();
// 如果 URL 不再是目标网站或已加载完成,取消超时
if (url != null && !url.contains("chat.deepseek.com") && !url.isEmpty()) {
cancelTimeout();
Log.d(TAG, "Page loaded successfully, cancelled timeout");
} else {
// 继续检查
checkHandler.postDelayed(this, 500);
}
}
}
};
checkHandler.postDelayed(checkRunnable, 1000);
// 使用 JavaScript 注入来监听页面加载状态(作为备用检测)
webView.postDelayed(new Runnable() {
@Override
public void run() {
if (webView != null) {
String js = "javascript:(function() {" +
"var startTime = Date.now();" +
"var timeout = " + TIMEOUT_MS + ";" +
"var checkInterval = setInterval(function() {" +
" if (document.readyState === 'complete' || document.readyState === 'interactive') {" +
" clearInterval(checkInterval);" +
" } else if (Date.now() - startTime > timeout) {" +
" clearInterval(checkInterval);" +
" window.location.href = 'https://localhost/error.html';" +
" }" +
"}, 100);" +
"})();";
webView.evaluateJavascript(js, null);
}
}
}, 500);
}
private void cancelTimeout() {
if (timeoutHandler != null && timeoutRunnable != null) {
timeoutHandler.removeCallbacks(timeoutRunnable);
}
pageLoadStartTime = 0;
}
@Override
public void onDestroy() {
super.onDestroy();
cancelTimeout();
}
}