Android WebView JS的注入

JavaScript在Web开发中非常有用,而现在越来越多的App界面也由Javascript来创建,我们需要解决一个问题:java与javaScript怎么进行交互呢?
例如,我们可以在html中创建一个按钮,为按钮事件添加一个界面。然后你可以使用html按钮跳转到另一个Activity中。

(1)本地Html文件

添加一个新的Assert文件夹

右键点击“ App ”module>new>folder>assert folder,然后将为您创建一个Assert Folder。

创建一个html文件

之后,通过右键单击Assert Folder>new >file创建一个html文件。将html文件命名为“sample.html”。然后按照下面的源代码添加html文件中的按钮并创建javascript界面​​。

<html>
<head>
    <style>
body{

}
input{

width: 300px;
padding:10px;

}
div#content{
padding:20px;

}
</style>
    <script type="text/javascript">
    function showToastA(toastmsg) {
        InterfaceName.showToast(toastmsg);
    }
 function navigateToAnotherActivityA() {
        InterfaceName.navigateToAnotherActivity();
    }
</script>
</head>
<body>
<center>
    <h3>Javascript bind to Android</h3>

    <div>
        <input type="button" value="Show Toast" onClick="showToastA('Message from Javascript')" /><br/><br/>
        <input type="button" value="Go to Another Activity" onClick="navigateToAnotherActivityA()" />
    </div>
</center>
</body>
</html>

创建第二个Activity

创建html文件后,转到项目中创建一个新的活动,所以稍后你将使用html按钮导航到这个Activity。
activity_main.xml:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.questdot.javascriptwebviewexample.MainActivity">

    <WebView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/webkit"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</RelativeLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView browser;
        browser=(WebView)findViewById(R.id.webkit);

        browser.getSettings().setJavaScriptEnabled(true);

        browser.addJavascriptInterface(new WebAppInterface(this), "InterfaceName");

        browser.loadUrl("file:///android_asset/sample.html");
    }

    public class WebAppInterface {
        Context mContext;

        WebAppInterface(Context c) {
            mContext = c;
        }

        @JavascriptInterface
        public void showToast(String toast) {
            Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
        }


        @JavascriptInterface
        public void navigateToAnotherActivity(){
            AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

            alertDialog.setTitle("Alert Message");

            alertDialog.setMessage("You want to Go another Activity?");

            alertDialog.setPositiveButton("YES",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {

                            Intent chnIntent = new Intent(MainActivity.this, SecondActivity.class);
                            startActivity(chnIntent);
                        }
                    });

            alertDialog.setNegativeButton("NO",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int which) {

                            dialog.cancel();
                        }
                    });
            // Showing Alert Message
            alertDialog.show();
        }
    }
}

addJavascriptInterface中的第二个参数“InterfaceName”应该与html文件中的相同,否则将报错。
还要注意的是4.2之前向webview注入的对象所暴露的接口toString没有注释语句@JavascriptInterface,而4.2及以后的则多了注释语句@JavascriptInterface
经过查官方文档所知,因为这个接口允许JavaScript 控制宿主应用程序,这是个很强大的特性,但同时,在4.2的版本前存在重大安全隐患,因为javascript 可以使用反射访问注入webview的Java对象的public fields,在一个包含不信任内容的WebView中使用这个方法,会允许攻击者去篡改宿主应用程序,使用宿主应用程序的权限执行java代码。因此4.2以后,任何为js暴露的接口,都需要加。

(2)动态网页

图片点击

我们还可以对网页中的图片添加点击事件,获得该图片的信息,比如链接等。
主要代码如下:

 // 注入js函数监听  
    private void addImageClickListner() {  
        // 这段js函数的功能就是,遍历所有的img结点,并添加onclick函数,函数的功能是在图片点击的时候调用本地java接口并传递url过去  
        contentWebView.loadUrl("javascript:(function(){" +  
        "var objs = document.getElementsByTagName(\"img\"); " +   
                "for(var i=0;i<objs.length;i++)  " +   
        "{"  
                + "    objs[i].onclick=function()  " +   
        "    {  "   
                + "        window.imagelistner.openImage(this.src);  " +   
        "    }  " +   
        "}" +   
        "})()");  
    }  
// js通信接口  
    public class JavascriptInterface {  

        private Context context;  

        public JavascriptInterface(Context context) {  
            this.context = context;  
        }  

        public void openImage(String img) {  
            System.out.println(img);  
            //  
            Intent intent = new Intent();  
            intent.putExtra("image", img);  
            intent.setClass(context, ShowWebImageActivity.class);  
            context.startActivity(intent);  
            System.out.println(img);  
        }  
    }  
// 监听  
    private class MyWebViewClient extends WebViewClient {  
        @Override  
        public boolean shouldOverrideUrlLoading(WebView view, String url) {  

            return super.shouldOverrideUrlLoading(view, url);  
        }  

        @Override  
        public void onPageFinished(WebView view, String url) {  

            view.getSettings().setJavaScriptEnabled(true);  

            super.onPageFinished(view, url);  
            // html加载完成之后,添加监听图片的点击js函数  
            addImageClickListner();  

        }  

        @Override  
        public void onPageStarted(WebView view, String url, Bitmap favicon) {  
            view.getSettings().setJavaScriptEnabled(true);  

            super.onPageStarted(view, url, favicon);  
        }  

        @Override  
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {  

            super.onReceivedError(view, errorCode, description, failingUrl);  

        }  
    }  
