Android 利用WebViewJavascriptBridge 实现js和java的交互(一)

此文出自:http://blog.csdn.net/sk719887916/article/details/47189607,skay

按安卓开发眼下现状来说,开发人员大部分时间还是花在UI的屏幕适配上,使用原生控件开发成本已不是那么理想,鉴于非常多项目和ios基于一致的ui界面。至使安卓UI开发成本花费更大的代价,因此眼下结合Html5和原生控件是解决UI适配的一种非常好的选择。处于APP性能也会用java和native层进行结合。无论是哪种结合。事实上原理都几乎相同,仅仅要依照它的协议来,是非常easy的,今天我们仅对于Html和Java层结合。学习下WebViewJavascriptBridge。

已经熟悉了jsbridge的朋友直接移步:

《Android基于JsBridge封装的高效带载入进度的WebView》:http://blog.csdn.net/sk719887916/article/details/52402470

一 什么是webViewjavascripBridge?

WebViewJavascriptBridge是移动UIView和Html交互通信的桥梁,用作者的话来说就是实现java(ios为oc)和js的互相调用的桥梁。替代了WebView的自带的JavascriptInterface的接口,使得我们的开发更加灵活和安全。

二 为什么要用webViewjavascripBridge?

对于安卓开发有一段时间的人来说,知道安卓4.4曾经谷歌的webview存在安全漏洞,站点能够通过js注入就能够随便拿到client的重要信息。甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后,增加了防御措施,假设要是js调用本地代码,开发人员必须在代码申明JavascriptInterface,

列如在4.0之前我们要使得webView载入js仅仅需例如以下代码:

mWebView.addJavascriptInterface(new JsToJava(), "myjsfunction");  

4.4之后调用须要在调用方法增加增加@JavascriptInterface注解,假设代码无此申明,那么也就无法使得js生效,也就是说这样就能够避免恶意网页利用js对安卓client的窃取和攻击。

可是即使这样,我们非常多时候须要在js记载本地代码的时候,要做一些推断和限制,或者有可能也会做些过滤和对用户友好提示,因此JavascriptInterface也就无法满足我们的需求了,特此有大神就写出了WebViewJavascriptBridge框架。

三 怎么使用webViewjavascripBridge

1 将jsBridge.jar引入到我们的project

Eclispe:

导入jar包, 或者直接copyjar源代码, jar能够到gitHub上下载。

Android Studio: 

repositories {
    // ...
    maven { url "https://jitpack.io" }
}

dependencies {
    compile ‘com.github.lzyzsd:jsbridge:1.0.4‘
}

2 WebView包需使用以上包的webView

Layout中使用第三方webView

EG:

<?

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" >

    <!-- button 演示Java调用web -->
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:text="@string/button_name"
        android:layout_height="48dp"
        />

    <!-- webview 演示web调用Java -->
    <com.github.lzyzsd.jsbridge.BridgeWebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
     </com.github.lzyzsd.jsbridge.BridgeWebView>

</LinearLayout>

3 Java代码

1 activity

public class MainActivity extends Activity implements OnClickListener {

	private final String TAG = "MainActivity";

	BridgeWebView webView;

	Button button;

	int RESULT_CODE = 0;

	ValueCallback<Uri> mUploadMessage;

    static class Location {
        String address;
    }

    static class User {
        String name;
        Location location;
        String testStr;
    }

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

        webView = (BridgeWebView) findViewById(R.id.webView);

		button = (Button) findViewById(R.id.button);

		button.setOnClickListener(this);

		webView.setDefaultHandler(new DefaultHandler());

