代码地址如下:
http://www.demodashi.com/demo/13107.html

android js 互相调用 第二版

  • 支持js匿名函数接收
  • 支持js json对象接收
  • 支持js函数返回值获取
  • 通过注解注入js方法
  • 优化第一版的反射注入方式,采用注解处理器编译时生成注入代码,提高运行效率
  • 加入简单的 webview 预加载功能

实现原理

  • 通过注解处理器实现js代码自动生成
  • 创建WebViewChromeClient重写 onProgress方法当进度大于30%的时候执行js代码注入,js代码必须注入成功才能调用

js代码生成逻辑

/**
* 注解处理器
*/
@AutoService(Processor.class) public class InjectProcessor extends AbstractProcessor { /**
* 文件相关的辅助类
*/
private Filer mFiler;
/**
* 元素相关的辅助类
*/
private Elements mElementUtils;
/**
* 日志相关的辅助类
*/
private Messager mMessager; //返回注解处理器可处理的注解操作
//@Override public Set<String> getSupportedOptions() {
// return getSupportedAnnotationTypes();
//} @Override public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
} //得到注解处理器可以支持的注解类型
@Override public Set<String> getSupportedAnnotationTypes() {
HashSet<String> objects = new HashSet<>();
objects.add(JsInject.class.getName());
return objects;
} //执行一些初始化逻辑
@Override public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mFiler = processingEnv.getFiler();
mElementUtils = processingEnv.getElementUtils();
mMessager = processingEnv.getMessager();
} //核心方法,扫描,解析并处理自定义注解,生成***.java文件
@Override public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
try {
processImpl(roundEnv);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
} private void processImpl(RoundEnvironment roundEnv) throws ClassNotFoundException, IOException {
// 获取被 JsInject 标记的元素
Set<? extends Element> elementsAnnotatedWith =
roundEnv.getElementsAnnotatedWith(JsInject.class);
Iterator<? extends Element> iterator = elementsAnnotatedWith.iterator();
//创建模板代码
String objectJs = "if(typeof(window.%s)=='undefined'){ window.%s = {";
String methodJs = "%s:function(){"
+ " return EasyJS.call('%s', '%s', Array.prototype.slice.call(arguments));},";
final StringBuilder objectSb = new StringBuilder();
Map<String, String> map = new HashMap<>();
while (iterator.hasNext()) {
Element next = iterator.next();
JsInject jsInject = next.getAnnotation(JsInject.class);
//String classType = next.asType().toString();
//error(next.getEnclosingElement() + "-----" + next.getKind() + "-----" + next.getSimpleName()
// +"----------"+next.getEnclosedElements()+"------"+next.asType().toString()+"--------",
// next);
// 获取 被JsInject 标记的 class ,并判断 当前类是否继承于 com.liwg.jsbridge.library.JsPlugin
TypeElement typeElement = (TypeElement) next;
if (!typeElement.getSuperclass().toString().equals("com.liwg.jsbridge.library.JsPlugin")) {
error("cover JsInject note class must extends JsPlugin", next);
return;
}
String objectName = jsInject.value();
if (objectName == null || objectName.length() < 1) {
objectName = typeElement.getSimpleName().toString();
}
map.put(objectName, String.format("new %s()", typeElement.getQualifiedName().toString()));
objectSb.append(String.format(objectJs, objectName, objectName));
final StringBuilder methodSb = new StringBuilder();
// 获取方法列表
List<? extends Element> methodElements = next.getEnclosedElements();
for (int i = 0; i < methodElements.size(); i++) {
Element element = methodElements.get(i);
if(!(element instanceof ExecutableElement))
continue;
ExecutableElement method = (ExecutableElement) element;
String methodName = method.getSimpleName().toString();
// 存在一个 <init>方法,过滤掉
if (methodName.contains("<")) continue;
if (!method.getModifiers().contains(Modifier.PUBLIC)) {
//必须是public 修饰的方法
continue;
}
// 过滤掉 JsInject 注解 声明需要过滤的方法
if (!filterMethod(jsInject.filter(), methodName)) {
//不需要过滤此方法
methodSb.append(String.format(methodJs, methodName, objectName, methodName));
}
}
if (methodSb.length() > 0) methodSb.deleteCharAt(methodSb.length() - 1);
objectSb.append(methodSb);
objectSb.append("}}");
}
objectSb.append("if(window.EasyJS&&window.EasyJS.injectFlag==0){if(JSBridgeReady){JSBridgeReady();window.EasyJS.injectFlag=1}}");
String jsCode = objectSb.toString();
// 创建 java 源文件
JavaFileObject sourceFile = mFiler.createSourceFile("com.liwg.jsbridge.library.JSBridge");
Writer writer = null;
try {
writer = sourceFile.openWriter();
writer.write("package com.liwg.jsbridge.library;\n\n");
writer.write("final class JSBridge implements com.liwg.jsbridge.library.IJSBridge{\n");
writer.write(" public static final JSBridge INSTANCE = new JSBridge();\n");
writer.write(" public java.util.Map<String,Object> map = new java.util.HashMap<>();\n");
writer.write(" private JSBridge(){\n");
for (Map.Entry<String, String> entry : map.entrySet()) {
writer.write(String.format(" map.put(\"%s\",%s); \n", entry.getKey(), entry.getValue()));
}
writer.write(" }\n");
writer.write(" public static final JSBridge get(){\n");
writer.write(" return INSTANCE;\n");
writer.write(" }\n");
writer.write(" public String getJsCode(){\n");
writer.write(" return \"" + jsCode + "\";\n");
writer.write(" }\n");
writer.write(
" /**注册对象, 被@JsPlugin注解标记的对象会自动注入,并调用空参的构造函数,\n 如果需要重写构造,需保留空参的构造,并调用此方法注册\n */\n");
writer.write(" public void register(String name,Object obj){\n");
writer.write(" map.put(name,obj);\n");
writer.write(" }\n");
writer.write(" public Object queryJavaObject(String name){\n");
writer.write(" return map.get(name);\n");
writer.write(" }\n");
/* writer.write(" public void callJsReady(com.liwg.jsbridge.library.BridgeWebView webview){\n");
writer.write(" webview.callJsMethod(\"JSBridgeReady()\");\n");
writer.write(" }\n");*/
writer.write(" }\n");
} catch (Exception e) {
error(e.getLocalizedMessage(),roundEnv.getRootElements().iterator().next());
} finally {
writer.close();
}
} private boolean filterMethod(String[] filter, String methodName) {
int length = filter == null ? 0 : filter.length;
for (int i = 0; i < length; i++) {
if (filter[i].equals(methodName)) {
return true;
}
}
return false;
} void error(CharSequence msg, Element element) {
mMessager.printMessage(Diagnostic.Kind.WARNING, msg, element);
}
}

