你的浏览器禁用了JavaScript, 请开启后刷新浏览器获得更好的体验!
首页
热门
推荐
精选
登录
|
注册
android js 互相调用
立即下载
用AI写一个
金额:
1
元
支付方式:
友情提醒:源码购买后不支持退换货
立即支付
我要免费下载
发布时间:2018-05-08
9人
|
浏览:2333次
|
收藏
|
分享
技术:android js
运行环境:AndroidStudio 2.3
概述
android js 互相调用支持js匿名函数接收
详细
### android js 互相调用 第二版 - ##### 支持js匿名函数接收 - ##### 支持js json对象接收 - ##### 支持js函数返回值获取 - ##### 通过注解注入js方法 - ##### 优化第一版的反射注入方式,采用注解处理器编译时生成注入代码,提高运行效率 - ##### 加入简单的 webview 预加载功能 ![](/contentImages/image/20180508/MzO4mlznSdnDe04bLYa.gif) ### 实现原理 - ##### 通过注解处理器实现js代码自动生成 - ##### 创建WebViewChromeClient重写 onProgress方法当进度大于30%的时候执行js代码注入,js代码必须注入成功才能调用 #### js代码生成逻辑 ```java /** * 注解处理器 */ @AutoService(Processor.class) public class InjectProcessor extends AbstractProcessor { /** * 文件相关的辅助类 */ private Filer mFiler; /** * 元素相关的辅助类 */ private Elements mElementUtils; /** * 日志相关的辅助类 */ private Messager mMessager; //返回注解处理器可处理的注解操作 //@Override public Set
getSupportedOptions() { // return getSupportedAnnotationTypes(); //} @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.RELEASE_7; } //得到注解处理器可以支持的注解类型 @Override public Set
getSupportedAnnotationTypes() { HashSet
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
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(); // 存在一个
方法,过滤掉 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
map = new java.util.HashMap<>();\n"); writer.write(" private JSBridge(){\n"); for (Map.Entry
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); } } ``` #### 生成的代码 ```java final class JSBridge implements com.liwg.jsbridge.library.IJSBridge{ public static final JSBridge INSTANCE = new JSBridge(); public java.util.Map
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); } } ``` #### 使用方式 ~~~xml
~~~ ### Activity - 注入的插件对象必须实现JsPlugin接口,所有需要注入的对象必须继承JsPlugin这个类并且 加上 @JsInject 注解标记 - 被 @JsInject 标记的类会被自动注入,并调用空参的构造创建对象,如果有自定义构造 可以使用 ***webView.getJsBridge().register()*** - 如果该类中的方法不希望被注入可以 使用 @JsInject 注解上的 filter参数过滤掉 ~~~java 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代码 ```js
refresh
``` #### 网页预加载 ~~~java //预加载,推荐在Application中调用 PreLoadManager.get(this).preload("http://www.baidu.com", "http://www.youku.com"); ~~~ ###### Activity 中使用 ~~~java 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")); } } ~~~ ### 项目结构图 ![](/contentImages/image/20180508/QkmcPxfYzQ9ZVzn6Xx8.png) [参考项目https://github.com/lwugang/safe-java-js-webview-bridge](https://github.com/lwugang/safe-java-js-webview-bridge) [参考项目https://github.com/dukeland/EasyJSWebView](https://github.com/dukeland/EasyJSWebView)
本实例支付的费用只是购买源码的费用,如有疑问欢迎在文末留言交流,如需作者在线代码指导、定制等,在作者开启付费服务后,可以点击“购买服务”进行实时联系,请知悉,谢谢
感谢
3
手机上随时阅读、收藏该文章 ?请扫下方二维码
相似例子推荐
评论
作者
@
1
例子数量
9
帮助
3
感谢
评分详细
可运行:
4.5
分
代码质量:
4.5
分
文章描述详细:
4.5
分
代码注释:
4.5
分
综合:
4.5
分
作者例子
android js 互相调用