<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>H5 on Zhang's Notes</title><link>https://zhangsnotes.com/tags/h5/</link><description>Recent content in H5 on Zhang's Notes</description><generator>Hugo</generator><language>zh-CN</language><copyright>Zhang's Notes</copyright><lastBuildDate>Sat, 26 Sep 2026 13:43:09 +0800</lastBuildDate><atom:link href="https://zhangsnotes.com/tags/h5/index.xml" rel="self" type="application/rss+xml"/><item><title>WebView 中 WebSocket 与 H5 状态同步方案</title><link>https://zhangsnotes.com/posts/webview-websocket-h5-status-sync/</link><pubDate>Sat, 26 Sep 2026 11:16:05 +0800</pubDate><guid>https://zhangsnotes.com/posts/webview-websocket-h5-status-sync/</guid><description>分析 APP 已有 WebSocket 与 WebView H5 实时状态同步的方案，通过 JS Bridge 传递状态变化通知，并结合 H5 HTTP API 获取最新数据。</description><content:encoded><![CDATA[<p><img src="/posts/webview-websocket-h5-status-sync/cover.avif" alt="cover"></p>
<blockquote>
<p>图片来自 <code>https://www.pexels.com/zh-cn/photo/macbook-209701/</code></p>
</blockquote>
<h2 id="一场景">一、场景</h2>
<p>APP 内嵌一个 WebView，WebView 中运行 H5 页面。</p>
<p>当前系统存在以下条件：</p>
<ol>
<li>APP 已经建立并维护 WebSocket 长连接。</li>
<li>H5 页面本身有大量 HTTP API，与后端进行业务数据交互。</li>
<li>H5 需要实时感知部分状态变化，状态可能在 1 分钟内变化 3～5 次。</li>
<li>H5 不允许单独建立 WebSocket，否则会与 APP 的 WebSocket 连接产生冲突。</li>
<li>H5 收到状态变化后，需要及时更新页面数据。</li>
</ol>
<p>因此需要解决的问题是：</p>
<blockquote>
<p>如何利用 APP 已有的 WebSocket，将实时状态变化传递给 H5，同时继续保持 H5 原有的 API 数据获取模式。</p>
</blockquote>
<hr>
<h2 id="二方案一h5-单独建立-websocket">二、方案一：H5 单独建立 WebSocket</h2>
<p>结构：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">后端
</span></span><span class="line"><span class="cl">├── WebSocket → APP
</span></span><span class="line"><span class="cl">└── WebSocket → H5</span></span></code></pre></div>
</div>
<p>不推荐。</p>
<p>H5 如果再次建立 WebSocket，会产生多个实时连接，同时与现有 APP WebSocket 的连接管理产生冲突。</p>
<p>如果后端同一用户只允许一个 WebSocket 连接，还可能导致 APP 的 WebSocket 被 H5 挤掉，从而影响 APP 本身的消息推送。</p>
<p>因此在当前限制下，H5 不应该直接建立 WebSocket。</p>
<hr>
<h2 id="三方案二app-websocket--js-bridge--h5">三、方案二：APP WebSocket → JS Bridge → H5</h2>
<p>推荐采用 APP 作为唯一的 WebSocket 客户端。</p>
<p>数据流：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">后端 WebSocket
</span></span><span class="line"><span class="cl">       ↓
</span></span><span class="line"><span class="cl">APP WebSocket
</span></span><span class="line"><span class="cl">       ↓
</span></span><span class="line"><span class="cl">Native
</span></span><span class="line"><span class="cl">       ↓
</span></span><span class="line"><span class="cl">JS Bridge
</span></span><span class="line"><span class="cl">       ↓
</span></span><span class="line"><span class="cl">H5</span></span></code></pre></div>
</div>
<p>APP 收到 WebSocket 消息后，通过 WebView 的 JavaScript Bridge 通知 H5。</p>
<p>例如 WebSocket 收到：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">json</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;status_changed&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;id&#34;</span><span class="p">:</span> <span class="mi">123</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<p>Native 将事件发送给 H5：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">javascript</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nb">window</span><span class="p">.</span><span class="nx">NativeBridge</span><span class="p">.</span><span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;status_changed&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">id</span><span class="o">:</span> <span class="mi">123</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div>
</div>
<p>H5 收到事件后处理对应业务。</p>
<p>这种方式可以避免 H5 建立第二个 WebSocket。</p>
<hr>
<h2 id="四不要让-websocket-消息成为-h5-的数据源">四、不要让 WebSocket 消息成为 H5 的数据源</h2>
<p>由于 H5 本身已经存在大量 HTTP API，因此更合理的设计是：</p>
<blockquote>
<p>WebSocket 负责发送“变化通知”，HTTP API 负责获取“最新状态”。</p>
</blockquote>
<p>例如：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">后端状态发生变化
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">WebSocket
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">APP
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">JS Bridge
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">H5 收到 status_changed
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">调用 HTTP API
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">获取最新状态
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">更新 UI</span></span></code></pre></div>
</div>
<p>例如 H5 收到：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">json</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;task_changed&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;taskId&#34;</span><span class="p">:</span> <span class="mi">123</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<p>然后调用：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">http</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-http" data-lang="http"><span class="line"><span class="cl"><span class="err">GET /api/task/123</span></span></span></code></pre></div>
</div>
<p>获取最新数据。</p>
<p>这样可以继续复用 H5 原有的 API、数据模型和业务逻辑，而不需要在 Android Native 中重新实现一套业务数据处理逻辑。</p>
<hr>
<h2 id="五推荐采用事件机制">五、推荐采用事件机制</h2>
<p>不建议在 Native 中直接调用大量具体的 JavaScript 方法：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">refreshUser()
</span></span><span class="line"><span class="cl">refreshTask()
</span></span><span class="line"><span class="cl">refreshOrder()
</span></span><span class="line"><span class="cl">refreshRoom()
</span></span><span class="line"><span class="cl">refreshStatus()</span></span></code></pre></div>
</div>
<p>随着业务增加，这种方式会导致 Native 与 H5 强耦合。</p>
<p>可以设计统一的事件通道：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Native WebSocket
</span></span><span class="line"><span class="cl">      ↓
</span></span><span class="line"><span class="cl">Native EventBus
</span></span><span class="line"><span class="cl">      ↓
</span></span><span class="line"><span class="cl">JS Bridge
</span></span><span class="line"><span class="cl">      ↓
</span></span><span class="line"><span class="cl">H5 EventBus
</span></span><span class="line"><span class="cl">      ↓
</span></span><span class="line"><span class="cl">业务模块
</span></span><span class="line"><span class="cl">      ↓
</span></span><span class="line"><span class="cl">HTTP API</span></span></code></pre></div>
</div>
<p>H5：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">javascript</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="nx">NativeBridge</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;task_changed&#34;</span><span class="p">,</span> <span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">refreshTask</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">taskId</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div>
</div>
<p>以后新增实时事件时，只需要增加事件类型：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">user_changed
</span></span><span class="line"><span class="cl">task_changed
</span></span><span class="line"><span class="cl">order_changed
</span></span><span class="line"><span class="cl">room_changed
</span></span><span class="line"><span class="cl">status_changed
</span></span><span class="line"><span class="cl">message_received</span></span></code></pre></div>
</div>
<hr>
<h2 id="六状态变化频繁时的处理">六、状态变化频繁时的处理</h2>
<p>如果一分钟内产生 3～5 次状态变化，通常不需要每次都立即发起 HTTP 请求。</p>
<p>可以在 H5 侧进行短时间防抖：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">javascript</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">timer</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">NativeBridge</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="s2">&#34;status_changed&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">clearTimeout</span><span class="p">(</span><span class="nx">timer</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">timer</span> <span class="o">=</span> <span class="nx">setTimeout</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">refreshStatus</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span> <span class="mi">100</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span></span></span></code></pre></div>
</div>
<p>如果短时间内连续收到多个通知：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">A
</span></span><span class="line"><span class="cl">B
</span></span><span class="line"><span class="cl">C</span></span></code></pre></div>
</div>
<p>可以合并成一次 API 请求。</p>
<hr>
<h2 id="七需要考虑请求乱序">七、需要考虑请求乱序</h2>
<p>实时状态同步还需要考虑 HTTP 请求返回顺序。</p>
<p>例如：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">状态 A → 请求 API A
</span></span><span class="line"><span class="cl">状态 B → 请求 API B
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">API B 先返回
</span></span><span class="line"><span class="cl">API A 后返回</span></span></code></pre></div>
</div>
<p>如果直接使用返回结果更新 UI，可能出现旧状态覆盖新状态的问题。</p>
<p>可以通过服务端版本号、时间戳或序列号解决：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">json</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;online&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;version&#34;</span><span class="p">:</span> <span class="mi">123</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<p>H5 只接受不低于当前版本的数据。</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">javascript</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">version</span> <span class="o">&gt;=</span> <span class="nx">currentVersion</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">updateUI</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
</div>
<hr>
<h2 id="八webview-生命周期">八、WebView 生命周期</h2>
<p>还需要考虑 WebView 当前是否已经准备完成。</p>
<p>例如：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">WebSocket 收到消息
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">WebView 尚未创建</span></span></code></pre></div>
</div>
<p>此时不能直接执行 JavaScript。</p>
<p>可以由 Native 缓存最新事件，在 H5 页面初始化完成后再发送。</p>
<p>典型流程：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">WebSocket
</span></span><span class="line"><span class="cl">    ↓
</span></span><span class="line"><span class="cl">Native 保存最新状态
</span></span><span class="line"><span class="cl">    ↓
</span></span><span class="line"><span class="cl">WebView 创建
</span></span><span class="line"><span class="cl">    ↓
</span></span><span class="line"><span class="cl">H5 Ready
</span></span><span class="line"><span class="cl">    ↓
</span></span><span class="line"><span class="cl">Bridge 建立
</span></span><span class="line"><span class="cl">    ↓
</span></span><span class="line"><span class="cl">发送最新状态事件</span></span></code></pre></div>
</div>
<p>这样可以避免 WebSocket 消息丢失。</p>
<hr>
<h2 id="九最终方案">九、最终方案</h2>
<p>综合当前场景，推荐采用：</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">text</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">                    Backend
</span></span><span class="line"><span class="cl">                 ┌────────────┐
</span></span><span class="line"><span class="cl">                 │ HTTP API   │
</span></span><span class="line"><span class="cl">                 │ WebSocket  │
</span></span><span class="line"><span class="cl">                 └─────┬──────┘
</span></span><span class="line"><span class="cl">                       │
</span></span><span class="line"><span class="cl">              ┌────────┴────────┐
</span></span><span class="line"><span class="cl">              │                 │
</span></span><span class="line"><span class="cl">          WebSocket             │ HTTP
</span></span><span class="line"><span class="cl">              │                 │
</span></span><span class="line"><span class="cl">              ▼                 ▼
</span></span><span class="line"><span class="cl">        ┌───────────┐     ┌───────────┐
</span></span><span class="line"><span class="cl">        │    APP    │     │    H5     │
</span></span><span class="line"><span class="cl">        │ WebSocket │     │ API Client│
</span></span><span class="line"><span class="cl">        └─────┬─────┘     └─────▲─────┘
</span></span><span class="line"><span class="cl">              │                  │
</span></span><span class="line"><span class="cl">              │ JS Bridge        │
</span></span><span class="line"><span class="cl">              ▼                  │
</span></span><span class="line"><span class="cl">        ┌───────────┐             │
</span></span><span class="line"><span class="cl">        │    H5     │─────────────┘
</span></span><span class="line"><span class="cl">        │ EventBus  │
</span></span><span class="line"><span class="cl">        └───────────┘</span></span></code></pre></div>
</div>
<p>核心职责划分：</p>
<table>
	<thead>
			<tr>
					<th>模块</th>
					<th>职责</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>APP WebSocket</td>
					<td>唯一实时连接</td>
			</tr>
			<tr>
					<td>JS Bridge</td>
					<td>Native 与 H5 之间传递事件</td>
			</tr>
			<tr>
					<td>H5 EventBus</td>
					<td>分发实时状态变化</td>
			</tr>
			<tr>
					<td>H5 HTTP API</td>
					<td>获取最新业务数据</td>
			</tr>
			<tr>
					<td>H5 UI</td>
					<td>根据最新数据更新页面</td>
			</tr>
	</tbody>
</table>
<p>最终形成：</p>
<blockquote>
<p><strong>WebSocket 负责通知，HTTP API 负责数据，JS Bridge 负责通信，H5 负责业务状态。</strong></p>
</blockquote>
<p>这种设计可以避免重复建立 WebSocket，同时最大程度复用现有 H5 API 架构，并降低 Android Native 与 H5 业务逻辑之间的耦合。</p>
]]></content:encoded></item></channel></rss>