Android中原生WebView与HTML5 里的 JS交互
16lz
2022-06-25
在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,以及获取其值
更多相关文章
- 在Android中使用Application保存全局变量
- Android自定义控件——仿ios开关按钮
- Android(安卓)实现记住用户名和密码的功能
- android 自定义带关闭按钮的dialog
- Android(安卓)-- Button(按钮)的几种监听方式
- 使用Android内部的DownloadProvider下载文件,并获取cache权限
- 箭头函数的基础使用
- NPM 和webpack 的基础使用
- Python list sort方法的具体使用