		webView.setWebChromeClient(new WebChromeClient() {

			@SuppressWarnings("unused")
			public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType, String capture) {
				this.openFileChooser(uploadMsg);
			}

			@SuppressWarnings("unused")
			public void openFileChooser(ValueCallback<Uri> uploadMsg, String AcceptType) {
				this.openFileChooser(uploadMsg);
			}

			public void openFileChooser(ValueCallback<Uri> uploadMsg) {
				mUploadMessage = uploadMsg;
				pickFile();
			}
		});
		//载入本地网页

		//webView.loadUrl("file:///android_asset/demo.html");
		//载入server网页
		webView.loadUrl("https://www.baidu.com");

		//必须和js同名函数。注冊详细运行函数。相似java实现类。
		webView.registerHandler("submitFromWeb", new BridgeHandler() {

			@Override
			public void handler(String data, CallBackFunction function) {

				String str ="这是html返回给java的数据:" + data;
				// 比如你能够对原始数据进行处理
				makeText(MainActivity.this, str, LENGTH_SHORT).show();

				Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
                function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));
			}

		});
		//模拟用户获取本地位置
        User user = new User();
        Location location = new Location();
        location.address = "上海";
        user.location = location;
        user.name = "Bruce";

        webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
            @Override
            public void onCallBack(String data) {
				makeText(MainActivity.this, "网页在获取你的位置", LENGTH_SHORT).show();

            }
        });

        webView.send("hello");

	}

	public void pickFile() {
		Intent chooserIntent = new Intent(Intent.ACTION_GET_CONTENT);
		chooserIntent.setType("image/*");
		startActivityForResult(chooserIntent, RESULT_CODE);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
		if (requestCode == RESULT_CODE) {
			if (null == mUploadMessage){
				return;
			}
			Uri result = intent == null || resultCode != RESULT_OK ?

null : intent.getData();
			mUploadMessage.onReceiveValue(result);
			mUploadMessage = null;
		}
	}

	@Override
	public void onClick(View v) {
		if (button.equals(v)) {
            webView.callHandler("functionInJs", "data from Java", new CallBackFunction() {

				@Override
				public void onCallBack(String data) {
					// TODO Auto-generated method stub
					Log.i(TAG, "reponse data from js " + data);
				}

			});
		}

	}

}

2 自定webVIewClient

class MyWebViewClient extends BridgeWebViewClient{

        public MyWebViewClient(BridgeWebView webView) {
            super(webView);
        }
    }

将自己定义webViewClient设置到webview上

     mWebView.setWebViewClient(new MyWebViewClient(mWebView));

上面句话非常关键:

通过实例化webView,使用方法和安卓原生的view没多大差别。设置WebChromClient, 设置载入的html(相同支持网络和本地文件)。接着我们须要给web注冊和html端约定好的js方法名。代码列举的submitFromweb和js的运行的方法名一致。玩过NDK的JNI调用的朋友也知道必须和c代码之间有个约定。事实上js桥和jni有点相似,

通过注冊handler来实现回调,Java代码中通过js返回的数据。进行处理后在调用function.onCallback返回给js.这里不做过多解释

//必须和js函数同名。注冊详细运行回调函数,相似java实现类。
		webView.registerHandler("submitFromWeb", new BridgeHandler() {

			@Override
			public void handler(String data, CallBackFunction function) {

				String str ="这是html返回给java的数据:" + data;
				// 比如你能够对原始数据进行处理
				makeText(MainActivity.this, str, LENGTH_SHORT).show();

				Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
                function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));
			}

		});

假设是webview刚開始就运行一段Java代码 ,能够通过webView.CallHandler()来实现 。

当然我们注冊的方法也要和js里面的方法名一致。

 webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() {
            @Override
            public void onCallBack(String data) {
				makeText(MainActivity.this, "网页在获取你的位置", LENGTH_SHORT).show();

            }
        });

3 Html和js实现

html代码例如以下。效果图

<html>
    <head>
        <meta content="text/html; charset=utf-8" http-equiv="content-type">
        <title>
            js调用java
        </title>
    </head>

    <body>
        <p>
            <xmp id="show">
            </xmp>
        </p>
        <p>
            <xmp id="init">
            </xmp>
        </p>
        <p>
            <input type="text" id="text1" value="username(username)" />
        </p>
        <p>
            <input type="text" id="text2" value="password" />
        </p>
        <p>
            <input type="button" id="enter" value="发消息给Native" onclick="testClick();"
            />
        </p>
        <p>
            <input type="button" id="enter1" value="调用Native方法" onclick="testClick1();"
            />
        </p>
        <p>
            <input type="button" id="enter2" value="显示html" onclick="testDiv();" />
        </p>
        <p>
            <input type="file" value="打开文件"  />
        </p>
    </body>

</html>

