ฟังก์ชั่น JavaScript สำหรับการโทรด้วย Android ใน WebView


249

ฉันพยายามที่จะเรียกบางjavascriptฟังก์ชั่นนั่งอยู่ในหน้าการทำงานภายในhtml android webviewค่อนข้างง่ายสิ่งที่รหัสพยายามทำด้านล่าง - จากแอพ android เรียกใช้javascriptฟังก์ชันด้วยข้อความทดสอบซึ่ง inturn เรียกฟังก์ชัน java กลับมาในแอพ android ที่แสดงข้อความทดสอบผ่านขนมปัง

javascriptฟังก์ชั่นรูปลักษณ์ที่ต้องการ:

function testEcho(message){
     window.JSInterface.doEchoTest(message);
}

จาก WebView ฉันได้ลองโทรjavascriptวิธีต่อไปนี้โดยไม่มีโชค:

myWebView.loadUrl("javascript:testEcho(Hello World!)");
mWebView.loadUrl("javascript:(function () { " + "testEcho(Hello World!);" + "})()");

ฉันเปิดใช้งานjavascriptบนWebView

myWebView.getSettings().setJavaScriptEnabled(true);
// register class containing methods to be exposed to JavaScript
myWebView.addJavascriptInterface(myJSInterface, "JSInterface"); 

และนี่คือJavaคลาส

public class JSInterface{

private WebView mAppView;
public JSInterface  (WebView appView) {
        this.mAppView = appView;
    }

    public void doEchoTest(String echo){
        Toast toast = Toast.makeText(mAppView.getContext(), echo, Toast.LENGTH_SHORT);
        toast.show();
    }
}

ฉันใช้เวลาไปกับการเที่ยวรอบ ๆ เพื่อดูว่าฉันทำอะไรผิด ตัวอย่างทั้งหมดที่ฉันได้พบใช้วิธีนี้ มีใครเห็นบางสิ่งผิดปกติที่นี่หรือไม่

แก้ไข: มีjavascriptไฟล์ภายนอกอื่น ๆ อีกหลายไฟล์ที่ถูกอ้างอิงและใช้ในhtmlพวกเขาจะเป็นปัญหาหรือไม่


13
รหัสที่คุณพยายามใช้คิดถึงเครื่องหมายคำพูดทางด้านจาวาสคริปต์
Paul de Vrieze

2
โปรดทราบว่าเนื่องจาก Android 4.2 คุณจำเป็นต้องใช้@JavascriptInterfaceมัณฑนากรบนวิธีการของ Java ที่คุณต้องการให้ WebView ผ่านทางส่วนต่อประสาน JavaScript
Fred

1
จากรหัสmyWebView.loadUrl("javascript:testEcho('Hello World!')");ฉันเข้าใจว่าคุณได้แนบไฟล์ html ไปกับ webview นั้นแล้ว คุณบอกฉันได้อย่างไรว่าคุณทำอย่างนั้น?
Tony_craft

คำตอบ:


195

ฉันพบว่าปัญหาคืออะไร: ไม่มีเครื่องหมายคำพูดในพารามิเตอร์ testEcho () นี่คือวิธีที่ฉันได้รับสายให้ทำงาน:

myWebView.loadUrl("javascript:testEcho('Hello World!')");

โปรดดูที่ลิงค์เหล่านี้stackoverflow.com/questions/9079374/…
kamal_tech_view

เป็นเรื่องดี แต่คุณจะส่งผ่านวัตถุ Java ไปยังฟังก์ชัน JavaScript เป็นอาร์กิวเมนต์ได้อย่างไร
Kovács Imre

1
@ KovácsImre String.Format ("javascript: testEcho ('% d', '% d', '% d')", 1, 2, 3); ฉันเดา
user457015

ขอบคุณฉันได้พบในเวลาเฉลี่ย
Kovács Imre

1
เริ่มต้นด้วย KitKat มีวิธีการประเมินผล
จาวาสคริปต์

117

จาก kitkat เป็นต้นไปใช้วิธี assessJavascript แทน loadUrl เพื่อเรียกใช้ฟังก์ชันจาวาสคริปต์เช่นด้านล่าง

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
        webView.evaluateJavascript("enable();", null);
    } else {
        webView.loadUrl("javascript:enable();");
    }

2
ทำไมต้องใช้ assessJavascript () แทน loadUrl ()
Kamran Bigdely

