IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 第一次使用WebView遇到的坑 -> 正文阅读

[移动开发]Android 第一次使用WebView遇到的坑

项目场景:

在项目开发过程中,需要对图标进行网页跳转,尝试用WebView实现


第一个坑!

问题描述:

我首先上网查询WebView入门demo,类似于跳转百度等这样的小demo,然后写出一个activity用来绑定WebView布局

package com.sprocomm.tablelamp.ui.market;

import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import com.sprocomm.tablelamp.base.BaseActivity;
import com.sprocomm.tablelamp.databinding.ActivityStudyCloudBinding;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * created by XuMaoxin
 * 2021/09/18
 * 中小学云平台WebView的父布局
 */
public class StudyCloudActivity extends BaseActivity {
    private ActivityStudyCloudBinding binding;
    private final String originalUrl = "https://ykt.eduyun.cn/ykt/sjykt/index.html";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityStudyCloudBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        //访问网页
        binding.wvStudyCloud.loadUrl(originalUrl);
        //系统会默认通过手机浏览器打开网页,为了能够直接通过WebView显示网页,则必须设置
        binding.wvStudyCloud.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);

                return  true;
            }
        });
    }
}

布局

<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/wv_study_cloud"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

</WebView>

如此一来,按照我们的预想,只要加载这个activity,就应该用webview显示咱们的目标url了,然鹅。。。
当我们通过其他地方的点击事件想要启动这个activity时,出现了这样的问题
在这里插入图片描述


解决方案:

通过面向百度、CSDN、简书等编程,我找到了这么一篇博客webViwe 报错 For security reasons, WebView is not allowed in privileged processes,根据文中描述,在Android8.0以后的版本中,如果你的app为系统app,则使用WebView会报错,这里我使用了博客中的方法二,在代码中加入了一个hookWebView()方法

private static String TAG = "hookWebView";
    public static void hookWebView(){
        int sdkInt = Build.VERSION.SDK_INT;
        try {
            Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
            Field field = factoryClass.getDeclaredField("sProviderInstance");
            field.setAccessible(true);
            Object sProviderInstance = field.get(null);
            if (sProviderInstance != null) {
                Log.i(TAG,"sProviderInstance isn't null");
                return;
            }

            Method getProviderClassMethod;
            if (sdkInt > 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
            } else if (sdkInt == 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
            } else {
                Log.i(TAG,"Don't need to Hook WebView");
                return;
            }
            getProviderClassMethod.setAccessible(true);
            Class<?> factoryProviderClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
            Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
            Constructor<?> delegateConstructor = delegateClass.getDeclaredConstructor();
            delegateConstructor.setAccessible(true);
            if(sdkInt < 26){//低于Android O版本
                Constructor<?> providerConstructor = factoryProviderClass.getConstructor(delegateClass);
                if (providerConstructor != null) {
                    providerConstructor.setAccessible(true);
                    sProviderInstance = providerConstructor.newInstance(delegateConstructor.newInstance());
                }
            } else {
                @SuppressLint("SoonBlockedPrivateApi") Field chromiumMethodName = factoryClass.getDeclaredField("CHROMIUM_WEBVIEW_FACTORY_METHOD");
                chromiumMethodName.setAccessible(true);
                String chromiumMethodNameStr = (String)chromiumMethodName.get(null);
                if (chromiumMethodNameStr == null) {
                    chromiumMethodNameStr = "create";
                }
                Method staticFactory = factoryProviderClass.getMethod(chromiumMethodNameStr, delegateClass);
                if (staticFactory!=null){
                    sProviderInstance = staticFactory.invoke(null, delegateConstructor.newInstance());
                }
            }

            if (sProviderInstance != null){
                field.set("sProviderInstance", sProviderInstance);
                Log.i(TAG,"Hook success!");
            } else {
                Log.i(TAG,"Hook failed!");
            }
        } catch (Throwable e) {
            Log.w(TAG,e);
        }
    }

并在setContentView()之前调用之

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        hookWebView();
        binding = ActivityStudyCloudBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        //访问网页
        binding.wvStudyCloud.loadUrl(originalUrl);
        //系统会默认通过手机浏览器打开网页,为了能够直接通过WebView显示网页,则必须设置
        binding.wvStudyCloud.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);

                return  true;
            }
        });
    }

此时运行就能成功启动activity,并显示出网页


第二个坑!

问题描述:

上网搜WebView使用教程,都会介绍到WebView中的一些方法,例如:canGoBack()goBack()。因为在实际使用中,用户会习惯使用返回键跳转网页,但是返回键会导致当前activity生命周期的结束,即finish(),因此需要重写onBackPressed()方法,即重写返回键的回调。而WebView提供的canGoBack()goBack()方法就是用于判断当前网页是否可返回上一级网页,和执行返回的。因此重写onBackPressed()方法可以如下:

    @Override
    public void onBackPressed() {
        if (binding.wvStudyCloud.canGoBack()) {
            binding.wvStudyCloud.goBack();
        } else {
            finish();
        }
    }

有上一级网页就返回网页,无上一级就finish(),简单吧?要真有这么简单就好了。

上面的逻辑没有问题,可是实际运行时我发现,就算在网页主页按返回,也是不能实现finish(),通过打 log 我发现,canGoBack()的返回值不管我是否在网页的主页,一直都是true,咋回事呢?

