代码地址如下:
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. AC日记——由乃与大母神原型和偶像崇拜 洛谷 P3792

    由乃与大母神原型和偶像崇拜 思路: 逆元+线段树维护和+线段树维护平方和+线段树维护最大最小值: 代码: #include <bits/stdc++.h> using namespace ...

  2. [thinkphp] U方法域名支持

    域名支持如果你的应用涉及到多个子域名的操作地址,那么也可以在U方法里面指定需要生成地址的域名,例如: U('Blog/read@blog.thinkphp.cn','id=1'); @后面传入需要指定 ...

  3. 修改hadoop的jar包运行时候分配的jvm内存

    在hadoop-env.sh中修改参数添加 export HADOOP_HEAPSIZE="4096" 设置分配的最大jvm内存为4096,一般用于jar包里面除了执行map和re ...

  4. Python的异步编程[0] -> 协程[0] -> 协程和 async / await

    协程 / Coroutine 目录 生产者消费者模型 从生成器到异步协程– async/await 协程是在一个线程执行过程中可以在一个子程序的预定或者随机位置中断,然后转而执行别的子程序,在适当的时 ...

  5. 礼物(BFS)

    礼物 时间限制: 1 Sec  内存限制: 64 MB提交: 39  解决: 4[提交][状态][讨论版] 题目描述 给出一个n行m列的点阵,“.”表示可通行格子,“#”表示不可通行格子,“K”表示国 ...

  6. 手动安装python3和xgboost

    yum install openssl-devel -y .tar.xz cd Python- ./configure --prefix=/usr/local/python3.5.4 make mak ...

  7. memcahced部署

    Memcached是一个内存数据库,数据以key/value键值对的形式保存在服务器预先分配的内存区块中,由于Memcached服务自身没有对缓存的数据进行持久化存储的设计,因此,在服务器端的Memc ...

  8. Hibernate4 No Session found for current thread

    Hibernate4 与 spring3 集成之后, 如果在取得session 的地方使用了getCurrentSession, 可能会报一个错:“No Session found for curre ...

  9. intellij idea android错误: Missing styles. Is the correct theme chosen for this layout?

    Missing styles. Is the correct theme chosen for this layout? Use the Theme combo box above the layou ...

  10. python3-开发进阶Django-debug-toolbar的配置和Django logging的配置

    阅读目录 django-debug-toolbar的配置 Django logging的配置 一.django-debug-toolbar的配置 1.介绍 django-debug-toolbar 是 ...