简述 Android和H5交互
- 1 — WebView基本配置
- 2 —WebView和H5的交互
- 3 — WebView错误页面处理
- 4 —WebView中的Cookie操作
- 5 —WebView漏洞
- 6 —WebView内存泄漏问题
1WebView基本配置
布局设置
代码设置
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp">
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:id="@+id/btn1"
android:text="无参调用"/>
<Button
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:text="有参调用"
android:id="@+id/btn2"/>
</LinearLayout>
<WebView
android:id="@+id/wv"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="9" />
</LinearLayout>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
初如化控件
//初始化控件
Button btn1 = (Button) findViewById(R.id.btn1);
btn1.setOnClickListener(this);
Button btn2 = (Button) findViewById(R.id.btn2);
btn2.setOnClickListener(this);
wv = (WebView) findViewById(R.id.wv);
//获取webSettings
WebSettings settings = wv.getSettings();
//让webView支持JS
settings.setJavaScriptEnabled(true);
//加载百度网页
wv.loadUrl("http://www.baidu.com/");
//这个时候就能显示百度页面了
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
2 WebView和H5的交互
我们自己写一个H5页面 放在assets目录下
注意 : 如果html文件存于assets:则加前缀:file:///android_asset/
如果在Sdcard直接使用file:///sdcard/ or file:/sdcard也可以
//加载本地assets目录下的静态网页
wv.loadUrl("file:///android_asset/123.html");
//第一个参数把自身传给js 第二个参数是this的一个名字
//这个方法用于让H5调用android方法
wv.addJavascriptInterface(this, "android");
- 1
- 2
- 3
- 4
- 5
Android调用JS方法
//这个是button的点击事件
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1:
//参数 “javascript:” + js方法名
wv.loadUrl("javascript:message()");
break;
case R.id.btn2:
//在android调用js有参的函数的时候参数要加单引号
wv.loadUrl("javascript:message2('" + name + "')");
break;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
JS调用Android方法
//下面的两个方法是让网页来调的
//这个注解必须加 因为 兼容问题
@JavascriptInterface
public void setMessage() {
Toast.makeText(this, "我弹", Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void setMessage(String name) {
Toast.makeText(this, "我弹弹" + name, Toast.LENGTH_SHORT).show();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
H5代码,如果H5看不懂请自行百度
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<button id="btn0">调用android无参参数</button>
<button id="btn1">调用android有参参数</button>
<a href='aa://atguigu/path'>点我试试</a>
<a href='https://www.baidu.com'>百度</a>
<div id="content"></div>
</body>
<script type="text/javascript">
var name = "啊福老师 哇哇哇"
document.getElementById("btn0").onclick = function(){
//android是传过来的对象名称,setmessage是android中的方法
android.setMessage();
};
document.getElementById("btn1").onclick = function(){
//android是传过来的对象名称,setmessage是android中的方法
android.setMessage(name);
};
var content = document.getElementById("content");
function message(){
content.innerHTML = "调用了有参的js函数"
};
function message2(des){
content.innerHTML = "调用了"+des;
};
</script>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
第二种交互方式 而是采用scheme + cookie的方式
--1、Java 调用 js里面的函数、效率低、估计要200ms左右
而js去调Java的方法、速度很快、50ms左右、所以尽量用js调用Java方法
--2、Java 调用 js 的函数、没有返回值、调用了就控制不到了
--3、Js 调用 Java 的方法、返回值如果是字符串、你会发现这个字符串是 native 的、转成 locale 的才能正常使用
--4、网页中尽量不要使用jQuery、执行起来需要5-6秒、最好使用原生的js写业务脚本、以提升加载速度、改善用户体验
--5、Android4.2以下的系统存在着webview的js对象注入漏洞…Android API 16 没有正确限制使用webview.addJavaScripteInterface方法,远程攻击者 使用JavaReflectionAPI利用执行任意java对象的方法
scheme设置:对于要启动的Activity
//这一章不详细讲解scheme的使用
<activity android:name=".SecondActivity">
<intent-filter>
<data android:scheme="aa"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
看一下原来的H5代码
<a href='aa://atguigu/path'>点我试试</a>
- 1
注意:
//Url地址 aa://atguigu/path
下面的是Activity清单文件的配置
<data android:scheme="aa" android:host="atguigu" android:path="/path"/>
上下对比其实和我们的URL地址是一样的
aa 是 scheme
host 是主机名称
path 是路径
当然还可以配置端口和加参数
aa://atguigu:8080/path?id = 10
通过activity配置那么就可以跳转到相应的界面里,如果activity只配置scheme = aa那么只要是aa的Url都是适配的
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
wv.setWebViewClient(new WebViewClient() {
//当页面开始加载的时候调用此方法
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
//通过对URl的解析来决定调转到哪个页面
//这边只是简单做一些判断当前是否是Scheme跳转
if (url.contains("aa")) {
Intent intent = new Intent(Intent.ACTION_VIEW,
Uri.parse(url));
startActivity(intent);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
3WebView错误页面处理
场景 : 加载webview错误时加载网页的错误页面,体验不好,速度慢,这个时候就需要我们自行处理错误页面
第一步 先做一个错误的H5页面放在本地,或者加载Activity都可以
wv.setWebViewClient(new WebViewClient() {
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
Log.i(TAG, "onReceivedError: " + errorCode + " " + description);
//判断错误类型
if (errorCode == ERROR_UNSUPPORTED_SCHEME) {
Log.i(TAG, "onReceivedError: " + "true");
//停止加载错误页面,否则会显示原来H5加载的错误页面
//再跳到现在的错误页面,体验不好,当然也可以做其它操作
view.stopLoading();
view.loadUrl("file:///android_asset/error.html");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
4WebView中的Cookie操作
关于Cookie的介绍在这不再详说
场景:webView需要保存从网页中获取到的Cookie 在涉及到账户体系的产品中,包含了一种登录状态的传递。比如,在Native(原生)界面的登录操作,进入到Web界面时,涉及到账户信息时,需要将登录状态传递到Web里面,避免用户二次登录。这里就涉及到WebView加载网页时的Cookie操作了。
通常我们在登录时获取到用户的Cookie信息,然后将其保存到sdcard的WebView缓存文件当中,这样在加载网页时,WebView会自动将当前url的本地Cookie信息放在http请求的request中,传递给服务器
获取cookie
//加载百度网站
wv.loadUrl("http://www.baidu.com");
wv.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
CookieManager instance = CookieManager.getInstance();
//这样就可以获取到Cookie了
String cookie = instance.getCookie(url);
Log.i(TAG, "onPageStarted: "+cookie);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
设置cookie
//创建CookieSyncManager 参数是上下文
CookieSyncManager.createInstance(context);
//得到CookieManager
CookieManager cookieManager = CookieManager.getInstance();
//得到向URL中添加的Cookie的值
String cookieString;//获取方法不再详述,以项目要求而定
//使用cookieManager..setCookie()向URL中添加Cookie
cookieManager.setCookie(url, cookieString);
CookieSyncManager.getInstance().sync();
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
5WebView漏洞和内存泄漏
/*
*
* ① Android API 16 没有正确限制使用webview.addJavaScripteInterface方法,远程攻击者使用 JavaReflectionAPI利用执行任意java对象的方法
*
* ② webview在布局文件中的使用:webView写在其它容器中时 (先销毁webview再销毁activity)因为 webview是独立进程,最好创建个viewGroup用来放置webview,Activity创建时add进来,在Activity停止时remove掉
*
* ③使用 jsbridge(详情看下面)
*
* ④ 在此方法的操作放到后者使用webviewClient.onPageFinished->WebChromeClient.onProgressChanged
*
* ⑤ 后台耗电问题 :activity不可见时要停用webview
*
* ⑥ webview硬件加速导致页面渲染问题-白屏展示(关闭硬件加速)
*
* ⑦动态添加webview,对传入webview中使用的context使用弱引用,动态添加webview意思在布局
* */
JSBridge
什么是JsBridge
WebViewJavascriptBridge是移动UIView和Html交互通信的桥梁,就是实现java和js的互相调用的桥梁。替代了WebView的自带的JavascriptInterface的接口,使得开发者更方便的让js和native灵活交互,使我们的开发更加灵活和安全。
JSBridge的优点
Android API 4.4以前,谷歌的webview存在安全漏洞,网站可以通过js注入就可以随便拿到客户端的重要信息,甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后,在API 4.4以后增加了防御措施,如果用js调用本地代码,开发者必须在代码申明JavascriptInterface, 列如在4.0之前我们要使得webView加载js只需如下代码:
mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");
4.4之后使用时需要在调用Java方法加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这样就可以避免恶意网页利用js对客户端的进行窃取和攻击。 但是即使这样,我们很多时候需要在js调用本地java代码的时候,要做一些判断和限制,或者有的场景也会做些过滤或者对用户友好提示,甚至更复杂的Hybrid模式下,需要js和native之间进行交互通讯,拍照上传,因此原生的JavascriptInterface 就比较维护了,特此有了基于JavascriptInterface 封装的WebViewJavascriptBridge框架。
JsBridge: https://github.com/lzyzsd/JsBridge
智能推荐
【Spark 内核】 Spark 内核解析-下
Spark内核泛指Spark的核心运行机制,包括Spark核心组件的运行机制、Spark任务调度机制、Spark内存管理机制、Spark核心功能的运行原理等,熟练掌握Spark内核原理,能够帮助我们更好地完成Spark代码设计,并能够帮助我们准确锁定项目运行过程中出现的问题的症结所在。 Spark Shuffle 解析 Shuffle 的核心要点 ShuffleMapStage与ResultSta...
spring cloud netflix (07) 服务的消费者(feign)
前言 完整知识点:spring cloud netflix 系列技术栈 Feign (同步通信 HTTP通信) feign是基于接口完成服务与服务之间的通信的 搭建Feign服务 项目结构 项目搭建 pom.xml application类 application.yml 使用feign完成服务与服务之间的通信 feign是基于接口完成服务与服务之间的通信的...
AtCoder Beginner Contest 174 E.Logs
AtCoder Beginner Contest 174 E.Logs 题目链接 到最后才发现是二分,菜菜的我/(ㄒoㄒ)/~~ 我们直接二分 [1,max{a[i]}][1,max\lbrace a[i]\rbrace][1,max{a[i]}] 即可,对每一个 midmidmid,每个数 a[i]a[i]a[i] 只需要切 a[i]−1mid\frac{a[i]-1}{mid}mi...
小程序基础与实战案例
小程序开发工具与基础 小程序开发准备: 申请小程序账号( appid ) 下载并安装微信开发者工具 具体步骤如下: 先进入 微信公众平台 ,下拉页面,把鼠标悬浮在小程序图标上 然后点击 小程序开发文档 照着里面给的步骤,就可以申请到小程序账号了。 然后就可以下载 开发者工具 了 下载完打开后的界面就是这个样子 下面让我们来新建一个小程序开发项目: 在AppID输入自己刚刚注册的AppID就可以,或...
猜你喜欢
VMware centOS7 下通过minikube部署Kubernetes
1、环境准备: VMware CentOS-7-x86_64 CPU:2*2core 内存:8G 宿主机和虚拟机需网络互通,虚拟机外网访问正常 Centos发行版版本查看:cat /etc/centos-release root用户操作 2、禁用swap分区 Kubernetes 1.8开始要求关闭系统的Swap,可暂时关闭或永久禁用, 使用 $ free -m 确认swap是否为开启状态 $ s...
逻辑回归与scikit-learn
欢迎关注本人的微信公众号AI_Engine LogisticRegression 算法原理 一句话概括:逻辑回归假设数据服从伯努利分布,通过极大化似然函数(损失函数)的方法,运用梯度下降或其他优化算法来求解参数,来达到将数据二分类的目的。 定义:逻辑回归(Logistic Regression)是一种用于解决二分类(0 or 1)问题的机器学习方法,用于估计某种事物的可能性(不是概率)。比如某用户...
指针OR数组?用他们来表达字符串又有何不同?
cocowy的编程之旅 在学习C语言的过程中我们经常可以看到或者听到这样一句话:数组其实等价于指针,例如: 在这里可以轻松的看出输出后他们的值相等,其实在计算机内存里面,p为本地变量,有着他自己的作用域。而指针变量q保存着这个数组的首地址,通过*号指向这个地址保存的变量值。 然而我们再看一个例子: 这个时候计算机报错,这是为什么呢? 其实原因很简单,指针说指向的这个字符串的地址是位于计算机代码段地...
广度搜索
广度搜索的基本使用方法 广度搜索不同于深度搜索,是一种一步一步进行的过程,每一个点只记录一遍。需要用到队列记录每一步可以走到的位置,找到目标位置输出步数即可。 用到的知识:结构体、队列 如图 首先我们需要定义一个结构体来存储每个遍历到的点和步数 广搜不会用到递归,所以可以直接在主函数里写,这里需要定义一个结构体队列 初始化队列并将起始点入列 遍历 完整代码...
NIO Socket 编程实现tcp通信入门(二)
1、NIO简介 NIO面向通道和缓冲区进行工作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。可以双向传输数据,是同步非阻塞式IO。NIO还引入了选择器机制,从而实现了一个选择器监听多个底层通道,减少了线程并发数。用NIO实现socket的Tcp通信需要掌握下面三个知识点: Buffer 缓冲区 Channel 通道 Selector 选择器 2、java.nio.Buff...