// 随便找了个带图片的网站  
        contentWebView.loadUrl("http://www.weim.me/12408.html");  
        // 添加js交互接口类,并起别名 imagelistner  
        contentWebView.addJavascriptInterface(new JavascriptInterface(this), "imagelistner");  
        contentWebView.setWebViewClient(new MyWebViewClient());  

以上内容转载自Android响应网页中的图片点击事件。

这种注入JS的方法基本可以说是放之四海而皆准的方法,我们还可以用此方法拦截按钮的点击事件。

按钮点击

我们以豆瓣登陆这个网页为例。

查看网页源代码,登陆按钮在这段代码中可以找到:

<form id="lzform" name="lzform" method="post" onsubmit="return validateForm(this);" action="https://accounts.douban.com/login">
  <div style="display:none;">
    <img src="https://www.douban.com/pics/blank.gif" onerror="document.lzform.action='https://accounts.douban.com/login'"/>
  </div>
  <input name="source" type="hidden" value="book"/>
    <input name="redir" type="hidden" value="https://book.douban.com/subject/1041482/"/>
  <div class="item-right">
    <a href="?redir=https://book.douban.com/subject/1041482/&amp;source=book&amp;login_type=sms">手机验证码登录</a>
  </div>
  <div class="item">
    <label>帐号</label>
    <input id="email" name="form_email" type="text" class="basic-input"
           maxlength="60" value="邮箱/手机号/用户名" tabindex="1"/>
  </div>
  <div class="item">
    <label>密码</label>
    <input id="password" name="form_password" type="password" class="basic-input" maxlength="20" tabindex="2"/>
  </div>
  <!-- tsYR99_fISw | 175.191.30.52 -->

  <div class="item">
    <label>&nbsp;</label>
    <p class="remember">
      <input type="checkbox" id="remember" name="remember" tabindex="4"/>
      <label for="remember" class="remember">下次自动登录</label>
      | <a href="https://accounts.douban.com/resetpassword">忘记密码了</a>
    </p>
  </div>
  <div class="item">
    <label>&nbsp;</label>
    <input type="submit" value="登录" name="login" class="btn-submit" tabindex="5"/>
  </div>




<div class="item item-3rd">
    <label>
    第三方登录:
    </label>
    <a target="_top" href="https://www.douban.com/accounts/connect/wechat/?from=book&amp;redir=https%3A//book.douban.com/subject/1041482/" class="item-wechat"><img src="https://img3.doubanio.com/f/accounts/1b6cc3ca91f78cf47f41eafa91fbcd4918ae239c/pics/connect_wechat.png" title="微信"></a>
    <a target="_top" href="https://www.douban.com/accounts/connect/sina_weibo/?from=book&amp;redir=https%3A//book.douban.com/subject/1041482/&amp;fallback=" class="item-weibo"><img src="https://img3.doubanio.com/f/accounts/e2f1d8c0ede93408b46cbbab4e613fb29ba94e35/pics/connect_sina_weibo.png" title="新浪微博"></a>
</div>

</form>

那么要怎么获取这个登录按钮的事件呢?关键代码如下:

//注入javascript到页面
webview1.setWebViewClient(new WebViewClient() {
    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);

        StringBuilder sb = new StringBuilder();
        //获得网页中所有form节点的标签数组,这里获得数组的第一个form节点
        //onsubmit 事件会在表单中的确认按钮被点击时发生。
        sb.append("document.getElementsByTagName('form')[0].onsubmit = function () {");
        //方法中的局部变量,用来存储用户名和密码
        sb.append("var objPWD, objAccount;var str = '';");
        //获得form节点下所有的input节点数组
        sb.append("var inputs = document.getElementsByTagName('input');");
        sb.append("for (var i = 0; i < inputs.length; i++) {");
        //获得输入的密码
        sb.append("if (inputs[i].type.toLowerCase() === 'password') {objPWD = inputs[i];}");
        //获得输入的用户名
        sb.append("else if (inputs[i].name.toLowerCase() === 'form_email') {objAccount = inputs[i];}");
        sb.append("}");
        sb.append("if (objAccount != null) {str += objAccount.value;}");
        sb.append("if (objPWD != null) { str += ' , ' + objPWD.value;}");
        sb.append("window.MYOBJECT.processHTML(str);");
        sb.append("return true;");
        sb.append("};");

        view.loadUrl("javascript:" + sb.toString());
    }

});

//创建一个由javascrip调用的类
class MyJavaScriptInterface
{
    @JavascriptInterface
    public void processHTML(String html)
    {
        //called by javascript
           Log.d("henrytest", html);

            AlertDialog.Builder builder = new AlertDialog.Builder(Main.this);
            builder.setTitle("AlertDialog from app")
                    .setMessage(html)
                    .setPositiveButton(android.R.string.ok,
                            new DialogInterface.OnClickListener() {

                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    // TODO Auto-generated method stub

                                }
                            })
                    .setCancelable(false).show();
    }
}
//为javascript注册接口
webview1.getSettings().setJavaScriptEnabled(true);
webview1.addJavascriptInterface(new MyJavaScriptInterface(), "MYOBJECT");

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值