在Android 帝国中,使用WebView 如何和HTML5 里面的JS 进行交互呢?

这篇博文我们一起来学习下两个小技巧

  • 在一个Android 应用程序中,使用Webview 接受处理JS 发来的消息,alert 弹出信息
  • 在一个HTML5 页面点击按钮发送一个消息到Webview 后用Toast控件处理这个消息

1.AndroidManifest.xml 配置如下

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.xingyun.webviewwithjssample">    <uses-permission android:name="android.permission.INTERNET"/>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:usesCleartextTraffic="true"        android:theme="@style/AppTheme">        <activity android:name=".MyWebViewActivity">activity>        <activity android:name=".MainActivity">            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            intent-filter>        activity>    application>manifest>

Tips:
值得注意的是,我们这里配置了两个Activity.

  • MainActivity
  • MyWebViewActivity

    然后这个界面上两个按钮都是WebView
  • 第一个按钮点击后发送一个字符串给android,然后用Toast 弹出来这个消息。
  • 第二个按钮点击后使用JS中的alert 弹出一个message.
    其次,还配置了三个权限,分别是网络权限,读取和写入存储卷。
    <uses-permission android:name="android.permission.INTERNET"/>    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

然后我们写下这两个页面的布局

activity_main.xml

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MainActivity">    <Button        android:id="@+id/clickMeBtn"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Click Me"        android:background="@color/colorPrimary"        android:textColor="@color/colorWhite"        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toTopOf="parent" />androidx.constraintlayout.widget.ConstraintLayout>

activity_my_web_view.xml

<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    tools:context=".MyWebViewActivity">    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical">        app:layout_constraintBottom_toBottomOf="parent"        app:layout_constraintLeft_toLeftOf="parent"        app:layout_constraintRight_toRightOf="parent"        app:layout_constraintTop_toTopOf="parent"        >        <WebView            android:id="@+id/myWebView"            android:layout_width="match_parent"            android:layout_height="match_parent"            >        WebView>    LinearLayout>androidx.constraintlayout.widget.ConstraintLayout>

然后编写MainActivity.java

import androidx.appcompat.app.AlertDialog;import androidx.appcompat.app.AppCompatActivity;import androidx.core.app.ActivityCompat;import androidx.core.content.ContextCompat;import android.Manifest;import android.content.DialogInterface;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.provider.Settings;import android.view.View;import android.widget.Button;import android.widget.Toast;import static androidx.core.content.PermissionChecker.PERMISSION_GRANTED;public class MainActivity extends AppCompatActivity {    Button button;    private static final int NOT_NOTICE = 2;//如果勾选了不再询问    private AlertDialog alertDialog;    private AlertDialog mDialog;    String[] permissionArray={Manifest.permission.INTERNET,Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        button=findViewById(R.id.clickMeBtn);        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                myRequetPermission();            }        });    }    private void myRequetPermission() {        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {            ActivityCompat.requestPermissions(this, permissionArray, 1);        }else {            Toast.makeText(this,"您已经申请了权限!",Toast.LENGTH_SHORT).show();            startActivity(new Intent(MainActivity.this,MyWebViewActivity.class));        }    }    @Override    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);        if (requestCode == 1) {            for (int i = 0; i < permissions.length; i++) {                if (grantResults[i] == PERMISSION_GRANTED) {//选择了“始终允许”                    Toast.makeText(this, "" + "权限" + permissions[i] + "申请成功", Toast.LENGTH_SHORT).show();                    startActivity(new Intent(MainActivity.this,MyWebViewActivity.class));                } else {                    if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])){//用户选择了禁止不再询问                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);                        builder.setTitle("permission")                                .setMessage("点击允许才可以使用我们的app哦")                                .setPositiveButton("去允许", new DialogInterface.OnClickListener() {                                    public void onClick(DialogInterface dialog, int id) {                                        if (mDialog != null && mDialog.isShowing()) {                                            mDialog.dismiss();                                        }                                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);                                        Uri uri = Uri.fromParts("package", getPackageName(), null);//注意就是"package",不用改成自己的包名                                        intent.setData(uri);                                        startActivityForResult(intent, NOT_NOTICE);                                    }                                });                        mDialog = builder.create();                        mDialog.setCanceledOnTouchOutside(false);                        mDialog.show();                    }else {//选择禁止                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);                        builder.setTitle("permission")                                .setMessage("点击允许才可以使用我们的app哦")                                .setPositiveButton("去允许", new DialogInterface.OnClickListener() {                                    public void onClick(DialogInterface dialog, int id) {                                        if (alertDialog != null && alertDialog.isShowing()) {                                            alertDialog.dismiss();                                        }                                        ActivityCompat.requestPermissions(MainActivity.this, permissionArray, 1);                                    }                                });                        alertDialog = builder.create();                        alertDialog.setCanceledOnTouchOutside(false);                        alertDialog.show();                    }                }            }        }    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        if(requestCode==NOT_NOTICE){            myRequetPermission();//由于不知道是否选择了允许所以需要再次判断        }    }}

代码很简单,主要配置了下动态申请了网络权限,读写权限,以及编写了中间按钮的点击跳转页面事件。

然后就是MyWebViewActivity.java

import android.os.Bundle;import android.util.Log;import android.webkit.JavascriptInterface;import android.webkit.JsResult;import android.webkit.WebChromeClient;import android.webkit.WebView;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;public class MyWebViewActivity extends AppCompatActivity {    private WebView myWebView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_my_web_view);        myWebView=findViewById(R.id.myWebView);        myWebView.getSettings().setAllowFileAccess(true);        myWebView.getSettings().setAllowContentAccess(true);        myWebView.getSettings().setJavaScriptEnabled(true);        myWebView.getSettings().setAllowFileAccessFromFileURLs(true);        myWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);        myWebView.getSettings().setSupportZoom(true);        myWebView.addJavascriptInterface(new JSInterface(),"test");        myWebView.setWebChromeClient(new WebChromeClient(){            @Override            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {                Log.d("swallow","onJsAlert:"+message);                return super.onJsAlert(view, url, message, result);            }        });        myWebView.loadUrl("file:///android_asset/index.html");    }    //https://www.jianshu.com/p/345f4d8a5cfa    class JSInterface extends Object{        @JavascriptInterface        public void sendToAndroid(String message){            Log.i("swallow",message);            Toast.makeText(MyWebViewActivity.this,message,Toast.LENGTH_LONG).show();        }    }}

