今日科技快讯

据路透社报道,推特(Twitter)公司周五宣布,它已经禁止总部位于莫斯科的卡巴斯基实验室在其社交平台上打广告,称这家网络安全公司的商业模式与其广告规则存在冲突,并援引美国政府的说法称卡巴斯基实验室与俄罗斯情报机构有关联。推特通过电子邮件证实了这项封杀令。

作者简介

欢迎回到周一,新的一周继续加油努力吧!

本篇来自 骑小猪看流星 的投稿,分享了他如何封装X5WebView以及如何在项目中灵活运用的过程 ,一起来看看!希望大家喜欢。

骑小猪看流星 的博客地址:

https://www.jianshu.com/u/0111a7da544b

前言

这一篇的目标就是怎么样快速封装X5WebView,如何有效的同步以及管理Cookie,使用IntentService优化预加载,如何监听进度条等一些在项目中使用的常用功能。

正文

功能需求

需求一:客户端账号密码登录成功以后,调用H5界面(也就是使用X5webView,以下简称X5)。H5界面也需要去记录你的状态,比如你客户端本地登录成功以后,H5界面也需要显示登录成功的状态。那么,客户端和H5如何去同步状态?

需求二:因为X5加载的时候,会有一段时间会显示空白或者卡顿,如何去监听并利用这个进度并优化?

需求三:如何简单封装X5WebView基本功能?

需求分析

  • 需求一

(针对需求一,我真的是参考了很多哥们的技术博客,然后由于笔者上周也就是3月9号接到的开发需求,其中遇到了很多坑,所以我希望这篇博客可以把这个需求写的详细,尽可能的造福以后遇到同样需求的朋友让他们节约时间少走弯路)

对于Cookie,我们并不是很陌生,这里继续多一嘴,这个Cookie简单理解主要是用来进程保活的,它具有时效性(持久化和非持久化),它是通过服务器的请求,在响应头里面拿到,然后在第二次http请求上以请求头的方式将参数带过去,优点是减少后台查库压力等等,更加具体的可以参考上面的链接文章,那么,在Android中,也就是WebView中,我们如何去管理Cookie?

首先:CookieSyncManager与CookieManager

  • Android中关于Cookie的说明

早期的cookie是由CookieSyncManager进行管理的,之后CookieSyncManager被抛弃了,换成了CookieManager来进行管理。两个版本的分割线就是Android SDK -- 21。

  • Android中Cookie的存储位置

目前Android系统WebView是将cookie存储data/data/package_name/app_webview这个目录下的一个叫Cookies的数据中。

CookieSyncManager在内存和存储器之间同步浏览器的cookie。另外,CookieSyncManager的同步策略是在一个独立的线程里定时进行同步。

注意:每次同步的时间间隔是5分钟。

CookieSyncManager类下的常用API介绍

cookie同步策略:

CookieSyncManager.createInstance(context);  CookieSyncManager.getInstance().startSync();

cookie停止同步:

CookieSyncManager.getInstance().stopSync()

cookie立即同步:调用了该方法会立即进行cookie的同步,代码如下:

CookieSyncManager.getInstance().sync()

删除cookie操作:

CookieSyncManager.createInstance(this); CookieManager.getInstance().removeAllCookie(); CookieManager.getInstance().removeSessionCookie(); CookieSyncManager.getInstance().sync(); CookieSyncManager.getInstance().startSync();

CookieManager管理cookie:从sdk21之后,webview已经内置了cookie的同步操作了。虽然不再需要关注cookie的同步,但是依然需要掌握删除cookie的操作。

删除cookie操作:底层实现是异步清除数据库的记录

CookieManager.getInstance().removeAllCookies(null);CookieManager.getInstance().flush();

立即同步:注意到这个flush()方法就是立即同步cookie的操作,本质上与CookieSyncManager中的sync()方法是一样的。于是乎,关于同步cookie我们可以有如下简单的写法:

