android webview和 js 各种用法交互

前言

各位同学大家好 有段时间没有跟大家见面了, 具体多久我也不记得了,最近疫情也比较严重 大家要记得做好防护 我只希望大家都健健康康的能看到我为大家带来知识点的总结和分享。 那么废话不多说我们今天要讲的是 Android webview 和js的交互。

为什么要讲这个

提到Android webview和js交互 很多同学会说网上有很多文章 博客 烂大街, 对是没错 我也知道。因为我是在游戏公司上班 难免会出一些webview 加载h5游戏的需求 其实webview和安卓这边交互有很多点值得说一说。我们是可以好好总结然后 避免以后踩坑的 也可以应对大家在工作绝大多数的需求。

效果图

具体实现:

web端代码
xml 复制代码
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>WebView</title>
	<style type="text/css">
			body{
				background: yellow;
			}
			.btn{
				line-height: 40px;
				margin: 10px;
				background: white;
			}
		</style>
</head>
<body>
<h2>WebView </h2>
<div>
	<span>请输入要传递的值:</span> <input type="text" id="input"/>
</div>
<div id="btn">
	<span>点我</span>
</div>
<script type="text/javascript">
			<!--//  imoocLaucher.setValue('徐庆-&#45;&#45;');-->
              var btnEle=document.getElementById("btn");
			var inputEle=document.getElementById("input");
			btnEle.addEventListener("click",function(){
				var str=inputEle.value;
				if(window.imoocLaucher){
                  imoocLaucher.setValue(str);
				}else{
				 alert("imoocLaucher 不存在");	
				}
			});
			var remote =function(str){
				inputEle.value=str;
			}
		</script>
</body>
</html>

调用部分代码

我们定义的原生方法上面要加上 @JavascriptInterface 注解

java 复制代码
package com.example.myapplication.listener;

import android.util.Log;
import android.webkit.JavascriptInterface;


public class ImooclJsInterface {
    private static final String TAG = "ImooclJsInterface";
   private JsClallback jsClallback;
    public ImooclJsInterface(JsClallback jsClallback) {
        this.jsClallback = jsClallback;
    }

    /**
     * @param value
     * 方法名上面需要加上注解不然无法调用到
     *
     */
    @JavascriptInterface
    public void  setValue(String value){
        Log.e(TAG, "setValue:  value  -----> " +value);
       jsClallback.setTextValue(value);

    }
}

Android native代码

typescript 复制代码
package com.example.myapplication.ui;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;

import androidx.annotation.Nullable;
import com.example.myapplication.R;
import com.example.myapplication.listener.ImooclJsInterface;
import com.example.myapplication.listener.JsClallback;


public class WebActivity extends BaseActivity {
    private final String TAG = "WebActivity";
    private WebView mWebView;
    private String fileurl="file:///android_asset/index.html";
    private TextView textView;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_web);
        mWebView=findViewById(R.id.testwebview);
        textView=findViewById(R.id.test);

        initView();
    }
    @SuppressLint("JavascriptInterface")
    protected void initView() {
        WebSettings settings = mWebView.getSettings();
        settings.setDomStorageEnabled(true);
        settings.setJavaScriptEnabled(true);
        settings.setBlockNetworkImage(true);
        settings.setBlockNetworkImage(false);
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setSupportZoom(false);//是否可以缩放,默认true
        settings.setBuiltInZoomControls(false);//是否显示缩放按钮,默认false
        settings.setUseWideViewPort(true);//设置此属性,可任意比例缩放。大视图模式
        settings.setLoadWithOverviewMode(true);//和setUseWideViewPort(true)一起解决网页自适应问题
        settings.setAppCacheEnabled(true);//是否使用缓存
        settings.setDomStorageEnabled(true);//DOM Storage
        settings.setAllowFileAccessFromFileURLs(true);
        settings.setBuiltInZoomControls(true);
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setDefaultTextEncodingName("utf-8");
        settings.setJavaScriptEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);  //设置 缓存模式
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, true);
        mWebView.requestFocus();
        // android 5.0以上默认不支持混合调用http与https,需要设置WebSettings来兼容一下
        if (Build.VERSION.SDK_INT >=21) {
            mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
        }
        mWebView.addJavascriptInterface(new ImooclJsInterface(jsClallback), "imoocLaucher");
        mWebView.loadUrl(fileurl);
        mWebView.setWebViewClient(new WebViewClient() {
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                //  重写此方法表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边
                view.loadUrl(url);
                return false;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                view.loadUrl("javascript:window.local_obj.showSource('<head>'+" +
                        "document.getElementsByTagName('html')[0].innerHTML+'</head>');");
                super.onPageFinished(view, url);

            }
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
            }
        });
    }

    JsClallback jsClallback=new JsClallback() {
        @Override
        public void setTextValue(String value) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Log.e(TAG, "run: value   --- > "+value );
                    textView.setText("js调用安卓传过来的值为 : "+ value);
                }
            });
        }
    };
}