生成的代码

final class JSBridge implements com.liwg.jsbridge.library.IJSBridge{
public static final JSBridge INSTANCE = new JSBridge();
public java.util.Map<String,Object> map = new java.util.HashMap<>();
private JSBridge(){
//生成 注入 js对象名和java对象的映射
map.put("AB",new com.src.wugang.jsbridge.MainActivity.AB());
map.put("Plugin",new com.src.wugang.jsbridge.A());
}
public static final JSBridge get(){
return INSTANCE;
}
public String getJsCode(){
return "if(typeof(window.Plugin)=='undefined'){ window.Plugin = {test:function(){ return EasyJS.call('Plugin', 'test', Array.prototype.slice.call(arguments));},test1:function(){ return EasyJS.call('Plugin', 'test1', Array.prototype.slice.call(arguments));}}}if(typeof(window.AB)=='undefined'){ window.AB = {test:function(){ return EasyJS.call('AB', 'test', Array.prototype.slice.call(arguments));},test1:function(){ return EasyJS.call('AB', 'test1', Array.prototype.slice.call(arguments));},test2:function(){ return EasyJS.call('AB', 'test2', Array.prototype.slice.call(arguments));}}}if(window.EasyJS&&window.EasyJS.injectFlag==0){if(JSBridgeReady){JSBridgeReady();window.EasyJS.injectFlag=1}}";
}
/**注册对象, 被@JsPlugin注解标记的对象会自动注入,并调用空参的构造函数,
如果需要重写构造,需保留空参的构造,并调用此方法注册
*/
public void register(String name,Object obj){
map.put(name,obj);
}
public Object queryJavaObject(String name){
return map.get(name);
}
}