2
@kami loadUrl () จะโหลดหน้าเว็บและโทร onPageFinished () อีกครั้ง
ซัค

1
@Zach มันยังคงเป็นปัญหาสำหรับ pre-KitKat ใช่ไหม?
Vsevolod Ganin

2
@kami การเพิ่มคำตอบของ Zach ที่ประเมิน Javascript () จะเรียกใช้ JavaScript แบบอะซิงโครนัส ดังนั้นเราสามารถหลีกเลี่ยงการบล็อกเธรด UI โดยใช้ assessJavascript ()
ปราห์

loadUrlทำให้แป้นพิมพ์ซอฟต์แวร์ปรากฏขึ้นเมื่อฉันพยายามเติมค่าในช่องป้อนข้อมูล
Joshua

34
public void run(final String scriptSrc) { 
        webView.post(new Runnable() {
            @Override
            public void run() { 
                webView.loadUrl("javascript:" + scriptSrc); 
            }
        }); 
    }

และถ้ามันไม่ได้ผลให้ทำเธรดใหม่ (Runnaable ใหม่) (.. {ชอบ <ด้านบน> .. }). start ()
Radu Simionescu

26

ฉันสร้าง wrapper ที่ดีเพื่อเรียกใช้เมธอด JavaScript มันยังแสดงข้อผิดพลาดของ JavaScript ในบันทึก:

private void callJavaScript(String methodName, Object...params){
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("javascript:try{");
    stringBuilder.append(methodName);
    stringBuilder.append("(");
    for (int i = 0; i < params.length; i++) {
        Object param = params[i];
        if(param instanceof String){
            stringBuilder.append("'");
            stringBuilder.append(param.toString().replace("'", "\\'"));
            stringBuilder.append("'");
        }
        if(i < params.length - 1){
            stringBuilder.append(",");
        }
    }
    stringBuilder.append(")}catch(error){Android.onError(error.message);}");
    webView.loadUrl(stringBuilder.toString());
}

คุณต้องเพิ่มสิ่งนี้ด้วย:

private class WebViewInterface{

    @JavascriptInterface
    public void onError(String error){
        throw new Error(error);
    }
}

และเพิ่มอินเทอร์เฟซนี้ให้กับมุมมองเว็บของคุณ:

webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "AndroidErrorReporter");

ฟังก์ชั่นนี้อย่างไรก็ตามเป็นความคิดที่ดี แต่มีการใช้งานผิด สายนี้: final Object a = new Object(); callJavaScript(mBrowser, "alert", 1, true, "abc", a);จะให้ผลผลิตไปSyntaxErrorด้วยUnexpected tokenซึ่งไม่น่าแปลกใจเมื่อคุณจะดู js สร้างโทร:javascript:try{alert(,,'abc',,)}catch(error){console.error(error.message);}
ลูคัสฟา 'Severiaan' Grela

แม้แต่การโทรธรรมดาก็callJavaScript(mBrowser, "alert", "abc", "def");สร้างข้อผิดพลาดเนื่องจากมีเครื่องหมายคอมมาอยู่ท้ายเสมอ -1 จากฉัน
Lukasz 'Severiaan' Grela

1
@ Lukasz'Severiaan'Grela tnx ฉันแก้ไขมันแล้ว PS ครั้งต่อไปอย่าเกียจคร้านคุณสามารถแก้ไขได้ด้วยตัวเอง ... ;)
Ilya Gazman

16
สำหรับการอ้างอิงในอนาคต: อยู่ในเชิงบวก ชี้ให้เห็นข้อผิดพลาดในรหัสไม่ได้ขี้เกียจ มันยังคงเป็นข้อเสนอแนะที่เป็นประโยชน์
DW

ฉันเดาว่ามันจะไม่ทำงานหากพารามิเตอร์ใดมีตัวอักษร 'อยู่ในนั้น? ตัวอย่าง:callJavascript(mBrowser, "can't do it");
Kostanos

18

ใช่คุณมีข้อผิดพลาดทางไวยากรณ์ หากคุณต้องการได้รับข้อผิดพลาด Javascript และคำสั่งการพิมพ์ใน logcat ของคุณคุณต้องใช้onConsoleMessage(ConsoleMessage cm)วิธีใน WebChromeClient ของคุณ มันให้ร่องรอยสแต็คที่สมบูรณ์เช่นเว็บคอนโซล (องค์ประกอบการตรวจสอบ) นี่คือวิธีการ

