前端使用阿里云圖形驗證碼并且與安卓進行交互實現(xiàn)方法
最近安卓同事那邊沒辦法去用原生的圖形驗證碼;要前端寫個html內(nèi)嵌進去,下面是流程如何實現(xiàn)的:
流程如下
1.引入阿里云前端腳本 & 基礎配置 在<head> 里:
<script>
window.AliyunCaptchaConfig = {
region: "cn",
prefix: "1fnzmt",
};
</script>
<script src="https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"></script>
window.AliyunCaptchaConfig:阿里云驗證碼的全局配置 region: “cn”:中國大陸區(qū)域
prefix: “1fnzmt”:你在阿里云控制臺那邊分配的前綴
AliyunCaptcha.js:阿里云的前端驗證碼 SDK 腳本,加載后會在window 上掛 initAliyunCaptcha 之類的方法。
自己又定義了一個 CONFIG:
const CONFIG = {
region: "cn",
prefix: "1fnzmt",
sceneId: "vjsqgp2k",
mode: "popup",
language: "cn",
slideStyle: { width: 360, height: 40 },
apiBaseURL: "http://39.96.184.160",
endpoints: {
captchaVerify: "/dev-api/android/sms/code"
}
};
sceneId: 阿里云驗證碼具體場景 ID。
apiBaseURL + endpoints.captchaVerify:你自己后端的驗證碼驗證接口地址。
2.初始化阿里云驗證碼實例
在 CaptchaManager.init() 里:
window.initAliyunCaptcha({
SceneId: CONFIG.sceneId,
mode: CONFIG.mode,
element: '#captcha-element',
button: '#verify-btn',
captchaVerifyCallback: this.captchaVerifyCallback.bind(this),
onBizResultCallback: this.onBizResultCallback.bind(this),
getInstance: (instance) => {
this.captcha = instance;
debugLog('驗證碼初始化成功');
}
});
邏輯:
- element: ‘#captcha-element’:驗證碼掛載在頁面中這個 div 上。
- button: ‘#verify-btn’:點擊“開始驗證”按鈕時,彈出/觸發(fā)驗證碼。
- captchaVerifyCallback:用戶通過驗證碼后,阿里云前端 SDK 會給你一個 captchaVerifyParam,然后會調(diào)用這個回調(diào)。
- onBizResultCallback:你業(yè)務側驗證成功/失敗后,返回一個業(yè)務結果給前端,這個回調(diào)會被觸發(fā)。
3.驗證碼通過后,前端調(diào)用你后端接口
captchaVerifyCallback 的實現(xiàn):
async captchaVerifyCallback(captchaVerifyParam) {
this.showStatus('驗證中...', 'loading');
try {
document.getElementById('verify-btn').disabled = true;
// 1. 先去原生拿手機號(下面第二部分會講這個接口)
const phoneNumber = await this.getPhoneNumber();
debugLog('最終使用的手機號: ' + phoneNumber);
// 2. 調(diào)用你后端的驗證接口(目前是 GET 請求)
const result = await this.apiRequest(CONFIG.endpoints.captchaVerify, {
phoneNumber: phoneNumber,
captchaVerifyParam: captchaVerifyParam
});
debugLog('后端返回結果: ' + JSON.stringify(result));
// 3. 按你的后端返回格式判斷是否成功
const success = result && result.code === 200 && result.data === true;
if (success) {
this.showStatus('驗證成功!', 'success');
} else {
const msg = (result && result.msg) ? result.msg : '驗證失敗';
this.showStatus(msg, 'error');
}
// 4. 返回給阿里云 SDK 一個統(tǒng)一結構
return {
captchaResult: success,
bizResult: success
};
} catch (error) {
debugLog('驗證失敗: ' + error.message);
this.showStatus('驗證失敗: ' + error.message, 'error');
return {
captchaResult: false,
bizResult: false
};
} finally {
document.getElementById('verify-btn').disabled = false;
}
}
調(diào)用后端的封裝在 apiRequest里:
async apiRequest(url, data) {
// 1. 拼接完整 URL
if (url.startsWith('http')) {
fullUrl = url;
} else if (url.startsWith('/')) {
fullUrl = CONFIG.apiBaseURL + url;
} else {
fullUrl = CONFIG.apiBaseURL + '/' + url;
}
// 2. 把 data 轉(zhuǎn)成 query 參數(shù) ?phoneNumber=xxx&captchaVerifyParam=yyy
const queryParams = new URLSearchParams();
for (const key in data) {
if (data[key] !== null && data[key] !== undefined) {
queryParams.append(key, data[key]);
}
}
...
const response = await fetch(fullUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
const result = await response.json();
...
return result;
}
總結這一塊:
前端通過 AliyunCaptcha.js 提供的 initAliyunCaptcha 接入阿里云驗證碼。
用戶操作 → 驗證碼通過 → 阿里云前端回調(diào) captchaVerifyCallback。
回調(diào)里先從安卓拿手機號,再調(diào)用你自己后端接口 /dev-api/android/sms/code。
后端返回 code=200 && data=true 視為驗證成功,再通過返回值告知阿里云 SDK 驗證結果。
跟安卓原生通訊的邏輯
與安卓約定的對象和方法
在 JS 這邊約定:
原生會在 window 上注入一個對象:window.testInterface。
你用的接口方法:
testInterface.getPhoneNumber(callbackName):安卓會拿到 callbackName,執(zhí)行完再在 JS 中調(diào)用 windowcallbackName 回傳結果。
testInterface.closeWebView():用于在業(yè)務成功時關閉 WebView。
這些是靠安卓那邊 WebView addJavascriptInterface 或 evaluateJavascript 實現(xiàn)的。
小結
阿里云連接邏輯: 通過 AliyunCaptcha.js + window.AliyunCaptchaConfig +initAliyunCaptcha 接入。 成功后在 captchaVerifyCallback 里拿到captchaVerifyParam。 再帶上手機號請求你自己后端http://39.96.184.160/dev-api/android/sms/code 進行業(yè)務驗證。 驗證結果再通過返回值以及
onBizResultCallback 和頁面交互。 與安卓通訊邏輯: 瀏覽器端默認認為安卓會注入
window.testInterface。 通過 NativeInterfaceManager 輪詢和回調(diào)機制確定接口是否可用。 通過
sendMessageToNative(action, callbackName) 把一個回調(diào)函數(shù)名給安卓,安卓執(zhí)行完再回調(diào)
windowcallbackName。 當前主要用到:
testInterface.getPhoneNumber(callbackName) → 獲取手機號
testInterface.closeWebView() → 業(yè)務成功后關閉 WebView 如果原生沒準備好或調(diào)用失敗,就用模擬數(shù)據(jù)
13400134000,頁面上也會顯示“模擬數(shù)據(jù)”。
完整代碼:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>圖形驗證碼驗證</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.captcha-container {
width: 100%;
max-width: 450px;
}
.captcha-form {
background: white;
padding: 30px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
text-align: center;
}
.captcha-form h2 {
color: #333;
margin-bottom: 8px;
font-weight: 600;
}
.description {
color: #666;
margin-bottom: 24px;
font-size: 14px;
line-height: 1.5;
}
.captcha-element {
margin: 20px 0;
min-height: 80px;
display: flex;
justify-content: center;
align-items: center;
border: 2px dashed #e0e0e0;
border-radius: 8px;
padding: 15px;
background: #fafafa;
}
.verify-btn {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
margin-bottom: 15px;
}
.verify-btn:hover:not(:disabled) {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.verify-btn:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.status-message {
margin: 15px 0;
padding: 10px;
border-radius: 4px;
font-size: 14px;
min-height: 20px;
}
.status-success {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-error {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.status-loading {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.server-info {
background: rgba(0, 0, 0, 0.05);
padding: 10px;
border-radius: 5px;
margin-top: 15px;
font-size: 12px;
color: #666;
line-height: 1.5;
}
.debug-info {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 5px;
padding: 10px;
margin-top: 15px;
font-size: 12px;
text-align: left;
max-height: 200px;
overflow-y: auto;
}
.debug-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.debug-controls button {
background: #6c757d;
color: white;
border: none;
padding: 4px 8px;
border-radius: 3px;
font-size: 10px;
cursor: pointer;
margin-left: 5px;
}
.debug-controls button:hover {
background: #5a6268;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.loading {
display: inline-block;
width: 16px;
height: 16px;
border: 2px solid #f3f3f3;
border-top: 2px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-right: 8px;
vertical-align: middle;
}
.interface-status {
padding: 8px;
border-radius: 4px;
margin: 10px 0;
font-weight: bold;
}
.interface-ready {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.interface-waiting {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.interface-missing {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.phone-info {
background: #e7f3ff;
color: #0066cc;
border: 1px solid #b3d9ff;
padding: 8px;
border-radius: 4px;
margin: 10px 0;
font-size: 14px;
}
.phone-number {
font-weight: bold;
color: #004085;
}
</style>
<!-- 阿里云驗證碼配置 -->
<script>
window.AliyunCaptchaConfig = {
region: "cn",
prefix: "1fnzmt",
};
</script>
<!-- 引入阿里云驗證碼JS -->
<script src="https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js"></script>
<!-- 引入vConsole用于移動端調(diào)試 -->
<script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
// 初始化vConsole
var vConsole = new VConsole();
console.log('? vConsole已加載,可以在移動端查看調(diào)試信息');
</script>
</head>
<body>
<div class="captcha-container">
<div class="captcha-form">
<h2>安全驗證</h2>
<p class="description">請完成驗證以繼續(xù)操作</p>
<!-- 接口狀態(tài)顯示 -->
<div id="interface-status" class="interface-status interface-waiting">
正在檢測原生接口...
</div>
<!-- 手機號信息顯示 -->
<div id="phone-info" class="phone-info">
當前手機號: <span id="current-phone" class="phone-number">等待獲取中...</span>
</div>
<!-- 驗證碼容器 -->
<div id="captcha-element" class="captcha-element"></div>
<!-- 驗證狀態(tài) -->
<div id="status-message" class="status-message"></div>
<button id="verify-btn" class="verify-btn">開始驗證</button>
<div class="server-info">
服務器地址: 192.168.5.59:5500
當前時間: <span id="current-time"></span>
</div>
<!-- 調(diào)試信息 -->
<div class="debug-info">
<div class="debug-header">
<h4>調(diào)試信息:</h4>
<div class="debug-controls">
<button id="clear-log">清空日志</button>
<button id="test-interface">測試接口</button>
</div>
</div>
<div id="debug-log"></div>
</div>
</div>
</div>
<script>
// 調(diào)試日志
function debugLog(message) {
const logElement = document.getElementById('debug-log');
const timestamp = new Date().toLocaleTimeString();
logElement.innerHTML += `[${timestamp}] ${message}
`;
console.log(message);
// 自動滾動到底部
logElement.scrollTop = logElement.scrollHeight;
}
// 顯示當前時間
function updateTime() {
document.getElementById('current-time').textContent = new Date().toLocaleString();
}
setInterval(updateTime, 1000);
updateTime();
// 配置參數(shù)
const CONFIG = {
region: "cn",
prefix: "1fnzmt",
sceneId: "vjsqgp2k",
mode: "popup",
language: "cn",
slideStyle: {
width: 360,
height: 40
},
apiBaseURL: "http://39.96.184.160",
endpoints: {
captchaVerify: "/dev-api/android/sms/code"
}
};
// 原生接口管理器
class NativeInterfaceManager {
constructor() {
this.isReady = false;
this.interfaceName = 'testInterface';
this.readyCallbacks = [];
this.retryCount = 0;
this.maxRetries = 100; // 延長到10秒
this.checkInterval = null;
this.init();
}
init() {
debugLog('初始化原生接口管理器');
debugLog('當前window對象的屬性: ' + Object.keys(window).filter(k => k.includes('test') || k.includes('Interface')).join(', '));
// 立即檢測一次
if (this.checkInterface()) {
return;
}
// 延遲啟動檢測,給Android更多注入時間
setTimeout(() => {
debugLog('開始定期檢測原生接口...');
this.startPeriodicCheck();
}, 500);
}
startPeriodicCheck() {
this.checkInterval = setInterval(() => {
debugLog(`檢測原生接口 (${this.retryCount + 1}/${this.maxRetries})`);
if (this.checkInterface() || this.retryCount >= this.maxRetries) {
clearInterval(this.checkInterval);
if (!this.isReady) {
debugLog('? 達到最大重試次數(shù),原生接口仍未就緒');
this.showInterfaceStatus('missing', '原生接口未就緒,使用模擬模式');
}
}
this.retryCount++;
}, 100);
}
checkInterface() {
if (window[this.interfaceName]) {
this.isReady = true;
debugLog('? 原生接口檢測成功');
this.showInterfaceStatus('ready', '原生接口已就緒');
// 執(zhí)行所有等待的回調(diào)
this.readyCallbacks.forEach(callback => callback(window[this.interfaceName]));
this.readyCallbacks = [];
return true;
}
return false;
}
onReady(callback) {
if (this.isReady) {
callback(window[this.interfaceName]);
} else {
this.readyCallbacks.push(callback);
}
}
async call(method, data) {
return new Promise((resolve, reject) => {
this.onReady(async (interfaceObj) => {
try {
const result = await this.sendMessageToNative(method, data, interfaceObj);
resolve(result);
} catch (error) {
reject(error);
}
});
// 如果接口未就緒,直接使用模擬數(shù)據(jù)
if (!this.isReady) {
debugLog('接口未就緒,使用模擬數(shù)據(jù)');
setTimeout(() => {
this.simulateNativeResponse(method).then(resolve);
}, 500);
}
});
}
// async sendMessageToNative(action, data, interfaceObj) {
// return new Promise((resolve, reject) => {
// const callbackName = 'cb_' + Math.random().toString(36).substring(2);
// debugLog(`發(fā)送消息給原生代碼,action: ${action}, callback: ${callbackName}`);
// window[callbackName] = (response) => {
// debugLog('安卓返回的驗證結果: ' + response);
// try {
// const result = typeof response === 'string' ? JSON.parse(response) : response;
// resolve(result);
// } catch (error) {
// debugLog('解析安卓返回結果失敗: ' + error);
// reject(error);
// }
// delete window[callbackName];
// };
// const timeoutId = setTimeout(() => {
// debugLog('調(diào)用原生方法超時');
// delete window[callbackName];
// this.simulateNativeResponse(action).then(resolve);
// }, 5000);
// if (interfaceObj && interfaceObj[action]) {
// try {
// // 直接傳遞回調(diào)函數(shù)名給安卓端
// interfaceObj[action](callbackName);
// clearTimeout(timeoutId);
// } catch (error) {
// debugLog('調(diào)用原生方法失敗: ' + error);
// clearTimeout(timeoutId);
// this.simulateNativeResponse(action).then(resolve);
// }
// } else {
// debugLog(`接口 ${this.interfaceName}.${action} 不存在,使用模擬數(shù)據(jù)`);
// clearTimeout(timeoutId);
// this.simulateNativeResponse(action).then(resolve);
// }
// });
// }
// 修改sendMessageToNative方法
async sendMessageToNative(action, data, interfaceObj) {
return new Promise((resolve, reject) => {
const callbackName = 'cb_' + Math.random().toString(36).substring(2);
debugLog(`發(fā)送消息給原生代碼,action: ${action}, callback: ${callbackName}`);
window[callbackName] = (response) => {
debugLog('安卓返回的驗證結果: ' + response);
try {
const result = typeof response === 'string' ? JSON.parse(response) : response;
resolve(result);
} catch (error) {
debugLog('解析安卓返回結果失敗: ' + error);
reject(error);
}
delete window[callbackName];
};
const timeoutId = setTimeout(() => {
debugLog('調(diào)用原生方法超時');
delete window[callbackName];
this.simulateNativeResponse(action).then(resolve);
}, 5000);
if (interfaceObj && interfaceObj[action]) {
try {
// 直接傳遞回調(diào)函數(shù)名給安卓端
interfaceObj[action](callbackName);
clearTimeout(timeoutId);
} catch (error) {
debugLog('調(diào)用原生方法失敗: ' + error);
clearTimeout(timeoutId);
this.simulateNativeResponse(action).then(resolve);
}
} else {
debugLog(`接口 ${this.interfaceName}.${action} 不存在,使用模擬數(shù)據(jù)`);
clearTimeout(timeoutId);
this.simulateNativeResponse(action).then(resolve);
}
});
}
simulateNativeResponse(action) {
return new Promise((resolve) => {
setTimeout(() => {
let mockData = {};
if (action === 'getPhoneNumber') {
mockData = {
phoneNumber: '13400134000',
success: true
};
}
debugLog(`模擬響應: ${JSON.stringify(mockData)}`);
resolve(mockData);
}, 500);
});
}
showInterfaceStatus(status, message) {
const element = document.getElementById('interface-status');
element.textContent = message;
element.className = `interface-status interface-${status}`;
}
// 測試接口
async test() {
debugLog('=== 測試原生接口 ===');
if (this.isReady) {
debugLog('? testInterface對象存在');
debugLog('testInterface方法: ' + Object.keys(window.testInterface).join(', '));
if (window.testInterface.getPhoneNumber) {
debugLog('? getPhoneNumber方法存在');
try {
const result = await this.call('getPhoneNumber', {});
debugLog('原生接口測試成功: ' + JSON.stringify(result));
} catch (error) {
debugLog('原生接口測試失敗: ' + error);
}
} else {
debugLog('? getPhoneNumber方法不存在');
}
} else {
debugLog('? testInterface對象不存在');
}
}
}
// 驗證碼管理
class CaptchaManager {
constructor() {
this.captcha = null;
this.nativeInterface = new NativeInterfaceManager();
this.currentPhoneNumber = null;
this.init();
}
init() {
try {
debugLog('開始初始化阿里云驗證碼');
window.initAliyunCaptcha({
SceneId: CONFIG.sceneId,
mode: CONFIG.mode,
element: '#captcha-element',
button: '#verify-btn',
captchaVerifyCallback: this.captchaVerifyCallback.bind(this),
onBizResultCallback: this.onBizResultCallback.bind(this),
getInstance: (instance) => {
this.captcha = instance;
debugLog('驗證碼初始化成功');
}
});
} catch (error) {
debugLog('驗證碼初始化失敗: ' + error.message);
}
}
// 獲取手機號 - 使用原生接口管理器
async getPhoneNumber() {
debugLog('開始獲取手機號');
try {
const result = await this.nativeInterface.call('getPhoneNumber', {});
if (result && result.phoneNumber) {
debugLog('成功獲取手機號: ' + result.phoneNumber);
this.updatePhoneDisplay(result.phoneNumber, true);
return result.phoneNumber;
} else {
debugLog('獲取手機號失敗,使用默認值');
this.updatePhoneDisplay('13400134000', false);
return '13400134000';
}
} catch (error) {
debugLog('獲取手機號異常: ' + error.message);
this.updatePhoneDisplay('13400134000', false);
return '13400134000';
}
}
// 更新手機號顯示
updatePhoneDisplay(phoneNumber, isReal) {
this.currentPhoneNumber = phoneNumber;
const phoneElement = document.getElementById('current-phone');
if (isReal) {
phoneElement.textContent = phoneNumber;
phoneElement.style.color = '#155724';
document.getElementById('phone-info').style.background = '#d4edda';
document.getElementById('phone-info').style.borderColor = '#c3e6cb';
document.getElementById('phone-info').style.color = '#155724';
} else {
phoneElement.textContent = phoneNumber + ' (模擬數(shù)據(jù))';
phoneElement.style.color = '#721c24';
document.getElementById('phone-info').style.background = '#f8d7da';
document.getElementById('phone-info').style.borderColor = '#f5c6cb';
document.getElementById('phone-info').style.color = '#721c24';
}
debugLog(`當前使用的手機號: ${phoneNumber} ${isReal ? '(真實數(shù)據(jù))' : '(模擬數(shù)據(jù))'}`);
}
// API請求封裝 - 修改為GET請求
async apiRequest(url, data) {
try {
let fullUrl;
if (url.startsWith('http')) {
fullUrl = url;
} else if (url.startsWith('/')) {
fullUrl = CONFIG.apiBaseURL + url;
} else {
fullUrl = CONFIG.apiBaseURL + '/' + url;
}
// 構建查詢字符串
const queryParams = new URLSearchParams();
for (const key in data) {
if (data[key] !== null && data[key] !== undefined) {
queryParams.append(key, data[key]);
}
}
const queryString = queryParams.toString();
if (queryString) {
fullUrl += (fullUrl.includes('?') ? '&' : '?') + queryString;
}
debugLog('請求URL: ' + fullUrl);
debugLog('請求數(shù)據(jù): ' + JSON.stringify(data));
const response = await fetch(fullUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
debugLog('響應結果: ' + JSON.stringify(result));
return result;
} catch (error) {
debugLog('API請求失敗: ' + error.message);
throw error;
}
}
// 驗證回調(diào)
async captchaVerifyCallback(captchaVerifyParam) {
this.showStatus('驗證中...', 'loading');
try {
// 禁用驗證按鈕
document.getElementById('verify-btn').disabled = true;
// 使用原生接口管理器獲取手機號
const phoneNumber = await this.getPhoneNumber();
debugLog('最終使用的手機號: ' + phoneNumber);
// 調(diào)用后端API - 使用GET請求
const result = await this.apiRequest(CONFIG.endpoints.captchaVerify, {
phoneNumber: phoneNumber,
captchaVerifyParam: captchaVerifyParam
});
debugLog('后端返回結果: ' + JSON.stringify(result));
// 根據(jù)后端 code 和 data 判斷是否驗證成功
const success = result && result.code === 200 && result.data === true;
if (success) {
this.showStatus('驗證成功!', 'success');
} else {
const msg = (result && result.msg) ? result.msg : '驗證失敗';
this.showStatus(msg, 'error');
}
// 按照阿里云要求返回結構
return {
captchaResult: success,
bizResult: success
};
} catch (error) {
debugLog('驗證失敗: ' + error.message);
this.showStatus('驗證失敗: ' + error.message, 'error');
return {
captchaResult: false,
bizResult: false
};
} finally {
document.getElementById('verify-btn').disabled = false;
}
}
// 業(yè)務回調(diào)
onBizResultCallback(bizResult) {
if (bizResult) {
this.showStatus('驗證成功!', 'success');
// 調(diào)用安卓關閉 WebView
try {
if (window.testInterface && typeof window.testInterface.closeWebView === 'function') {
debugLog('調(diào)用安卓 closeWebView 方法關閉頁面');
window.testInterface.closeWebView();
} else {
debugLog('未找到 testInterface.closeWebView 方法');
}
} catch (e) {
debugLog('調(diào)用安卓 closeWebView 出錯: ' + e);
}
setTimeout(() => {
if (window.onCaptchaSuccess) {
window.onCaptchaSuccess();
}
}, 1000);
} else {
this.showStatus('驗證失敗,請重試', 'error');
}
}
showStatus(message, type) {
const element = document.getElementById('status-message');
element.textContent = message;
element.className = `status-message status-${type}`;
if (type === 'loading') {
element.innerHTML = `<span class="loading"></span>${message}`;
}
}
}
// 頁面加載后初始化
document.addEventListener('DOMContentLoaded', () => {
debugLog('頁面加載完成,開始初始化驗證碼');
window.captchaManager = new CaptchaManager();
// 初始化調(diào)試控件
document.getElementById('clear-log').addEventListener('click', () => {
document.getElementById('debug-log').innerHTML = '';
debugLog('日志已清空');
});
document.getElementById('test-interface').addEventListener('click', () => {
window.captchaManager.nativeInterface.test();
});
});
// 成功回調(diào)
window.onCaptchaSuccess = function () {
debugLog('驗證碼驗證成功');
alert('驗證成功!');
};
// 添加安卓端回調(diào)通知 - 增強版
window.onNativeInterfaceReady = function() {
debugLog('?? 收到安卓端通知:原生接口已就緒');
if (window.captchaManager && window.captchaManager.nativeInterface) {
// 強制重新檢測
window.captchaManager.nativeInterface.isReady = false;
if (window.captchaManager.nativeInterface.checkInterface()) {
// 停止定期檢測
if (window.captchaManager.nativeInterface.checkInterval) {
clearInterval(window.captchaManager.nativeInterface.checkInterval);
}
}
}
};
// 全局暴露檢測方法,供Android主動調(diào)用
window.forceCheckInterface = function() {
debugLog('?? Android主動觸發(fā)接口檢測');
if (window.captchaManager && window.captchaManager.nativeInterface) {
return window.captchaManager.nativeInterface.checkInterface();
}
return false;
};
// 延遲測試原生接口
setTimeout(() => {
debugLog('開始測試原生接口');
if (window.captchaManager && window.captchaManager.nativeInterface) {
window.captchaManager.nativeInterface.test();
}
}, 3000);
</script>
</body>
</html>
總結
到此這篇關于前端使用阿里云圖形驗證碼并且與安卓進行交互實現(xiàn)方法的文章就介紹到這了,更多相關前端使用阿里云圖形驗證碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android 自定義驗證碼輸入框的實例代碼(支持粘貼連續(xù)性)
這篇文章主要介紹了Android 自定義驗證碼輸入框的實例代碼(支持粘貼連續(xù)性),代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10
Android應用開發(fā)的版本更新檢測升級功能實現(xiàn)示例
本文對Android版本更新的知識做全面的總結,主要包括開發(fā)中版本的設置,如何檢測本程序的版本,版本的更新判斷和顯示,新版本程序的安裝2022-04-04
關于Android中drawable必知的一些規(guī)則
drawable這個東西相信大家天天都在使用,每個Android開發(fā)者都再熟悉不過了,但可能還有一些你所不知道的規(guī)則,那今天我們就來一起探究一下這些規(guī)則。2016-08-08
Android studio 實現(xiàn)手機掃描二維碼功能
這篇文章主要介紹了Android studio 實現(xiàn)手機掃描二維碼功能,需要的朋友可以參考下2019-10-10
Android Studio進行APP圖標更改的兩種方式總結
這篇文章主要介紹了Android Studio進行APP圖標更改的兩種方式總結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-06-06
Android開發(fā)導入項目報錯Ignoring InnerClasses attribute for an anonym
今天小編就為大家分享一篇關于Android開發(fā)導入項目報錯Ignoring InnerClasses attribute for an anonymous inner class的解決辦法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12

