前言
各位同学大家好 有段时间没有跟大家见面了, 具体多久我也不记得了,最近疫情也比较严重 大家要记得做好防护 我只希望大家都健健康康的能看到我为大家带来知识点的总结和分享。 那么废话不多说我们今天要讲的是 Android webview 和js的交互。
为什么要讲这个
提到Android webview和js交互 很多同学会说网上有很多文章 博客 烂大街, 对是没错 我也知道。因为我是在游戏公司上班 难免会出一些webview 加载h5游戏的需求 其实webview和安卓这边交互有很多点值得说一说。我们是可以好好总结然后 避免以后踩坑的 也可以应对大家在工作绝大多数的需求。
效果图
具体实现:
-
web端代码
WebView
WebView
请输入要传递的值:
点我
调用部分代码
我们定义的原生方法上面要加上 @JavascriptInterface 注解
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代码
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(''+" +
"document.getElementsByTagName('html')[0].innerHTML+'');");
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
-
前端代码
WebView
WebView
请输入要传递的值:
点我
-
客户端代码
runOnUiThread(new Runnable() {
@Override
public void run() {
nativewebview.loadUrl("javascript:if(window.remote){window.remote('"+getedtext+"')}");
}
});
我们通过loadurl 这个方法继续去加载js里面的方法即可
js调用 安卓打开相机和相册
-
前端代码
调配设备照片
-
客户端代码
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 mUploadCallbackAboveL;
private android.webkit.ValueCallback 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(''+" +
"document.getElementsByTagName('html')[0].innerHTML+'');");*/
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 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 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 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 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是动态权限 所以我代码里面也出了下权限申请 。最后