我看到效果在js里面调用原生安卓的方法 然后在原生安卓的这边textview控件上面展示效果 最后我们要刷ui 我们还是要切换到UI 线程操作 不然会报出线程阻塞的异常

原生安卓调用js

前端代码

xml 复制代码
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>WebView</title>
	<style type="text/css">
			body{
				background: yellow;
			}
			.btn{
				line-height: 40px;
				margin: 10px;
				background: white;
			}
		</style>
</head>
<body>
<h2>WebView </h2>
<div>
	<span>请输入要传递的值:</span> <input type="text" id="input"/>
</div>
<div id="btn">
	<span>点我</span>
</div>
<script type="text/javascript">
			<!--//  imoocLaucher.setValue('徐庆-&#45;&#45;');-->
              var btnEle=document.getElementById("btn");
			var inputEle=document.getElementById("input");
			btnEle.addEventListener("click",function(){
				var str=inputEle.value;
				if(window.imoocLaucher){
                  imoocLaucher.setValue(str);
				}else{
				 alert("imoocLaucher 不存在");
				}
			});
			var remote =function(str){
				inputEle.value=str;
			}
		</script>
</body>
</html>

客户端代码

typescript 复制代码
          runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        nativewebview.loadUrl("javascript:if(window.remote){window.remote('"+getedtext+"')}");

                    }
                });

我们通过loadurl 这个方法继续去加载js里面的方法即可

js调用 安卓打开相机和相册

前端代码

xml 复制代码
<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style type="text/css">
			p{
				font-size: 40px;
			}
		</style>
	</head>
	<body>
		

		<div class="conThree">
		                 <div class="conHead">调配设备照片</div>
		                 <div class="conThreeCon">
		                        <div id="preview"></div>
		                        <label class="input-group-btn">
		                            <input id="fileImage" type="file" size="30" name="fileselect[]" accept="image/jpeg,image/png,image/gif">
		                            <img src="../../../../img/Group.png" alt="">
		                        </label>
		                        <!-- <div class="upload_submit">
		                            <button type="button" id="fileSubmit" class="upload_submit_btn">确认上传图片</button>
		                        </div> -->
		                 </div>
		            </div>
		
	</body>
</html>

客户端代码

ini 复制代码
package com.example.myapplication.ui;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import android.widget.Toast;
import androidx.core.app.ActivityCompat;
import com.blankj.utilcode.util.ToastUtils;
import com.example.myapplication.AppManager;
import com.example.myapplication.Config;
import com.example.myapplication.permission.AppDialogManager;
import com.example.myapplication.permission.PermissionManager;
import com.example.myapplication.R;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;


