zoukankan      html  css  js  c++  java
  • android js 互相调用

    代码地址如下:
    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;
    
    ");
          writer.write("final class JSBridge implements com.liwg.jsbridge.library.IJSBridge{
    ");
          writer.write("    public static final JSBridge INSTANCE = new JSBridge();
    ");
          writer.write("    public java.util.Map<String,Object> map = new java.util.HashMap<>();
    ");
          writer.write("    private JSBridge(){
    ");
          for (Map.Entry<String, String> entry : map.entrySet()) {
            writer.write(String.format("     map.put("%s",%s); 
    ", entry.getKey(), entry.getValue()));
          }
          writer.write("    }
    ");
          writer.write("    public static final JSBridge get(){
    ");
          writer.write("      return INSTANCE;
    ");
          writer.write("    }
    ");
          writer.write("    public String getJsCode(){
    ");
          writer.write("      return "" + jsCode + "";
    ");
          writer.write("    }
    ");
          writer.write(
              "    /**注册对象, 被@JsPlugin注解标记的对象会自动注入,并调用空参的构造函数,
      如果需要重写构造,需保留空参的构造,并调用此方法注册
      */
    ");
          writer.write("    public void register(String name,Object obj){
    ");
          writer.write("      map.put(name,obj);
    ");
          writer.write("    }
    ");
          writer.write("    public Object queryJavaObject(String name){
    ");
          writer.write("     return map.get(name);
    ");
          writer.write("    }
    ");
    /*      writer.write("    public void callJsReady(com.liwg.jsbridge.library.BridgeWebView webview){
    ");
          writer.write("     webview.callJsMethod("JSBridgeReady()");
    ");
          writer.write("    }
    ");*/
          writer.write("  }
    ");
        } 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大师代发,拒绝转载,转载需要作者授权

  • 相关阅读:
    Go语言基础之包
    Go语言基础之结构体
    Go语言基础之指针
    Goland debug失败
    Geymotion 中安装 arm
    Linux 常用的目录及作用
    CentOS7使用YUM安装Adobe Flash Player
    XShell 连接 VirtualBox CentOS7
    机械键盘的修理方法是什么
    CentOS7网络配置
  • 原文地址:https://www.cnblogs.com/demodashi/p/9442742.html
Copyright © 2011-2022 走看看