使用方式

	<com.wugang.jsbridge.library.BridgeWebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/web_view"/>

Activity

  • 注入的插件对象必须实现JsPlugin接口,所有需要注入的对象必须继承JsPlugin这个类并且 加上 @JsInject 注解标记
  • 被 @JsInject 标记的类会被自动注入,并调用空参的构造创建对象,如果有自定义构造 可以使用 webView.getJsBridge().register()
  • 如果该类中的方法不希望被注入可以 使用 @JsInject 注解上的 filter参数过滤掉
    public class MainActivity extends AppCompatActivity {

       @Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
PreLoadManager.get(this).preload("http://www.baidu.com", "http://www.youku.com");
BridgeWebView webView = (BridgeWebView) findViewById(R.id.web_view);
webView.getJsBridge().register("AB",new AB(this));
webView.loadUrl("file:///android_asset/test.html");
WebView.setWebContentsDebuggingEnabled(true);
} @JsInject public static class AB extends JsPlugin {
private Context context; public AB() {
} public AB(Context context) {
this.context = context;
} public void test(String s, JSFunction jsFunction) {
Toast.makeText(context, "js调用我", 1).show();
jsFunction.execute("test execute " + s);
} public void test1() {
Log.e("-------", "test1: ");
} public void test2() {
Log.e("-------", "test2: ");
}
}
}

HTML&JS代码

	<html>
<script>
// 推荐使用方式,否则直接调用将无法调用到 原生方法
window.JSBridgeReady=function(){
console.log("---window EasyJSReady---")
AB.test2();
AB.test("call test",function(ret){
console.log(ret)
})
}
</script>
<script src="test.js"></script> <body>
<button onclick="javascript:location.reload()">refresh</button>
</body>
</html>

网页预加载

   //预加载,推荐在Application中调用
PreLoadManager.get(this).preload("http://www.baidu.com", "http://www.youku.com");
Activity 中使用
public class PreLoadActivity extends AppCompatActivity {
@Override protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BridgeWebView webView = PreLoadManager.get(this).getWebView();
setContentView(webView);
webView.setWebViewClient(new WebViewClient(){
@Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if(request.hasGesture()){
Intent intent = new Intent(PreLoadActivity.this,PreLoadActivity.class);
intent.putExtra("url",request.getUrl());
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, request);
}
});
webView.loadUrl(getIntent().getStringExtra("url"));
}
}

项目结构图

参考项目https://github.com/lwugang/safe-java-js-webview-bridge

参考项目https://github.com/dukeland/EasyJSWebView

android js 互相调用

代码地址如下:
http://www.demodashi.com/demo/13107.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