public class MainActivity extends BaseActivity {
    private Context mContext= MainActivity.this;
    private WebView mWebView;
    private android.webkit.ValueCallback<Uri[]> mUploadCallbackAboveL;
    private android.webkit.ValueCallback<Uri> mUploadCallbackBelow;
    private Uri imageUri;
    private int REQUEST_CODE = 1234;
    private String fileurl="file:///android_asset/test.html";
    private String imageFilePath;
    private static final int REQUEST_PERMISSION = 0;
    private ProgressBar progressBar;




    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView=findViewById(R.id.mainwebview);
        progressBar=findViewById(R.id.main_progressbar);
        webviewToload(mWebView,fileurl);

    }


    @SuppressLint("JavascriptInterface")
    private void webviewToload(WebView mwebview, String url) {
        WebSettings settings = mwebview.getSettings();
        settings.setDomStorageEnabled(true);
        settings.setJavaScriptEnabled(true);
        settings.setBlockNetworkImage(true);
        settings.setBlockNetworkImage(false);
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        settings.setSupportZoom(false);//是否可以缩放,默认true
        settings.setBuiltInZoomControls(false);//是否显示缩放按钮,默认false
        settings.setUseWideViewPort(true);//设置此属性,可任意比例缩放。大视图模式
        settings.setLoadWithOverviewMode(true);//和setUseWideViewPort(true)一起解决网页自适应问题
        settings.setAppCacheEnabled(true);//是否使用缓存
        settings.setDomStorageEnabled(true);//DOM Storage
        settings.setAllowFileAccessFromFileURLs(true);
        settings.setBuiltInZoomControls(true);
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setDefaultTextEncodingName("utf-8");
        settings.setJavaScriptEnabled(true);
        settings.setCacheMode(WebSettings.LOAD_NO_CACHE);  //设置 缓存模式
        settings.setJavaScriptCanOpenWindowsAutomatically(true);
        CookieManager.getInstance().setAcceptThirdPartyCookies(mwebview, true);
        mwebview.requestFocus();
        // android 5.0以上默认不支持混合调用http与https,需要设置WebSettings来兼容一下
        if (Build.VERSION.SDK_INT >=21) {
            mwebview.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
        }
        mwebview.addJavascriptInterface(mContext, "local_obj");
        mwebview.setWebViewClient(new WebViewClient() {
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                //  重写此方法表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边
                view.loadUrl(url);
                return false;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
              /*  view.loadUrl("javascript:window.local_obj.showSource('<head>'+" +
                        "document.getElementsByTagName('html')[0].innerHTML+'</head>');");*/
                super.onPageFinished(view, url);

            }
            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                super.onPageStarted(view, url, favicon);
            }
        });
        mwebview.loadUrl(url);

        mwebview.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                if (newProgress == 100) {
                    progressBar.setVisibility(View.GONE);//加载完网页进度条消失
                } else {
                    progressBar.setProgress(newProgress);//设置进度值
                    progressBar.setVisibility(View.VISIBLE);//开始加载网页时显示进度条
                }
            }

            /**
             * 8(Android 2.2) <= API <= 10(Android 2.3)回调此方法
             */
            private void openFileChooser(android.webkit.ValueCallback<Uri> uploadMsg) {
                Log.e("WangJ", "运行方法 openFileChooser-1");
                // (2)该方法回调时说明版本API < 21,此时将结果赋值给 mUploadCallbackBelow,使之 != null
                mUploadCallbackBelow = uploadMsg;
                takePhoto();
            }

            /**
             * 11(Android 3.0) <= API <= 15(Android 4.0.3)回调此方法
             */
            public void openFileChooser(android.webkit.ValueCallback<Uri> uploadMsg, String acceptType) {
                Log.e("WangJ", "运行方法 openFileChooser-2 (acceptType: " + acceptType + ")");
                // 这里我们就不区分input的参数了,直接用拍照
                openFileChooser(uploadMsg);
            }

            /**
             * 16(Android 4.1.2) <= API <= 20(Android 4.4W.2)回调此方法
             */
            public void openFileChooser(android.webkit.ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
                Log.e("WangJ", "运行方法 openFileChooser-3 (acceptType: " + acceptType + "; capture: " + capture + ")");
                // 这里我们就不区分input的参数了,直接用拍照
                openFileChooser(uploadMsg);
            }

            /**
             * API >= 21(Android 5.0.1)回调此方法
             */
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> valueCallback, FileChooserParams fileChooserParams) {
                Log.e("WangJ", "运行方法 onShowFileChooser");
                // (1)该方法回调时说明版本API >= 21,此时将结果赋值给 mUploadCallbackAboveL,使之 != null
                mUploadCallbackAboveL = valueCallback;
                startPreviewWithPermission();
                //requestPermissionAndShotView();
                return true;
            }
        });

    }




    /**
     * 调用相机
     */
    private void takePhoto() {
        // 指定拍照存储位置的方式调起相机
        String filePath = Environment.getExternalStorageDirectory() + File.separator
                + Environment.DIRECTORY_PICTURES + File.separator;
        String fileName = "IMG_" + DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg";
        imageUri = Uri.fromFile(new File(filePath + fileName));
        imageFilePath=filePath + fileName;
        Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

        Intent Photo = new Intent(Intent.ACTION_PICK,
                android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

        Intent chooserIntent = Intent.createChooser(Photo, "Image Chooser");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});

        startActivityForResult(chooserIntent, REQUEST_CODE);
    }

    /**
     * Android API < 21(Android 5.0)版本的回调处理
     * @param resultCode 选取文件或拍照的返回码
     * @param data 选取文件或拍照的返回结果
     */
    private void chooseBelow(int resultCode, Intent data) {
        Log.e("WangJ", "返回调用方法--chooseBelow");

        if (RESULT_OK == resultCode) {
            updatePhotos();

            if (data != null) {
                // 这里是针对文件路径处理
                Uri uri = data.getData();
                if (uri != null) {
                    Log.e("WangJ", "系统返回URI:" + uri.toString());
                    mUploadCallbackBelow.onReceiveValue(uri);
                } else {
                    mUploadCallbackBelow.onReceiveValue(null);
                }
            } else {
                // 以指定图像存储路径的方式调起相机,成功后返回data为空
                Log.e("WangJ", "自定义结果:" + imageUri.toString());
              //  mUploadCallbackBelow.onReceiveValue(imageUri);

               // mUploadCallbackAboveL.onReceiveValue(new Uri[]{imageUri});

                File mFile = new File(imageFilePath);
                Uri mUri = null;
                try {
              /*      if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
                    }*/
                    mUri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(), mFile.getAbsolutePath(), mFile.getName(), mFile.getName()));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                mUploadCallbackAboveL.onReceiveValue(new Uri[]{mUri});
            }
        } else {
            mUploadCallbackBelow.onReceiveValue(null);
        }
        mUploadCallbackBelow = null;
    }

    /**
     * Android API >= 21(Android 5.0) 版本的回调处理
     * @param resultCode 选取文件或拍照的返回码
     * @param data 选取文件或拍照的返回结果
     */
    private void chooseAbove(int resultCode, Intent data) {
        Log.e("WangJ", "返回调用方法--chooseAbove");

        if (RESULT_OK == resultCode) {
            updatePhotos();

            if (data != null) {
                // 这里是针对从文件中选图片的处理
                Uri[] results;
                Uri uriData = data.getData();
                if (uriData != null) {
                    results = new Uri[]{uriData};
                    for (Uri uri : results) {
                        Log.e("WangJ", "系统返回URI:" + uri.toString());
                    }
                    mUploadCallbackAboveL.onReceiveValue(results);
                } else {
                    mUploadCallbackAboveL.onReceiveValue(null);
                }
            } else {
                Log.e("WangJ", "自定义结果:" + imageUri.toString());
               // mUploadCallbackAboveL.onReceiveValue(new Uri[]{imageUri});
               // mUploadCallbackAboveL.onReceiveValue(new Uri[]{imageUri});

                File mFile = new File(imageFilePath);
                Uri mUri = null;
                try {
              /*      if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
                    }*/
                    mUri = Uri.parse(MediaStore.Images.Media.insertImage(getContentResolver(), mFile.getAbsolutePath(), mFile.getName(), mFile.getName()));
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
                mUploadCallbackAboveL.onReceiveValue(new Uri[]{mUri});
            }
        } else {
            mUploadCallbackAboveL.onReceiveValue(null);
        }
        mUploadCallbackAboveL = null;
    }





    private void updatePhotos() {
        // 该广播即使多发(即选取照片成功时也发送)也没有关系,只是唤醒系统刷新媒体文件
        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        intent.setData(imageUri);
        sendBroadcast(intent);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE) {
            // 经过上边(1)、(2)两个赋值操作,此处即可根据其值是否为空来决定采用哪种处理方法
            if (mUploadCallbackBelow != null) {
                chooseBelow(resultCode, data);
            } else if (mUploadCallbackAboveL != null) {
                chooseAbove(resultCode, data);
            } else {
                Toast.makeText(this, "发生错误", Toast.LENGTH_SHORT).show();
            }
        }
    }
    private void startPreviewWithPermission() {
        PermissionManager.permission(MainActivity.this, new PermissionManager.OnPermissionGrantCallback() {
            @Override
            public void onGranted() {
               // ToastUtils.showShort("onGranted");
                takePhoto();
            }

            @Override
            public void onDenied() {
                ToastUtils.showShort("onDenied");
            }
        }, PermissionManager.CAMERA, PermissionManager.STORAGE);
    }
    //交互示例
    @JavascriptInterface
    public void sdkBack() {
        this.finish();
    }
}