if (Build.VERSION.SDK_INT <21){    com.tencent.smtt.sdk.CookieSyncManager.getInstance().sync();}else {    com.tencent.smtt.sdk.CookieManager.getInstance().flush();}

然后,笔者就遇到了第一个坑,按照如下写法以后,cookie居然神奇的不同步(下面是伪代码,下面是伪代码)

   public void syncCookie(String url, String cookie) {

        CookieSyncManager.createInstance(getContext());        if (!TextUtils.isEmpty(url)) {            CookieManager cookieManager = CookieManager.getInstance();            cookieManager.setAcceptCookie(true);            cookieManager.removeSessionCookie();// 移除            cookieManager.removeAllCookie();            String[] split = cookie.split(";");            for (String string : split) {                //为url设置cookie//                 ajax方式下  cookie后面的分号会丢失                cookieManager.setCookie(url, string);            }

            String newCookie = cookieManager.getCookie(url);            Log.i(TAG, "syncCookie: newCookie == " + newCookie);//          sdk21之后CookieSyncManager被抛弃了,换成了CookieManager来进行管理。            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {                CookieSyncManager.getInstance().sync();//同步cookie            } else {                CookieManager.getInstance().flush();            }        } else {        }

    }

之前笔者通过字符串拼接,也就是append字符串 ,拼接字符串以后,我想直接通过cookieManager.setCookie(url, cookie); 在x5WebView.loadUrl(url);调用之前去设置cookie,然后,cookie就是同步不了。没得办法,打印日志之后发现,手动设置的cookie值,神奇的只有一个分号 !

谷歌百度后,有哥们说是因为cookie Value的值在读取时,只会读取到第一个分号时,当发现第一个分号即认为读取结束。所以分号后面的cookie的值将不会读取,实际测试确实是这样。那么,我们该如何解决拼接cookie读取失败的问题?

解决办法如下,我们可以一个个手动设置cookie,即可拼接完整的Cookie。(这种办法虽然笨拙,但的确可以有效解决分号切割问题)

cookieManager.setCookie(url,"xxx");cookieManager.setCookie(url,"xxx");cookieManager.setCookie(url,"xxx");cookieManager.setCookie(url,"xxx");

当然,我在公司项目里用的是Okhttp,(为什么这里用Okhttp,因为!这样就可以用Okhttp、Retrofit、OkGo等网络框架,直接集成 我在项目中给大家提供的拦截器、自定义CookieJar使用了)。通过自定义拦截器,实现CookieJar去完成同步客户端和H5的cookie状态。这里先上下最终效果图:(笔者的代码可能不是唯一实现功能需求的,但凑合还能用。实现方式有很多种,写的不好也请大家见谅)

OkHttpClient okHttpClient = new OkHttpClient.Builder()        .connectTimeout(10, TimeUnit.SECONDS)        .readTimeout(10, TimeUnit.SECONDS)        .writeTimeout(10, TimeUnit.SECONDS)        .addNetworkInterceptor(new HttpLoggingInterceptor().         setLevel(HttpLoggingInterceptor.Level.BODY))        .addInterceptor(new AddCookiesInterceptor())        .addInterceptor(new SaveCookiesInterceptor())        .cookieJar(new SaCookieManger(HomeApplication.getInstance())).build();

关于AddCookiesInterceptor以及SaveCookiesInterceptor这两个拦截器,主要就是存Cookie和使用Cookie,然后我们点进自定义cookieJar中的 SaCookieManger

public class SaCookieManger implements CookieJar {

    private static final String TAG = LogTAG.cookie;    private static Context mContext;

    public SaCookieManger(Context context) {        mContext = context;    }

    @Override    public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {        SaasCookieManager.loadCookie(cookies,url.host());    }

    @Override    public List<Cookie> loadForRequest(HttpUrl url) {        return new ArrayList<>();    }}

这个类定义了一个context构造参数,在保存cookie的saveFromResponse方法中,调用了SaasCookieManager.loadCookie(cookies,url.host());方法,我们点进SaasCookieManager

public class SaasCookieManager {    static final String TAG = LogTAG.cookie;    /**     * 获取cookie     * @param cookies     * @param url     */    public static void loadCookie(List<Cookie> cookies,String url){        List<String> convertCookies  = new ArrayList<>();

        for (int i = 0; i < cookies.size(); i++) {            String temp = cookies.get(i).toString();            convertCookies.add(temp);        }        com.tencent.smtt.sdk.CookieManager cookieManager= com.tencent.smtt.sdk.CookieManager.getInstance();        cookieManager.setAcceptCookie(true);

        for (String aCookiesArray : convertCookies) {            cookieManager.setCookie(url, aCookiesArray);        }        if (Build.VERSION.SDK_INT <21){            com.tencent.smtt.sdk.CookieSyncManager.getInstance().sync();        }else {            com.tencent.smtt.sdk.CookieManager.getInstance().flush();        }    }

通过遍历添加到集合里面,然后一个个的setCookie( url ,cookie )、接着判断SDK版本号进行同步刷新即可,具体可以参考项目源代码。当然大家也可以在这里面根据开发需求去增加自己的实际功能。

通过上面的步骤,我们就可以简单的实现 客户端与H5端同步cookie。笔者的项目里面,是客户端登录成功以后,进入H5页面,H5页面上直接显示已登录状态。

拓展:有部分手机使用后可能还是无法同步,那么我们可以尝试,设置跨域读取cookie,开启webview对第三方cookie的支持。

public final void setAcceptThirdPartyCookies() {    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {        CookieManager.getInstance().setAcceptThirdPartyCookies(this, true);    }else {        CookieManager.getInstance().setAcceptCookie(true);    }}

当页面加载完毕的时候,我们可以通过下面的截图代码,去获取H5上面的cookie,我们也可以打印日志、进行同步

@Overridepublic void onPageFinished(WebView webView, String s) {    CookieManager cookieManager = CookieManager.getInstance();    cookieManager.setAcceptCookie(true);    String endCookie = cookieManager.getCookie(s);    Log.i(TAG, "onPageFinished: endCookie : " + endCookie);    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {        CookieSyncManager.getInstance().sync();//同步cookie    } else {        CookieManager.getInstance().flush();    }    super.onPageFinished(webView, s);}
  • 需求二

监听进度,是这样,WebView里面的WebChromeClient这个类,里面有个onProgressChanged方法,重写这个方法就可以获取加载进度。获取到X5Webview的进度之后,还需要通过WebView的setWebChromeClient方法,将我们自定义的WebChromeClient对象传进去,即可完成进度监听。

    public void onProgressChanged(WebView view, int newProgress) {            // TODO Auto-generated method stub            progressBar.setProgress(newProgress);            if (progressBar != null && newProgress != 100) {//              Webview加载没有完成 就显示我们自定义的加载图                progressBar.setVisibility(VISIBLE);            } else if (progressBar != null) {//              Webview加载完成 就隐藏进度条,显示Webview                progressBar.setVisibility(GONE);                imageView.setVisibility(GONE);            }        }
  • 需求三

封装X5Webview基本功能

常用设置

比如设置对JS的支持等等一些比较常用的,我们可以直接这样设置

setBackgroundColor(getResources().getColor(android.R.color.white));setWebViewClient(client);setWebChromeClient(chromeClient);setDownloadListener(downloadListener);addJavascriptInterface(new H5JsInterface(getContext(), this), "h5app");setClickable(true);setOnTouchListener(new OnTouchListener() {    @Override    public boolean onTouch(View v, MotionEvent event) {        return false;    }});WebSettings webSetting = getSettings();

webSetting.setJavaScriptEnabled(true);webSetting.setBuiltInZoomControls(true);webSetting.setJavaScriptCanOpenWindowsAutomatically(true);webSetting.setDomStorageEnabled(true);webSetting.setAllowFileAccess(true);webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);webSetting.setSupportZoom(true);webSetting.setUseWideViewPort(true);webSetting.setSupportMultipleWindows(true);webSetting.setAppCacheEnabled(true);webSetting.setGeolocationEnabled(true);webSetting.setAppCacheMaxSize(Long.MAX_VALUE);webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND);webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH);

滚动条(内侧、外侧的设置),隐藏或显示的基本使用

  getX5WebViewExtension().setScrollBarFadingEnabled(false);        setHorizontalScrollBarEnabled(false);//水平不显示小方块        setVerticalScrollBarEnabled(false); //垂直不显示小方块        setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);//滚动条在WebView内侧显示//      setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);//滚动条在WebView外侧显示

处理预加载

有的小伙伴说,X5预加载不是很友好。在加载X5内核的时候,X5内核需要进行一些初始化,这些初始化如果不明确指出运行的线程,它就会在你启动页面的时候,默认在主线程中执行,因此就会出现卡顿(这个现象时有时无,但是我们在代码层面尽可能的去规避使用风险),所以,我们可以写个 IntentService 去帮我们管理预加载问题:

public class X5NetService extends IntentService {

    public static final String TAG = LogTAG.x5webview;    public X5NetService(){        super(TAG);    }    public X5NetService(String name) {        super(TAG);    }

    @Override    public void onHandleIntent(@Nullable Intent intent) {        initX5Web();    }

    public void initX5Web() {

        if (!QbSdk.isTbsCoreInited()) {            QbSdk.preInit(getApplicationContext(), null);// 设置X5初始化完成的回调接口        }        QbSdk.initX5Environment(getApplicationContext(), cb);

    }

    QbSdk.PreInitCallback cb = new QbSdk.PreInitCallback() {

        @Override        public void onViewInitFinished(boolean arg0) {            // TODO Auto-generated method stub        }

        @Override        public void onCoreInitFinished() {            // TODO Auto-generated method stub        }

    };}

多提一嘴:IntentService是Service的子类,比普通的Service增加了额外的功能。IntentService会创建独立的worker线程来处理所有的Intent请求;会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程的问题;所有请求处理完成后,IntentService会自动停止,开发者无需手动调用stopSelf()方法停止Service;

写完之后我们在去自定义Application里面注册服务:(别忘了去清单文件配置Services)

public class HomeApplication extends Application{

    private static HomeApplication homeApplication;    @Override    public void onCreate(){        super.onCreate();        homeApplication = this;        preInitX5Core();    }

    public static HomeApplication getInstance(){        return homeApplication;    }

    private void preInitX5Core(){        //预加载x5内核        Intent intent = new Intent(this,X5NetService.class);        startService(intent);    }}

返回键的处理

这个就根据大家开发需求具体使用了,有的要求返回键按下两次才允许退出等等

生命周期的处理、释放资源的处理:这个就不说了,大家查阅资料集成功能即可

拦截广告的处理

有些哥们说,使用这个经常会出现广告,解决这个办法有两个办法

  1. 使用Https

  2. 设计拦截url规则,对允许的url进行放行加载,不允许的url,WebView禁止加载

结语

好了,文章到这里就结束了,具体demo地址如下:

https://github.com/zuowutan/ShareX5WebViewDemo

欢迎长按下图 -> 识别图中二维码

或者 扫一扫 关注我的公众号

Android中X5WebView详解相关推荐

  1. android中getSystemService详解

    原文地址:android中getSystemService详解作者:邹斌 http://blog.sina.com.cn/s/blog_71d1e4fc0100o8qr.html http://blo ...

  2. Android中menu详解(转)

    Android中菜单的使用时非常频繁的,能够达到很好的使用效果,其实他的实现方法非常简单,下面为大家讲解,首先看图: JAVA代码: package com.smart; import android ...

  3. android广播intent原理,Android中BroadcastReceiver详解

    BroadcastReceiver是什么? Android app可以发送广播也可以接收系统或者其它app发送的广播,是发送/订阅的设计模式.这些广播被发送当重要的事件发生的时候.例如,安卓系统发送广 ...

  4. android 静态注册wifi广播,Android中BroadcastReceiver详解

    BroadcastReceiver是什么? Android app可以发送广播也可以接收系统或者其它app发送的广播,是发送/订阅的设计模式.这些广播被发送当重要的事件发生的时候.例如,安卓系统发送广 ...

  5. Android中Context详解

    给大家介绍下我们在应用开发中最熟悉而陌生的朋友-----Context类 ,说它熟悉,是应为我们在开发中时刻的在与它打交道,例如:Service.BroadcastReceiver.Activity等 ...

  6. Android中LaunchMode详解

    越是做的时间越长,基础知识就忘的越干净,最近做一个项目中,发现启动的几个Activity居然重叠了,我ri--,再不回忆一下就要退出Android界了. 概念解释 Task Task叫做任务,这个简单 ...

  7. android asynctask 参数,Android中AsyncTask详解

    定义 AsyncTask是一个抽象类,在使用时需要继承该类,实现其抽象方法protected abstract Result doInBackground(Params... params).其主要作 ...

  8. [content-description] find_element_by_accessibility_id 在 android 中的详解

    出处:http://testerhome.com/topics/1034 最近 Appium 引入了一个新的 find element 方法:python client 为例 def find_ele ...

  9. android中界面滑动延伸,android中ViewPager详解--视图滑动、界面卡等效果 (三)

    GuideActivity.java引导界面: import java.util.ArrayList; import java.util.List; import android.app.Activi ...

  10. android中ViewPager详解--视图滑动、界面卡等效果 (三)

    2019独角兽企业重金招聘Python工程师标准>>> GuideActivity.java引导界面: import java.util.ArrayList; import java ...

最新文章

  1. 看完这篇,code review 谁敢喷你代码写的烂?怼回去!
  2. SQL Server 2008 R2占用内存越来越大解决方法
  3. Mac下的Parallel Windows忘记密码怎么办?
  4. python py2exe_转:py2exe 生成 python 可执行文件
  5. #内存不够,swap来凑# Linux上创建SWAP文件/分区
  6. linux录音命令,安装Linux 录音软件 Record Audio
  7. 算法总结之 一行代码求两个数的最大公约数
  8. boost::filesystem使用手册
  9. 乐优商城遇到的坑(三)之Centos6.5下安装nginx出错
  10. python怎么过验证,python用户验证怎么弄
  11. python-更新word目录
  12. 跨平台报表和仪表板工具Stimulsoft v2020.1,一键自动激活移动模式
  13. cmd中通过winsat命令测试硬盘、CPU、内存、3d性能等
  14. error c2678解决方法
  15. 自动驾驶汽车的库尔雪维尔测试是不可能的挑战
  16. 单应性(Homography)变换
  17. 门禁系统服务器 控制器 读卡器,门禁系统建设方案.docx
  18. Java 开发环境搭建
  19. ojbk的sas proc 过程之transpose
  20. 百度被黑了,百度被真相曝光

热门文章

  1. CNVD 与 CNNVD 的区别
  2. 基于Java的学生管理系统
  3. abaqus如何并行计算_Abaqus软件与并行计算的硬件配置
  4. 详解全局免流原理(转载)
  5. 支持机顶盒的电影服务器,手把手教你用网络机顶盒看电影
  6. linux cuda 编程指南,CUDA编程指南阅读笔记
  7. ADS 2022 软件下载与安装教程
  8. 利息计算器 html,html+jQuery简单的利息计算器
  9. 软件测试管理者会遇到那些问题?
  10. html浮窗音乐播放器插件,墨涩网 - WordPress浮窗音乐播放器Floating Window Music Player V3.2.6——墨涩网...