android js 互相调用的更多相关文章

  1. Android js相互调用

    一.webview相当于android中的浏览器,基于webkit开发,可以浏览网页文件,支持css javas cript 以及html webview.getSettings().setJavaS ...

  2. android 中webview调用js

    1.android中利用webview调用网页上的js代码. Android 中可以通过webview来实现和js的交互,在程序中调用js代码,只需要将webview控件的支持js的属性设置为true ...

  3. Android与js互相调用

    有话要说: 本篇主要总结了简单的Android与js互相调用的方法. 在开发过程中遇到了需要在安卓中调用js方法的需求,于是将具体的实现过程总结成这篇博客. 效果: 其中“调用安卓方法”按钮是html ...

  4. Android-webview和js互相调用

    Android-webview和js互相调用 Android 和 H5 都是移动开发应用的非常广泛.市面上很多App都是使用Android开发的,但使用Android来开发一些比较复杂附属类,提示性的 ...

  5. Atitit.android js 的键盘按键检测Back键Home键和Menu键事件

    Atitit.android js 的键盘按键检测Back键Home键和Menu键事件 1. onKeyDown @Override public boolean onKeyDown(int keyC ...

  6. WebView使用详解(一)——Native与JS相互调用(附JadX反编译)

    念念不忘,必有回响,永远坚持你所坚持的! 一直在用WebView,还没有系统的总结过它的用法,下面就系统的总结下,分享给大家 一.基本用法 1.加载在线URL void loadUrl(String ...

  7. Cordova app 检查更新 ----JS进行调用(二)

    原文:Cordova app 检查更新 ----JS进行调用(二) 1.获取版本号 需要添加 插件 cordova plugin add https://github.com/whiteoctober ...

  8. android JS 互相通讯

    1.android中利用webview调用网页上的js代码. Android 中可以通过webview来实现和js的交互,在程序中调用js代码,只需要将webview控件的支持js的属性设置为true ...

  9. 在Android开发中调用Rest web服务(转)

    首先从维基百科上拷贝一点Rest的基本概念给大家看看,然后我们再开始详解在Android中如何调用Rest服务.表象化状态转变(英文:Representational State Transfer,简 ...

随机推荐

  1. 【转】Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址

    Android循环滚动广告条的完美实现,封装方便,平滑过渡,从网络加载图片,点击广告进入对应网址 关注finddreams,一起分享,一起进步: http://blog.csdn.net/finddr ...

  2. Windows和Ubuntu平台Android +JAVA 环境搭建

    NOTE 测试的时候,尤其是移动端的测试,需要搭建JAVA和Andriod环境: appium和macaca都需要这两个环境: Q&A Macaca doctor 发现没有platforms这 ...

  3. 线段树+扫描线【HDU1542】Atlantis

    Description 给定一些二维空间上的矩形,求它们的面积并. 一道线段树+扫描线的板子题 然而即使我会打了,也不能灵活运用这种算法.QAQ 遇到题还是不太会. 但是这种板子题还是随随便便切的. ...

  4. 15、Django实战第15天:我要学习咨询

    今天完成的是课程机构列表页面的最后一个模块:我要学习 我们在models中创建对应的表时UserAsk.之前我们讲过:在做表单的时候,我们可以通过forms先对提交的数据做验证,之前我们使用的是For ...

  5. 初步接触CERNVM

    初步接触的来源是对ROOT数据分析工具的搜索,看到一个叫做Life as a Physicist的国外博客.知道了这个包含容器分发的软件,跟重要的是,这个欧洲核子中心开发的平台,对于我等科研人员是一大 ...

  6. 洛谷 P1452 Beauty Contest

    题目背景 此处省略1W字^ ^ 题目描述 贝茜在牛的选美比赛中赢得了冠军”牛世界小姐”.因此,贝西会参观N(2 < = N < = 50000)个农场来传播善意.世界将被表示成一个二维平面 ...

  7. 【暴力】vijos P1897 学姐吃牛排

    判断堆:递归判断每个节点的孩子是否都比其父亲大(小). 判断BST:中序遍历是否有序. #include<cstdio> using namespace std; #define lc ( ...

  8. 《ggplot2:数据分析与图形艺术》,读书笔记

    第1章 简介 1.3图形的语法 第2章从qplot开始入门 1.基本用法:qplot(x,y,data) x是自变量横轴,y是因变量纵轴,data是数据框   2.图形参数 colour=I(&quo ...

  9. JNI之数组

    Array Operations -- 数组操作 1.GetArrayLength jsize GetArrayLength(JNIEnv *env, jarray array); Returns t ...

  10. NHibernate官方文档中文版——持久化类(Persistent Classes)

    持久化类是一个应用程序中的类,主要用来实现业务逻辑(例如,在电商应用中的客户和订单类).持久化类,就像它的名字一样,生命周期短暂并且用来持久化的据库对象实例. 如果这些类的构造能够依照一些简单的原则, ...