Back

使用Android, IOS 与Vuejs 做结合的注意点: 调用js 方法

发布时间: 2016-08-23 11:21:00

1. Android中调用

参考:   http://stackoverflow.com/questions/10389572/call-java-function-from-javascript-over-android-webview  , 

通过消息传递来修改视图的文章:  http://blog.csdn.net/veryitman/article/details/6384641

Android 调用 js代码其实很简单. 直接    使用 evaluateJavascript 就可以:

        webview.evaluateJavascript("get_right_button_text()", new ValueCallback() {
            @Override
            public void onReceiveValue(String value) {
                setTextFromHtml(buttonRight, value);
                Log.d(TAG, "get_right_button_text value=" + value);
            }
        });

但是, 这个情况只适用于 普通的HTML( 传统的PHP, JSP,RAILS 所生成的HTML页面) .

不适用于:  Angular, Vuejs 这样, 使用 js引擎对页面再次渲染生成的页面.在这样的页面中, Android的一些方法都会失效(onPageFinished 等等)  往往会报错, 找不到方法. 

所以, 我们要想一个办法.  能够让Android 能够在 合适的时间( js 引擎渲染完HTML页面后) , 执行JS代码. 

试过一些方法, 不过都不行. IOS 下适用的方法,不适合android . 所以, 走的通的方法是:  使用javascript interface. 

步骤: 

1. 声明一个java class: 

package com.tuling.Fragment;

import android.app.Activity;
import android.app.Fragment;
import android.os.Message;
import android.util.Log;
import android.webkit.JavascriptInterface;

import com.tuling.Fragment.html.WebViewFragmentBase;

/**
 * 在 js中调用的 java对象.
 * refer to: http://stackoverflow.com/questions/10389572/call-java-function-from-javascript-over-android-webview
 */
public class AndroidObjectInJavascript {
    private Activity activity;

    private WebViewFragmentBase fragment;

    public AndroidObjectInJavascript(Activity activity, WebViewFragmentBase fragment){
        this.activity = activity;
        this.fragment = fragment;
    }

    /**
     * 设置 对应fragment的标题. 通过 消息机制来实现.
     */
    @JavascriptInterface
    public String setTitle(String title){

        Log.d("AndroidObjectInJs", "== setting title: " + title);
        Message message = fragment.get_title_handler().obtainMessage();
        message.what = 1;  // 这个东西没有意义, 认为它一直是1好了.
        message.obj = title;
        message.sendToTarget();
        Log.d("AndroidObjectInJs", " == 触发了message");

        fragment.set_title(title);

        return "title set in java code.";
    }
}

2. 在调用打开某个Webview的 Activity 或者 Fragment中, 增加如下内容( 下面以fragment为例子): 

public abstract class WebViewFragmentBase extends Fragment implements View.OnClickListener {

    /**
     * 页面链接
     */
    protected String pageUrl;
    protected WebView webview;
    private TextView title;

    // 通过消息机制来修改 某个View的属性. 否则会出现 异常:  "Only the original thread that created a view hierarchy can touch its views" 
    Handler title_handler = new Handler(){
        @Override
        public void handleMessage(Message message){
            // 目前只设置title. TODO 将来可以设置 各种其他变量常量字符串.
            title.setText((String)message.obj);
        }
    };

    // 这个方法给 AndroidObjectInJavascript 使用.
    public Handler get_title_handler(){
        return title_handler;
    }
    public void set_title(String title){
        this.title.setText(title);
        this.title.setVisibility(View.VISIBLE);
    }

    /**
     * 设置当前页面的标题.
     * @param title
     */
    public void set_title(String title){
        this.title.setText(title);
        this.title.setVisibility(View.VISIBLE);
    }

    // 打开这个页面, 注意其中的 "android" js变量, 就是android在js中的对象.
    public View onCreateView(){
        webview = (WebView) view.findViewById(R.id.web_view);
        // 大师添加: 向webview中注入 android object in js:
        AndroidObjectInJavascript android = new AndroidObjectInJavascript(getActivity(), this);
        webview.addJavascriptInterface(android, "android");
        webview.loadUrl(pageUrl);
    }

3. 在对应的 Vuejs 页面中, 直接调用 android 这个变量:

...
<script>
  export default {
     data() {   
       return { 
           // 这里定义初始化变量
       }
     }, 
     route: { 
       this.$http.get('..../interface...').then(function(){
           // 获取 titile 变量
           title = this.article.title
           try{ 
              android.setTitle(title)
           }catch(e) { 
              console.warn("== 出现错误, 如果在非android环境下访问, 出现该警告是正常的.")
              console.warn(e)
           }
       })
     } 
  }

</script>

2. IOS 中调用 Vue

..

<template>
   <!-- 这里是 template的内容 -->
</template>

<script>
  export default {
    data () {
      return {
        article: {}
      }
    },
    route: {
      data (transition) {
        this.showLoading()
        this.$http.get(this.$config.API + '/interface/shoppings/article_details', {article_id: this.$route.params.aid}).then(function (response) {
          this.$set('article', response.data.article)

          var title = this.article.heading

          // IOS语法糖开始.  暴露给 ios 的方法, 要写到这个setTimeout 里面.
          setTimeout(function () {

            // 利用iframe的onload事件刷新页面
            // 注意:  这里的 window.get_title() 方法, 就是要暴露给 IOS的方法. 
            window.get_title = function (){
              return document.title
            }

            // 暴露给 ios的方法, 要写在这个方法之前.
            // 下面代码是废代码(boilerplate code) 没它不行, 写它只为了能让ios知道 上面的 js 方法.
            var iframe = document.createElement('iframe')
            iframe.style.visibility = 'hidden'
            iframe.style.width = '1px'
            iframe.style.height = '1px'
            iframe.onload = function () {
              setTimeout(function () {
                document.body.removeChild(iframe)
              }, 0)
            }
            document.body.appendChild(iframe)

          }, 0)   
          // ios 语法糖,结束          

        }, function (response) {
          console.log('response.data.error' + response.data.error)
        })
        this.hideLoading()
      }
    },
    methods: {

    },
    components: {
      XHeader
    }
  }

</script>

Back