最后总结

Android webview 和js 交互还有很多其他的我这边就不展开一一去讲了 后面需要我们在这个文章里面继续更新, webview 和js 交互主要是通过 @JavascriptInterface 这个接口才能达到通信的目的 访问相机和相册部分是需要客户端这边去申请权限 因为Android6.0是动态权限 所以我代码里面也出了下权限申请 。最后

相关推荐
御水流红叶41 分钟前
安卓加固脱壳
android·开发语言·python
智江鹏2 小时前
Android 之 网络通信(HTTP/TCP/UDP/JSON)
android
北有花开4 小时前
Android方法耗时监控插件:基于ASM字节码插桩的性能分析工具
android
whysqwhw4 小时前
React Native应用中实现原生组件与JavaScript组件的复杂交互
android
whysqwhw4 小时前
React Native 中调用 Android 自定义控件
android
往事如烟隔多年4 小时前
一加Ace5无法连接ColorOS助手解决(安卓设备ADB模式无法连接)
android·adb·手机·coloros
00后程序员张4 小时前
iOS软件性能监控实战指南 开发到上线的完整流程解析
android·ios·小程序·https·uni-app·iphone·webview
2401_837088504 小时前
Axios介绍
android·okhttp
一杯凉白开4 小时前
Compose实现点击防抖,给Modifier添加扩展函数(含扩展函数的原理)
android