public boolean onConsoleMessage(ConsoleMessage cm) 
    {
        Log.d("Message", cm.message() + " -- From line "
                             + cm.lineNumber() + " of "
                             + cm.sourceId() );
        return true;
    }

หลังจากการติดตั้งคุณจะได้รับข้อผิดพลาด Javascript และคำสั่งพิมพ์ ( console.log) บน logcat ของคุณ


2
ฉันเชื่อว่าอยู่ใน WebChromeClient ไม่ใช่ WebViewClient
Michael Levy

ใช่คุณถูกต้อง @MichaelLevy ขอบคุณที่ชี้นำฉันสำหรับความผิดพลาดนั้น ตอนนี้ฉันได้แก้ไขคำตอบของฉัน
Dinesh

1
ขอบคุณคำตอบของคุณคือสิ่งที่ฉันต้องการ ฉันขอแนะนำให้ผู้คนเขียนทับ onJsAlert () ใน WebChromeClient การรวมกันของการบันทึก javascript และการแจ้งเตือนจะมีประโยชน์จริง ๆ เมื่อทำการดีบัก Javascript ที่โฮสต์ใน webview
Michael Levy

13

การแก้ไขคำตอบ @Ilya_Gazman

    private void callJavaScript(WebView view, String methodName, Object...params){
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("javascript:try{");
        stringBuilder.append(methodName);
        stringBuilder.append("(");
        String separator = "";
        for (Object param : params) {               
            stringBuilder.append(separator);
            separator = ",";
            if(param instanceof String){
                stringBuilder.append("'");
            }
                stringBuilder.append(param.toString().replace("'", "\\'"));
            if(param instanceof String){
                stringBuilder.append("'");
            }

        }
        stringBuilder.append(")}catch(error){console.error(error.message);}");
        final String call = stringBuilder.toString();
        Log.i(TAG, "callJavaScript: call="+call);


        view.loadUrl(call);
    }

จะสร้างการโทร JS อย่างถูกต้องเช่น

callJavaScript(mBrowser, "alert", "abc", "def");
//javascript:try{alert('abc','def')}catch(error){console.error(error.message);}
callJavaScript(mBrowser, "alert", 1, true, "abc");
//javascript:try{alert(1,true,'abc')}catch(error){console.error(error.message);}

โปรดทราบว่าวัตถุจะไม่ถูกส่งผ่านอย่างถูกต้อง - แต่คุณสามารถทำให้เป็นอันดับก่อนที่จะผ่านเป็นอาร์กิวเมนต์

นอกจากนี้ฉันได้เปลี่ยนที่ข้อผิดพลาดไปฉันหันเหความสนใจไปที่บันทึกคอนโซลซึ่งสามารถฟังได้โดย:

    webView.setWebChromeClient(new CustomWebChromeClient());

และลูกค้า

class CustomWebChromeClient extends WebChromeClient {
    private static final String TAG = "CustomWebChromeClient";

    @Override
    public boolean onConsoleMessage(ConsoleMessage cm) {
        Log.d(TAG, String.format("%s @ %d: %s", cm.message(),
                cm.lineNumber(), cm.sourceId()));
        return true;
    }
}

ขอบคุณ เมธอดนี้จะหลบหนี 'ภายในตัวแปรหรือไม่? ในคำอื่น ๆ ก็จะทำงานร่วมกับ: callJavascript(mBrowser, "can't do it");?
Kostanos

1
เพิ่งแก้ไขคำตอบและเพิ่มการหลบหนีเป็น 'ตัวละคร
Kostanos

2

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <WebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

MainActivity.java


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.bluapp.androidview.R;

public class WebViewActivity3 extends AppCompatActivity {
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web_view3);
        webView = (WebView) findViewById(R.id.webView);
        webView.setWebViewClient(new WebViewClient());
        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl("file:///android_asset/webview1.html");


        webView.setWebViewClient(new WebViewClient(){
            public void onPageFinished(WebView view, String weburl){
                webView.loadUrl("javascript:testEcho('Javascript function in webview')");
            }
        });
    }
}

ไฟล์สินทรัพย์

<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.0//EN" "http://www.wapforum.org/DTD/xhtml-mobile10.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>WebView1</title>
<meta forua="true" http-equiv="Cache-Control" content="max-age=0"/>
</head>

<body style="background-color:#212121">
<script type="text/javascript">
function testEcho(p1){
document.write(p1);
}
</script>
</body>

</html>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.