js代码

 <script>

        function testDiv() {
            document.getElementById("show").innerHTML = document.getElementsByTagName("html")[0].innerHTML;
        }

        function testClick() {
            var str1 = document.getElementById("text1").value;
            var str2 = document.getElementById("text2").value;

            //发送消息给java代码
            var data = "name=" + str1 + ",pass=" + str2;

            window.WebViewJavascriptBridge.send(
                data
                , function(responseData) {

                    document.getElementById("show").innerHTML = "repsonseData from java, data = " +responseData
                }
            );

        }

        function testClick1() {
            var str1 = document.getElementById("text1").value;
            var str2 = document.getElementById("text2").value;

            //调用本地java方法
            window.WebViewJavascriptBridge.callHandler(
                ‘submitFromWeb‘
                , {‘param‘: str1}
                , function(responseData) {
                    document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
                }
            );
        }

        function bridgeLog(logContent) {
            document.getElementById("show").innerHTML = logContent;
        }//注冊事件监听
        function connectWebViewJavascriptBridge(callback) {
            if (window.WebViewJavascriptBridge) {
                callback(WebViewJavascriptBridge)
            } else {
                document.addEventListener(
                    ‘WebViewJavascriptBridgeReady‘
                    , function() {
                        callback(WebViewJavascriptBridge)
                    },
                    false
                );
            }
        }
        //注冊回调函数,第一次连接时调用 初始化函数
        connectWebViewJavascriptBridge(function(bridge) {
            bridge.init(function(message, responseCallback) {
                console.log(‘JS got a message‘, message);
                var data = {
                    ‘Javascript Responds‘: ‘Wee!‘
                };
                console.log(‘JS responding with‘, data);
                responseCallback(data);
            });

            bridge.registerHandler("functionInJs", function(data, responseCallback) {
                document.getElementById("show").innerHTML = ("data from Java: = " + data);
                var responseData = "Javascript Says Right back aka!";
                responseCallback(responseData);
            });
        })
    </script>

这段代码不难理解。我们对上面的id为enter的Button注冊了一个点击事件,点击后运行下面testClick()方法,依次类推,其它Button注冊事件相同,

当点击“发消息给Native”的按钮时,Js通过webWiew的jsBridage.send()发送一条数据给java层(密码和username)。同一时候在此用function()来运行应java层回调函数的。此demo中是把java返回的数据插入到"show"的div里面去。

testClick1():此方法中调用callHandler来调用Java代码的submitFromweb同名函数,能够结合上面的Activty的代码理解下。此函数调用我们已在java注冊实现好的

//必须和js同名函数。注冊详细运行函数,相似java实现类。

webView.registerHandler("submitFromWeb", new BridgeHandler() {

			@Override
			public void handler(String data, CallBackFunction function) {

				String str ="这是html返回给java的数据:" + data;
				// 比如你能够对原始数据进行处理
				makeText(MainActivity.this, str, LENGTH_SHORT).show();

				Log.i(TAG, "handler = submitFromWeb, data from web = " + data);
                function.onCallBack( str + ",Java经过处理后截取了一部分:"+ str.substring(0,5));
			}

		});

java注冊Js函数如上面列子 。那么在js中注冊方法来注冊呢,

在js中我们相同能够直接注冊一个回调函数,通过 bridge.registerHandler()来注冊。接着调用 responseCallback(responseData)来返回数据给java代码的同名函数。f

也能够直接调用init()来指定网页首次载入上面注冊java代码。

  connectWebViewJavascriptBridge(function(bridge) {
            bridge.init(function(message, responseCallback) {
                console.log(‘JS got a message‘, message);
                var data = {
                    ‘Javascript Responds‘: ‘Wee!‘
                };
                console.log(‘JS responding with‘, data);
                responseCallback(data);
            });

            bridge.registerHandler("functionInJs", function(data, responseCallback) {
                document.getElementById("show").innerHTML = ("data from Java: = " + data);
                var responseData = "Javascript Says Right back aka!";
                responseCallback(responseData);
            });
        })

四 总结

通过以上的API介绍,代码演示样例,不难发现此框架的优雅和简便,对js和java两方来说,假设Html中的js须要调用java代码,而java代码没做不论什么实现,那么js中方法也是无效的,反之java代码注冊的函数,没在js里去回调实现。那么Java层也是无法获取js中数据的。由此可见,此通信是两方支持的。必须由两方来约定,这样就避免了Android之前存在的js注入漏洞,也非常大的提高了安全性,也能够保证我们的网页数据不被第三方的APP获取,详细来讲,列如我们的项目某一个web的h5界面,被系统浏览器或者其它第三方App的恶意载入,那么它的java代码想调用你的js函数,实现须要你的H5的Js先注冊,不然跟本无法调用你的h5信息。这样保证了这个html数据的安全性,。第三方的浏览器能够载入预览你的网页,可是第三方java无法和你的的h5中的js交互通信的。相同载入我们自己的APP载入第三方的网页时候,我们能够对第三方网页进行一些行为的过滤,方便保护我们手机的安全,列如第三方能够获取本机地址时我们能够提示用户授权。

尽管H5并不属于插件的一种。可是借助h5我能够方便的去更新一些运营活动。和某些须要常常须要更换UI的业务功能的地方,以上仅仅JSBridge的使用姿势,,以后再给大家解剖下JsBridge的内部实现。

jsbridage的封装请參考: <Android基于JsBridge封装的高效带载入进度的WebView>