面向百度吧。

解决方案:

Android webView加载网页重定向导致canGoBack一直为true的解决方案
既然因为判断条件恒为true导致页面无法finish掉,那么只要更改正确的判断条件就ok了,要判断当前网页是否是网页主页,可以根据url去判断,而WebView提供了获取当前url的方法getUrl(),所以判断条件可以更换为

    @Override
    public void onBackPressed() {
        Log.d("xumaoxin", "============================");
        Log.d("xumaoxin", ""+ originalUrl);
        Log.d("xumaoxin", ""+ binding.wvStudyCloud.getUrl());

        if (binding.wvStudyCloud.canGoBack() && !(originalUrl.equals(binding.wvStudyCloud.getUrl()))) {
            binding.wvStudyCloud.goBack();
        } else {
            finish();
        }
    }

originalUrl是在创建类时定义的用于加载网页的url,如果当前url和originalUrl不一致,则执行goBack();若一致,则证明当前已经在网页主页了,则执行finish()canGoBack()的判断我没删,因为此处它不造成任何影响,就保留下来了。

这样,webView就可以正常工作了,本以为不难使用的一个WebView,弄了我三四个小时,属实难受,希望这个记录贴能帮助到看到的人,少走弯路,节约宝贵的时间。

最后修改好的完整代码如下(其中项目相关包名隐藏;BaseActivity是架构中所带,此处可以替换为AppCompatActivity;binding.wvStudyCloud是ViewBinding的用法,不懂的可以用findViewById()):

package ****;

import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import ****.BaseActivity;
import ****.ActivityStudyCloudBinding;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * created by XuMaoxin
 * 2021/09/18
 * 中小学云平台WebView的父布局
 */
public class StudyCloudActivity extends BaseActivity {
    private ActivityStudyCloudBinding binding;
    private final String originalUrl = "https://ykt.eduyun.cn/ykt/sjykt/index.html";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        hookWebView();
        binding = ActivityStudyCloudBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        //访问网页
        binding.wvStudyCloud.loadUrl(originalUrl);
        //系统会默认通过手机浏览器打开网页,为了能够直接通过WebView显示网页,则必须设置
        binding.wvStudyCloud.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);

                return  true;
            }
        });
    }

    @Override
    public void onBackPressed() {
        Log.d("xumaoxin", "============================");
        Log.d("xumaoxin", ""+ originalUrl);
        Log.d("xumaoxin", ""+ binding.wvStudyCloud.getUrl());

        if (binding.wvStudyCloud.canGoBack() && !(originalUrl.equals(binding.wvStudyCloud.getUrl()))) {
            binding.wvStudyCloud.goBack();
        } else {
            finish();
        }
    }

    private static String TAG = "hookWebView";
    public static void hookWebView(){
        int sdkInt = Build.VERSION.SDK_INT;
        try {
            Class<?> factoryClass = Class.forName("android.webkit.WebViewFactory");
            Field field = factoryClass.getDeclaredField("sProviderInstance");
            field.setAccessible(true);
            Object sProviderInstance = field.get(null);
            if (sProviderInstance != null) {
                Log.i(TAG,"sProviderInstance isn't null");
                return;
            }

            Method getProviderClassMethod;
            if (sdkInt > 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getProviderClass");
            } else if (sdkInt == 22) {
                getProviderClassMethod = factoryClass.getDeclaredMethod("getFactoryClass");
            } else {
                Log.i(TAG,"Don't need to Hook WebView");
                return;
            }
            getProviderClassMethod.setAccessible(true);
            Class<?> factoryProviderClass = (Class<?>) getProviderClassMethod.invoke(factoryClass);
            Class<?> delegateClass = Class.forName("android.webkit.WebViewDelegate");
            Constructor<?> delegateConstructor = delegateClass.getDeclaredConstructor();
            delegateConstructor.setAccessible(true);
            if(sdkInt < 26){//低于Android O版本
                Constructor<?> providerConstructor = factoryProviderClass.getConstructor(delegateClass);
                if (providerConstructor != null) {
                    providerConstructor.setAccessible(true);
                    sProviderInstance = providerConstructor.newInstance(delegateConstructor.newInstance());
                }
            } else {
                @SuppressLint("SoonBlockedPrivateApi") Field chromiumMethodName = factoryClass.getDeclaredField("CHROMIUM_WEBVIEW_FACTORY_METHOD");
                chromiumMethodName.setAccessible(true);
                String chromiumMethodNameStr = (String)chromiumMethodName.get(null);
                if (chromiumMethodNameStr == null) {
                    chromiumMethodNameStr = "create";
                }
                Method staticFactory = factoryProviderClass.getMethod(chromiumMethodNameStr, delegateClass);
                if (staticFactory!=null){
                    sProviderInstance = staticFactory.invoke(null, delegateConstructor.newInstance());
                }
            }

            if (sProviderInstance != null){
                field.set("sProviderInstance", sProviderInstance);
                Log.i(TAG,"Hook success!");
            } else {
                Log.i(TAG,"Hook failed!");
            }
        } catch (Throwable e) {
            Log.w(TAG,e);
        }
    }
}
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-24 10:40:21  更:2021-09-24 10:44:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:24:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码