代码也很简单,主要有两部分:

  • 第一部分:监控HTML5页面弹窗

关键点: 如果在webview 中想要监控HTML5中的弹窗事件,那么就重写myWebView.setWebChromeClient
中的onJsAlert 方法。 这样操作能处理HTML5 中出现的各种弹窗信息

  • 第二部分:让HTML5页面JS中传递一个message 给Webview 然后用Toast 弹出来。

关键点

  • myWebView.addJavascriptInterface(new JSInterface(),“test”); 相当于在JS中创建了一个test对象。
  • 然后在JS中就可以调用 test.sendToAndroid(“js调用了android中的sendToAndroid方法”);
  • 值得注意的是,必须使用@JavascriptInterface注解绑定了sendToAndroid方法,然后在JS中使用才有效。这是为了解决一个JS
    漏洞。
  • 详情参考 你不知道的 Android WebView 使用漏洞

我们需要一个HTML页面放到assets文件夹

然后目录结构成这样

index.html 必须绑定

<html><head>    <meta charset="UTF-8"/>head><script>        function callAndroid(){            test.sendToAndroid("js调用了android中的sendToAndroid方法");        }        function callJS(){             alert("alert message From JS");        }    script><body><div align="center">    <p>        <input type="button" value="HTML上点击按钮显示Android Toast" onclick="callAndroid()"/>    p>    <p>        <input type="button" value="HTML上点击按钮显示JS弹窗" onclick="callJS()"/>    p>div>body>html>

源码下载

源码下载

参考链接

  • 最全面总结 Android WebView与 JS 的交互方式
  • 你不知道的 Android WebView 使用漏洞
  • android webview获取js中的alert、confirm、和prompt,以及获取其值

更多相关文章

  1. 在Android中使用Application保存全局变量
  2. Android自定义控件——仿ios开关按钮
  3. Android(安卓)实现记住用户名和密码的功能
  4. android 自定义带关闭按钮的dialog
  5. Android(安卓)-- Button(按钮)的几种监听方式
  6. 使用Android内部的DownloadProvider下载文件,并获取cache权限
  7. 箭头函数的基础使用
  8. NPM 和webpack 的基础使用
  9. Python list sort方法的具体使用

随机推荐

  1. Android(安卓)Touch Calibration解决方案
  2. Android的消息循环机制 Looper Handler类
  3. Android零基础入门第8节:HelloWorld,我的第
  4. Android(安卓)UI Operation in Thread
  5. 开发规范:《阿里巴巴Android开发手册》之
  6. Android上在两个Activity之间传递Bitmap
  7. Android(安卓)Studio占用太多内存
  8. Android(安卓)图片加载Bit地图 OOM异常解
  9. Android文档阅读01—Android是什么?
  10. Android(安卓)Low memory killer by 永远