Android 系统的 WebView 初始功能并不具备完备的浏览器能力。在线客服网站使用了
Cookie、LocalStoreage、文件上传、文件下载
等需要浏览器交互的功能。因此需要将以上功能在 WebView 中实现,可以参考以下代码:package com.example.myapplication;?import android.app.Activity;import android.app.DownloadManager;import android.content.Intent;import android.net.Uri;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.webkit.*;?import androidx.appcompat.app.AppCompatActivity;?import java.net.URL;?public class MainActivity extends AppCompatActivity {private WebView webView;private ValueCallback<Uri[]> filePathCallback;private final int REQUEST_SELECT_FILE = 100;?@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);webView = findViewById(R.id.webView);?webView.setWebViewClient(new WebViewClient());webView.setWebChromeClient(new WebChromeClient() {@Overridepublic void onReceivedTitle(WebView view, String title) {// 这里我们使用了原生组件的Title,因此需要在View Title变化时,同步更新原生组件的Titlesuper.onReceivedTitle(view, title);setTitle(title);}?@Overridepublic boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> callback, FileChooserParams fileChooserParams) {// 网页中有文件上传控件时,会调用这个方法filePathCallback = callback;if (fileChooserParams.getAcceptTypes().length > 0 && fileChooserParams.getAcceptTypes()[0].startsWith("image/")) {Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType("image/*");intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/png", "image/jpeg", "image/gif", "image/webp", "image/bmp"});Intent chooserIntent = Intent.createChooser(intent, "选择图片");startActivityForResult(chooserIntent, REQUEST_SELECT_FILE);} else {Intent intent = fileChooserParams.createIntent();Intent chooserIntent = Intent.createChooser(intent, "选择文件");startActivityForResult(chooserIntent, REQUEST_SELECT_FILE);}return true;}});?WebSettings webSettings = webView.getSettings();DownloadBlobFileJSInterface mDownloadBlobFileJSInterface = new DownloadBlobFileJSInterface(this);// DownloadManager只支持http和https协议,因此需要在这里使用mDownloadBlobFileJSInterface处理blob协议webView.addJavascriptInterface(mDownloadBlobFileJSInterface, "Android");webView.setDownloadListener((url, userAgent, contentDisposition, mimetype, contentLength) -> {if (url.startsWith("http://") || url.startsWith("https://")) {DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));request.allowScanningByMediaScanner();request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "download");DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);dm.enqueue(request);} else if (url.startsWith("blob:")) {String filename = url.split("\\\\?filename=")[1];webView.loadUrl(mDownloadBlobFileJSInterface.getBase64StringFromBlobUrl(url, filename));}});?// 打开LocalStoragewebSettings.setDomStorageEnabled(true);// 允许Js支持webSettings.setJavaScriptEnabled(true);// Cookie支持CookieManager cookieManager = CookieManager.getInstance();cookieManager.setAcceptCookie(true);cookieManager.setAcceptThirdPartyCookies(webView, true);// 允许访问文件webSettings.setAllowFileAccess(true);webSettings.setAllowFileAccessFromFileURLs(true);webSettings.setAllowUniversalAccessFromFileURLs(true);webSettings.setAllowContentAccess(true);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {webSettings.setAllowFileAccessFromFileURLs(true);webSettings.setAllowUniversalAccessFromFileURLs(true);}// 加载网站(隐藏标题栏使用参数 hideHeader=true)webView.loadUrl("https://tccc.qcloud.com/web/im/chat/?webAppId=9903496b6be2e57ad291dd2333f3ad9f&hideHeader=true");}?@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_SELECT_FILE) {if (filePathCallback == null) {return;}if (resultCode == Activity.RESULT_OK) {if (data == null) {filePathCallback.onReceiveValue(new Uri[]{});} else {filePathCallback.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, data));}} else {filePathCallback.onReceiveValue(null);}filePathCallback = null;}}}
?package com.example.myapplication;??import static androidx.core.app.ActivityCompat.requestPermissions;import static androidx.core.content.PermissionChecker.checkSelfPermission;??import android.Manifest;import android.content.Context;import android.os.Build;import android.os.Environment;import android.util.Base64;import android.webkit.JavascriptInterface;import android.webkit.URLUtil;import android.widget.Toast;?import androidx.annotation.RequiresApi;import androidx.core.content.PermissionChecker;??import java.io.File;import java.io.FileOutputStream;import java.util.regex.Matcher;import java.util.regex.Pattern;?public class DownloadBlobFileJSInterface {??private Context mContext;private static final int REQUEST_CODE = 200;private static String mFileName;private DownloadFileSuccessListener mDownloadFileSuccessListener;private Pattern pattern = Pattern.compile("^data:(.*);base64,");??public DownloadBlobFileJSInterface(Context context) {this.mContext = context;}??public void setDownloadFileSuccessListener(DownloadFileSuccessListener listener) {mDownloadFileSuccessListener = listener;}??@RequiresApi(api = Build.VERSION_CODES.N)@JavascriptInterfacepublic void getBase64FromBlobData(String base64Data, String blobUrl) {convertToFileAndProcess(base64Data, blobUrl);}??public static String getBase64StringFromBlobUrl(String blobUrl, String filename) {mFileName = filename;if (blobUrl.startsWith("blob")) {return "javascript: var xhr = new XMLHttpRequest();" +"xhr.open('GET', '" + blobUrl + "', true);" +"xhr.setRequestHeader('Content-type','*');" +"xhr.responseType = 'blob';" +"xhr.onload = function(e) {" +" if (this.status == 200) {" +" var blobFile = this.response;" +" var reader = new FileReader();" +" reader.readAsDataURL(blobFile);" +" reader.onloadend = function() {" +" base64data = reader.result;" +" Android.getBase64FromBlobData(base64data, '" + blobUrl + "');" +" }" +" }" +"};" +"xhr.send();";}return "javascript: console.log('It is not a Blob URL');";}??@RequiresApi(api = Build.VERSION_CODES.N)private void convertToFileAndProcess(String base64Str, String blobUrl) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {if (checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PermissionChecker.PERMISSION_GRANTED) {requestPermissions((MainActivity) mContext, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);return;}}Matcher matcher = pattern.matcher(base64Str);if(matcher.find()){String mimeType = matcher.group(1);String base64String = matcher.replaceFirst("");if(mFileName.isEmpty()) {mFileName = URLUtil.guessFileName(blobUrl,null, mimeType);}File stlFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) , mFileName);saveFileToPath(base64String, stlFile);Toast.makeText(mContext, "文件" + mFileName + "已下载成功", Toast.LENGTH_SHORT).show();if (mDownloadFileSuccessListener != null) {mDownloadFileSuccessListener.downloadFileSuccess(stlFile.getAbsolutePath());}}}??private void saveFileToPath(String base64, File filePath) {try {?byte[] fileBytes = Base64.decode(base64,0);FileOutputStream os = new FileOutputStream(filePath, false);os.write(fileBytes);os.flush();os.close();} catch (Exception e) {e.printStackTrace();}}??public interface DownloadFileSuccessListener {void downloadFileSuccess(String absolutePath);}}
?