<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>@JavascriptInterface &#8211; 記下來</title>
	<atom:link href="https://noter.tw/tag/javascriptinterface/feed/" rel="self" type="application/rss+xml" />
	<link>https://noter.tw</link>
	<description>一路上踩到的坑、遇到的問題，一點一滴記下來，希望能幫助到需要的人~</description>
	<lastBuildDate>Tue, 12 Oct 2021 01:08:55 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.3</generator>

<image>
	<url>https://noter.tw/wp-content/uploads/cropped-old-1130742_1920-1-32x32.jpg</url>
	<title>@JavascriptInterface &#8211; 記下來</title>
	<link>https://noter.tw</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Android 2.3 @JavascriptInterface Issue</title>
		<link>https://noter.tw/3202/android-2-3-javascriptinterface-issue/</link>
					<comments>https://noter.tw/3202/android-2-3-javascriptinterface-issue/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Fri, 28 Mar 2014 12:25:25 +0000</pubDate>
				<category><![CDATA[手機 App]]></category>
		<category><![CDATA[網頁前端]]></category>
		<category><![CDATA[addJavascriptInterface]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[@JavascriptInterface]]></category>
		<guid isPermaLink="false">http://wazai.net/?p=3202</guid>

					<description><![CDATA[<p>JavascriptInterface 這個問題相信 Google 一下就可以找到很多文章，雖然如此小蛙還是試了好久才成功，這篇文章記錄一下當時小蛙不成功的盲點在哪裡。上一篇文章「Android 與 &#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/3202/android-2-3-javascriptinterface-issue/" data-wpel-link="internal">Android 2.3 @JavascriptInterface Issue</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[
<p>JavascriptInterface 這個問題相信 Google 一下就可以找到很多文章，雖然如此小蛙還是試了好久才成功，這篇文章記錄一下當時小蛙不成功的盲點在哪裡。上一篇文章「<a href="https://noter.tw/3204/android-%e8%88%87-webview-%e4%b8%ad%e7%9a%84-javascript-%e7%9b%b8%e4%ba%92%e6%ba%9d%e9%80%9a/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Android 與 WebView 中的 Javascript 相互溝通</a>」記錄怎麼讓 Android 與 Javascript 相互溝通。</p>



<span id="more-3202"></span>



<p>小蛙在實作 WebView 載入的網頁與 Android 互動時，一直都以手邊的 Nexus 7 2013 來做測試，直到所有功能都已就緒，有一天突然想起之前好像有看到 Javascript 在 2.3.x 會出現錯誤的問題，於是開了 2.3 版本的 emulator 來做測試，由於小蛙實作的部分是從 Javascript 呼叫某一個 function 來實作開啟行事曆的功能，這邊發生了大概類似下面的這種錯誤(小蛙的錯誤訊息已經洗掉了，以下內容來字<a href="http://twigstechtips.blogspot.tw/2013/09/android-webviewaddjavascriptinterface.html" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">參考資料1<span class="wpel-icon wpel-image wpel-icon-6"></span></a>)</p>



<pre class="wp-block-preformatted withcode">JNI WARNING: jarray 0xb5d256f0 points to non-array object (Ljava/lang/String;)
"WebViewCoreThread" prio=5 tid=8 NATIVE
  | group="main" sCount=0 dsCount=0 obj=0xb5cfc348 self=0x8234e98
  | sysTid=2023 nice=0 sched=0/0 cgrp=[fopen-error:2] handle=136531904
  at android.webkit.WebViewCore.nativeTouchUp(Native Method)
  at android.webkit.WebViewCore.nativeTouchUp(Native Method)
  at android.webkit.WebViewCore.access$3300(WebViewCore.java:53)
  at android.webkit.WebViewCore$EventHub$1.handleMessage(WebViewCore.java:1162)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:130)
  at android.webkit.WebViewCore$WebCoreThread.run(WebViewCore.java:633)
  at java.lang.Thread.run(Thread.java:1019)
VM aborting</pre>



<p>對於這個問題，<a href="http://twigstechtips.blogspot.tw/2013/09/android-webviewaddjavascriptinterface.html" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">參考資料1 <span class="wpel-icon wpel-image wpel-icon-6"></span></a>的作者將 JavascriptInterface 整理成一個比較完整的 solution，不過因為教學文章「漏漏長」，所以小蛙後來沒有採用這個方案，雖然方案不同，不過解決這個問題的邏輯是差不多的。</p>



<p>小蛙後來使用了<a href="http://fred-zone.blogspot.tw/2011/12/android-23x-javascript-interface.html" target="_blank" rel="noreferrer noopener nofollow external" data-wpel-link="external" class="wpel-icon-right">參考資料2<span class="wpel-icon wpel-image wpel-icon-6"></span></a>的方法來解決，這邊大概記錄一下作法 (不知道怎麼讓 Android 與 Javascript 互通的話可參考「<a href="https://noter.tw/3204/android-%e8%88%87-webview-%e4%b8%ad%e7%9a%84-javascript-%e7%9b%b8%e4%ba%92%e6%ba%9d%e9%80%9a/" target="_blank" rel="noreferrer noopener" data-wpel-link="internal">Android 與 WebView 中的 Javascript 相互溝通 @ 蛙齋</a>」)：</p>



<p><strong>檢查 Android 版本</strong></p>



<p>設定參數來記錄 Android 版本是否為 2.3.x</p>



<pre class="wp-block-preformatted withcode">private static JavaScriptInterface JSInterface;
// 設置 javascriptInterfaceBroken 來判斷是否為 2.3.x
private static boolean javascriptInterfaceBroken = false;
public static WebView wv;
try {
        if (Build.VERSION.RELEASE.contains("2.3.")) {
                javascriptInterfaceBroken = true;
        }
} catch (Exception e) { }
JSInterface = new JavaScriptInterface(getActivity());</pre>



<p><strong>依照版本設定對應動作</strong></p>



<pre class="wp-block-preformatted withcode">if(!javascriptInterfaceBroken){
    // 非 2.3.x 版本，正常執行
    wv.addJavascriptInterface(JSIterface, "JSInterface");
    ...
}else{
    // 2.3.x 版本
    wv.setWebViewClient(new WebViewClient(){
        @Override
        public void onPageFinished(WebView view, String url){
        super.onPageFinished(view, url);
        String handleGingerbreadStupidity =
                "javascript:function addToNotify(title, url, start, end) { window.location='http://JSInterface:addToNotify:'+(title)+'◎'+(url)+'◎'+(start)+'◎'+(end); }; "
                + "javascript: function handler() { this.addToNotify=addToNotify }; "
                + "javascript: var JSInterface = new handler();";
                view.loadUrl(handleGingerbreadStupidity);
        }
        @Override
        public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
            if (url.contains("JSInterface:addToNotify")) {
                // 在這邊可用 regular expression 把剛剛組出來的 url 解回
                                // 須注意如果有中文的話，字串要先從 ISO-8859-1 轉回 UTF-8
                String nUrl = new String(url.getBytes("ISO-8859-1"), "UTF-8");
                String title = "title";
                String desc = "desc";
                String start = "start";
                String end = "end";
                try {
                    Method sMethod = JSInterface.getClass().getMethod("addToNotify", new Class[] { String.class, String.class, String.class, String.class });
                    sMethod.invoke(JSInterface, title, desc, start, end);
                } catch (SecurityException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
            return true;
        }
    });
}</pre>



<p>上述程式碼中有幾個部份小蛙提出來記錄：</p>



<p>handleGingerbreadStupidity 字串中的 addToNotify 改成自己的方法名稱；JSInterface 改成自己的 JavaScriptInterface 名稱；後面參數有幾個都用「:」連接，因為小蛙的參數中有冒號會跟原本的冒號混淆，導致要拆出字串的時候很麻煩，因此這邊改成用「◎」來區隔。</p>



<p>shouldOverrideUrlLoading 會接收到從 onPageFinished 過來的 url，接著判斷如果包含剛剛我們自己組合的字串的話，就使用 reflection 的方式來呼叫對應方法。JSInterface.getClass().getMethod 中的 JSInterface 是開頭宣告的 JavaScriptInterface 的物件名稱，getMethod 第一個參數為呼叫的方法名稱，第二個參數是傳入的參數型別，都設定好之後就用 sMethod.invoke 呼叫該方法並傳入需要的物件及參數。</p>



<p>記錄的有點亂，小蛙當時卡在 onPagefinished 中傳參數的部分，因為內容包含了冒號導致發生一些問題；再來是 getMethod 方法不太會用，卡了一段時間 &#8230;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/3202/android-2-3-javascriptinterface-issue/" data-wpel-link="internal">Android 2.3 @JavascriptInterface Issue</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/3202/android-2-3-javascriptinterface-issue/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Android 與 WebView 中的 Javascript 相互溝通</title>
		<link>https://noter.tw/3204/android-%e8%88%87-webview-%e4%b8%ad%e7%9a%84-javascript-%e7%9b%b8%e4%ba%92%e6%ba%9d%e9%80%9a/</link>
					<comments>https://noter.tw/3204/android-%e8%88%87-webview-%e4%b8%ad%e7%9a%84-javascript-%e7%9b%b8%e4%ba%92%e6%ba%9d%e9%80%9a/#respond</comments>
		
		<dc:creator><![CDATA[黃小蛙]]></dc:creator>
		<pubDate>Fri, 28 Mar 2014 12:25:19 +0000</pubDate>
				<category><![CDATA[手機 App]]></category>
		<category><![CDATA[網頁前端]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[@JavascriptInterface]]></category>
		<category><![CDATA[addJavascriptInterface]]></category>
		<category><![CDATA[Uncaught TypeError: Object [object Object] has no method]]></category>
		<guid isPermaLink="false">http://wazai.net/?p=3204</guid>

					<description><![CDATA[<p>最近的一個 app 需要讓 Android 可以呼叫 WebView 中的 Javascript，並且按下 WebView 中的按鈕時，也可以呼叫 Android 內部的方法，這篇文章記錄一下大概的做&#46;&#46;&#46;</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/3204/android-%e8%88%87-webview-%e4%b8%ad%e7%9a%84-javascript-%e7%9b%b8%e4%ba%92%e6%ba%9d%e9%80%9a/" data-wpel-link="internal">Android 與 WebView 中的 Javascript 相互溝通</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></description>
										<content:encoded><![CDATA[<p>最近的一個 app 需要讓 Android 可以呼叫 WebView 中的 Javascript，並且按下 WebView 中的按鈕時，也可以呼叫 Android 內部的方法，這篇文章記錄一下大概的做法，免得下次遇到又忘記 &#8230;<br />
<span id="more-3204"></span><br />
這個 app 需要做到的功能很簡單，在 Android 端希望可以點選「字體放大」、「字體縮小」的按鈕來呼叫 WebView 中的 Javascript；並且點選 WebView 中的行事曆按鈕後，可以開啟 Android 內建的 Google 行事曆。</p>
<ul>
<li>
<h5>Android 呼叫 WebView 中的 Javascript</h5>
</li>
</ul>
<p>透過 Android 本身的按鈕來達到呼叫 Javascript 的功能很簡單，以小蛙的例子是用 fragment 中放置一個 WebView</p>
<div>
<div id="highlighter_456521" class="syntaxhighlighter  java">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="gutter">
<div class="line number1 index0 alt2">01</div>
<div class="line number2 index1 alt1">02</div>
<div class="line number3 index2 alt2">03</div>
<div class="line number4 index3 alt1">04</div>
<div class="line number5 index4 alt2">05</div>
<div class="line number6 index5 alt1">06</div>
<div class="line number7 index6 alt2">07</div>
<div class="line number8 index7 alt1">08</div>
<div class="line number9 index8 alt2">09</div>
<div class="line number10 index9 alt1">10</div>
<div class="line number11 index10 alt2">11</div>
<div class="line number12 index11 alt1">12</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2"><code class="java comments">// 取得 WebView 物件</code></div>
<div class="line number2 index1 alt1"><code class="java plain">WebView wv = (WebView) v.findViewById(R.id.webView1);</code></div>
<div class="line number3 index2 alt2"><code class="java comments">// 設定此 WebView 支援 Javascript</code></div>
<div class="line number4 index3 alt1"><code class="java plain">wv.getSettings().setJavaScriptEnabled(</code><code class="java keyword">true</code><code class="java plain">);</code></div>
<div class="line number5 index4 alt2"><code class="java comments">// 取得按鈕物件</code></div>
<div class="line number6 index5 alt1"><code class="java plain">Button b = (Button) v.findViewById(R.id.button1);</code></div>
<div class="line number7 index6 alt2"><code class="java plain">b.setOnClickListener(</code><code class="java keyword">new</code> <code class="java plain">View.OnClickListener() {</code></div>
<div class="line number8 index7 alt1"><code class="java spaces">    </code><code class="java color1">@Override</code></div>
<div class="line number9 index8 alt2"><code class="java spaces">    </code><code class="java keyword">public</code> <code class="java keyword">void</code> <code class="java plain">onClick(View v) {</code></div>
<div class="line number10 index9 alt1"><code class="java spaces">                </code><code class="java plain">wv.loadUrl(</code><code class="java string">"javascript:fontSize('+')"</code><code class="java plain">);</code></div>
<div class="line number11 index10 alt2"><code class="java spaces">    </code><code class="java plain">}</code></div>
<div class="line number12 index11 alt1"><code class="java plain">});</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>上面的程式碼只寫出個大概，取得 WebView 後設定支援 Javascript，並且設定按鈕來執行 Javascript，上面例子小蛙 WebView 中有一個字體放大的 Javascript 叫做 fontSize(&#8216;+&#8217;)，因此直接使用 wv.loadUrl(&#8216;要呼叫的 Javascript&#8217;); 即可！</p>
<ul>
<li>
<h5>從 WebView 呼叫 Android Function</h5>
</li>
</ul>
<p>這個部分就比較複雜一些些，首先必須先建立一個 JavascriptInterface 物件，設定好要執行的相對應功能。例如：</p>
<div>
<div id="highlighter_626920" class="syntaxhighlighter  java">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="gutter">
<div class="line number1 index0 alt2">01</div>
<div class="line number2 index1 alt1">02</div>
<div class="line number3 index2 alt2">03</div>
<div class="line number4 index3 alt1">04</div>
<div class="line number5 index4 alt2">05</div>
<div class="line number6 index5 alt1">06</div>
<div class="line number7 index6 alt2">07</div>
<div class="line number8 index7 alt1">08</div>
<div class="line number9 index8 alt2">09</div>
<div class="line number10 index9 alt1">10</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2"><code class="java keyword">public</code> <code class="java keyword">class</code> <code class="java plain">JavaScriptInterface {</code></div>
<div class="line number2 index1 alt1"><code class="java spaces">       </code><code class="java keyword">private</code> <code class="java plain">Activity activity;</code></div>
<div class="line number3 index2 alt2"><code class="java spaces">       </code><code class="java keyword">public</code> <code class="java plain">JavaScriptInterface(Activity activiy) {</code></div>
<div class="line number4 index3 alt1"><code class="java spaces">               </code><code class="java keyword">this</code><code class="java plain">.activity = activiy;</code></div>
<div class="line number5 index4 alt2"><code class="java spaces">       </code><code class="java plain">}</code></div>
<div class="line number6 index5 alt1"><code class="java spaces">       </code><code class="java color1">@JavascriptInterface</code></div>
<div class="line number7 index6 alt2"><code class="java spaces">       </code><code class="java keyword">public</code> <code class="java keyword">void</code> <code class="java plain">showToast(){</code></div>
<div class="line number8 index7 alt1"><code class="java spaces">               </code><code class="java plain">Toast.makeText(activity, </code><code class="java string">"我被從WebView呼叫了"</code><code class="java plain">, Toast.LENGTH_SHORT).show();</code></div>
<div class="line number9 index8 alt2"><code class="java spaces">       </code><code class="java plain">}</code></div>
<div class="line number10 index9 alt1"><code class="java plain">}</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>這邊要注意的是如果 targetSdkVersion 設定為 17 以上，就必須在每個可以被 Javascript 呼叫的方法前面加上 @JavascriptInterface，否則會出現以下錯誤</p>
<div>
<div id="highlighter_751180" class="syntaxhighlighter  java">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="gutter">
<div class="line number1 index0 alt2">1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2"><code class="java plain">Uncaught TypeError: Object [object Object] has no method </code><code class="java string">'showToast'</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>將設定好的 JavaScriptInterface 設定給 WebView 物件</p>
<div>
<div id="highlighter_558003" class="syntaxhighlighter  java">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="gutter">
<div class="line number1 index0 alt2">1</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2"><code class="java plain">wv.addJavascriptInterface(</code><code class="java keyword">new</code> <code class="java plain">JavaScriptInterface(getActivity()), </code><code class="java string">"JSInterface"</code><code class="java plain">);</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>後面的參數名字可以自己取，小蛙這邊使用 JSInterface，這個名字是讓 Javascript 可以知道透過哪個物件來呼叫。Android 這邊完成後接著在 html 中加入相對應的動作，例如：</p>
<div>
<div id="highlighter_659373" class="syntaxhighlighter  html">
<table border="0" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td class="gutter">
<div class="line number1 index0 alt2">1</div>
<div class="line number2 index1 alt1">2</div>
<div class="line number3 index2 alt2">3</div>
<div class="line number4 index3 alt1">4</div>
<div class="line number5 index4 alt2">5</div>
<div class="line number6 index5 alt1">6</div>
<div class="line number7 index6 alt2">7</div>
</td>
<td class="code">
<div class="container">
<div class="line number1 index0 alt2"><code class="html plain">&lt;</code><code class="html keyword">input</code> <code class="html color1">type</code><code class="html plain">=</code><code class="html string">'button'</code> <code class="html color1">value</code><code class="html plain">=</code><code class="html string">'按我'</code> <code class="html color1">onclick</code><code class="html plain">=</code><code class="html string">'javascript:showToast();'</code><code class="html plain">&gt;</code></div>
<div class="line number2 index1 alt1"><code class="html plain">&lt;</code><code class="html keyword">script</code> <code class="html color1">type</code><code class="html plain">=</code><code class="html string">'text/javascript'</code><code class="html plain">&gt;</code></div>
<div class="line number3 index2 alt2"><code class="html plain">function showToast(){</code></div>
<div class="line number4 index3 alt1"><code class="html spaces">    </code><code class="html plain">if(JSInterface)</code></div>
<div class="line number5 index4 alt2"><code class="html spaces">        </code><code class="html plain">JSInterface.showToast();</code></div>
<div class="line number6 index5 alt1"><code class="html plain">}</code></div>
<div class="line number7 index6 alt2"><code class="html plain">&lt;/</code><code class="html keyword">script</code><code class="html plain">&gt;</code></div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<p>設定一個 button 讓使用者點選時執行 function showToast()，而 showToast 方法中倘若存在 JSInterface 物件 (這個物件是上面 wv.addJavascriptInterface 時設定的)，就執行 JSInterface.showToast() 的方法 (這一步就會對應到我們自訂的 JavaScriptInterface 類別中的 public void showToast())，在 app 中顯示 Toast 訊息。<br />
到這邊，從 Android 呼叫 Javascript 以及從 Javascript 呼叫 Android 方法都已經完成，但是在 Android 2.3.x 版本中會發生一些錯誤，造成當機無法正確執行，解決方法請參考 <a href="https://noter.tw/3202/android-2-3-javascriptinterface-issue/" target="_blank" rel="noopener noreferrer" data-wpel-link="internal">Android 2.3 @JavascriptInterface Issue</a>。</p>
<p>這篇文章 <a rel="nofollow" href="https://noter.tw/3204/android-%e8%88%87-webview-%e4%b8%ad%e7%9a%84-javascript-%e7%9b%b8%e4%ba%92%e6%ba%9d%e9%80%9a/" data-wpel-link="internal">Android 與 WebView 中的 Javascript 相互溝通</a> 最早出現於 <a rel="nofollow" href="https://noter.tw" data-wpel-link="internal">記下來</a>。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://noter.tw/3204/android-%e8%88%87-webview-%e4%b8%ad%e7%9a%84-javascript-%e7%9b%b8%e4%ba%92%e6%ba%9d%e9%80%9a/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