项目实例:https://github.com/Tamicer/JsBridge_Android

很多其它文章请关注本人微信公众号:

时间: 2024-10-11 14:51:15

Android 利用WebViewJavascriptBridge 实现js和java的交互(一)的相关文章

Android 中利用WebViewJavascriptBridge 实现js和java的交互(一)

此文出自:http://blog.csdn.net/sk719887916/article/details/47189607 ,skay 博客 按安卓开发目前现状来说,开发者大部分时间还是花在UI的屏幕适配上,使用原生控件开发成本已不是那么理想,鉴于很多项目和ios基于一致的ui界面,至使安卓UI开发成本花费更大的代价,因此目前结合Html5和原生控件是解决UI适配的一种很好的选择,处于APP性能也会用java和native层进行结合.不管是哪种结合,其实原理都差不多,只要按照它的协议来,是很容

Android混合开发之WebViewJavascriptBridge实现JS与java安全交互

前言: 为了加快开发效率,目前公司一些功能使用H5开发,这里难免会用到Js与Java函数互相调用的问题,这个Android是提供了原生支持的,不过存在安全隐患,今天我们来学习一种安全方式来满足Js与java互相调用的需求.它就是WebViewJavascriptBridge. 学习动机: 先看下之前的解决办法:Android混合开发之WebView与Javascript交互 最近棒棒安全的一个市场推广来我们公司推广他们的产品,当时也没太引起我的注意,后来这个市场推广人员把我们的app的进行了他们

Android的WebView通过JS调用java代码

做项目时候会遇到我们用WebView 打开一个web,希望这个web可以调用自己的一些方法,比如我们在进一个web页面,然后当我们点击web上的某个按钮时,希望能判断当前手机端是否已经登录,如果未登录,那么就会跳转到登录页面(登陆页面是另一个Activity).这个时候,一个简单的做法就是在按钮动作事件的js上调用java的方法,从而起到判断是否登录,并决定是否跳转到另一个页面. Google的WebView为我们提供了 addJavascriptInterface(Object obj, St

android webView开发之js调用java代码示例

1.webView设置 webView.getSettings().setJavaScriptEnabled(true);//设置支持js webView.addJavascriptInterface(new JsOperation(),"client");//设置js调用的java类 2.声明js要调用java类 class JsOperation { // 测试方法 @JavascriptInterface//这句标识必须要写上否则会出问题 public void test(Str

Android的JS调用Java代码或使用了Javascript相关技术改如何混淆

http://www.androidren.com/index.php?qa=282&qa_1=android的js调用java代码或使用了javascript相关技术改如何混淆 Android 4.2开始 JS调用Java代码的时候必须加上@JavascriptInterface才能调用. 加上@JavascriptInterface之后就必须要考虑混淆时候的问题,如果混淆的时候把@JavascriptInterface搞丢了你的程序就无法调用了. 其实很简单,你只需要在混淆里面加上: -ke

如何在android平台上使用js直接调用Java方法[转]

转载自:http://www.cocos.com/docs/html5/v3/reflection/zh.html #如何在android平台上使用js直接调用Java方法 在cocos2d-js 3.0beta中加入了一个新特性,在android平台上我们可以通过反射直接在js中调用java的静态方法.它的使用方法很简单: var o = jsb.reflection.callStaticMethod(className, methodName, methodSignature, parame

【cocos2d-js官方文档】二十四、如何在android平台上使用js直接调用Java方法

在cocos2d-js 3.0beta中加入了一个新特性,在android平台上我们可以通过反射直接在js中调用java的静态方法.它的使用方法很简单: var o = jsb.reflection.callStaticMethod(className, methodName, methodSignature, parameters...) 在callStaticMethod方法中,我们通过传入Java的类名,方法名,方法签名,参数就可以直接调用Java的静态方法,并且可以获得Java方法的返回

android混合开发,webview的java与js互操作

android原生应用,用webview加载应用中的网页,并且java代码与js代码可以互相操作. 这是混合开发的基石,最基本也最重要的东西,实验代码在这里. 概括说说—— java调js:调用webView.load("javascript:someFunction()"); 这样可以调用webView里页面上的全局方法.这不是什么新鲜东西,你在网页中也可以这么做,试试在浏览器地址栏输入javascript:alert("427studio");也可以在浏览器地址

Android 使用js调用Java

效果如: 主要用到一个接口类:MyObject package com.example.jsdemo; import android.content.Context; import android.support.v7.app.AlertDialog; import android.webkit.JavascriptInterface; import android.widget.Toast; public class MyObject { private Context context; pu