android中使用tmf框架插件化开发的问题

android中使用tmf框架插件化开发的问题

最近项目开发使用的是tmf框架,其中大多数都是通过源生和H5交互的方式来实现的,大体实现和别的三方框架是一样的,需要按照tmf的官方文档引入一些lib和依赖,添加一些配置等。
我主要就我们在使用做一简单的总结。
大致是使用是项目中创建一个抽象的父类(TMFBaseJsApi)让其extends JsApi,其中JsApi是tmf框架中的类。在TMFBaseJsApi中重写两个核心的方法,分别是:

public abstract class TMFBaseJsApi extends JsApi {
     
    protected final String TAG = "TMFBaseJsApi";
    protected long clickTimeMillis = System.currentTimeMillis();
    protected static final String STATUS = "status";
    protected static final String MESSAGE = "message";
    protected static final String CALLBACK_DATA = "callbackData";//回调数据
    protected static final String STATUS_SUCC = "0";//成功
    protected static final String STATUS_FIAL = "1";//失败
    protected static final String STATUS_NETWORK_NONE = "-9999";//无网
    protected Activity activity;
@Override
    public String method() {
     
        return pluginName();
    }

 @Override
    public void handle(BaseTMFWeb baseTMFWeb, JsCallParam jsCallParam) {
     
        Logs.d(TAG, "TMFBaseJsApi-名称:" + pluginName() + ",参数:" + jsCallParam.paramStr);
        this.activity = baseTMFWeb.mActivity;
        execute(baseTMFWeb, jsCallParam);
    }

以上两个重要的方法,第一个是H5所调用的插件的名字,第二个是handle()这个方法H5调用源生的入口方法,同时您看到在这个方法中调了execute(baseTMFWeb,jsCallParam)这个方法,该方法也是一个抽象方法。

 /**
     * 调用入口
     */
    public abstract void execute(BaseTMFWeb baseTMFWeb, JsCallParam jsCallParam);

该方法是每个客户端写的插件被调到的入口,例如以扫码这个插件为例简单说一下。
目前我们的需求是扫二维码将结果给H5,让H5对扫码的结果进行一些处理。因此我需要些一个插件(也就是一个类)这里定义为addScan(人家定义的类名,莫笑),所有的插件都是继承父类TMFBaseJsApi,需要重写核心的方法,代码如下:

public class addScan extends TMFBaseJsApi {
     
 @Override
    public String pluginName() {
     
        return "addScan";
    }
`` @Override
    public void execute(final BaseTMFWeb baseTMFWeb, final JsCallParam jsCallParam) {
     
        this.baseTMFWeb = baseTMFWeb;`
}

这就写好了一个插件,这里pluginName一定要和H5保持一致,否则调用不到,如果这个插件被H5调用到,则execute()方法就会被执行,而jSCallParam 则是H5需要给源生端传的参数,通常是JSONObject,通常是通过解析Json来拿到我们需要获取的值。比如我们这一步需要从H5获得一些参数,我们只需要加以下代码:


```java
 @Override
    public void execute(final BaseTMFWeb baseTMFWeb, final JsCallParam jsCallParam) {
     
        this.baseTMFWeb = baseTMFWeb;
        //注册扫描结果事件
        if (!EventBus.getDefault().isRegistered(this)){
     
            EventBus.getDefault().register(this);
        }
        try {
     
            JSONObject json = new JSONObject(jsCallParam.paramStr);
            JSONObject temp1 = new JSONObject(json.optString("scanCallback"));
           // scanFunc = new TestClass();
            scanFunc=new JsCallbackFunc();
            scanFunc.sessionId = temp1.optString("sessionId");
            scanFunc.funcId = temp1.optInt("funcId");

            JSONObject temp2 = new JSONObject(json.optString("leftBtnFunc"));
            leftFunc = new JsCallbackFunc();
            leftFunc.sessionId = temp2.optString("sessionId");
            leftFunc.funcId = temp2.optInt("funcId");

            JSONObject temp3 = new JSONObject(json.optString("centerBtnFunc"));
            centerFunc = new JsCallbackFunc();
            centerFunc.sessionId = temp3.optString("sessionId");
            centerFunc.funcId = temp3.optInt("funcId");

            JSONObject temp4 = new JSONObject(json.optString("rightBtnFunc"));
            rightFunc = new JsCallbackFunc();
            rightFunc.sessionId = temp4.optString("sessionId");
            rightFunc.funcId = temp4.optInt("funcId");

            JSONObject temp5 = new JSONObject(json.optString("rightUpBtnFunc"));
            rightUpFunc = new JsCallbackFunc();
            rightUpFunc.sessionId = temp5.optString("sessionId");
            rightUpFunc.funcId = temp5.optInt("funcId");

        } catch (Exception e) {
     
            e.printStackTrace();
            //如果H5插件未加载完成
            if(null==scanFunc && null==rightFunc && null==leftFunc && null==centerFunc && null==rightUpFunc){
     
               mOnCheckPluginInter.onPluginIsReady(false);
            }else{
     
                mOnCheckPluginInter.onPluginIsReady(true);
            }
        }
    }

我分别从H5端获取了4个callBack,这4个callBack分别是JsCallbackFunc的对象,而JsCallbackFunc是tmf提供的js与源生交互的桥,它只有class文件

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.tencent.tmf.webview.api.callback;

import android.text.TextUtils;
import android.util.Log;
import com.tencent.tmf.webview.api.TMFWebConfig;
import com.tencent.tmf.webview.api.base.BaseTMFWeb;
import com.tencent.tmf.webview.api.utils.EmptyUtils;
import com.tencent.tmf.webview.api.utils.GsonHelper;
import java.util.Iterator;
import org.json.JSONObject;

public class JsCallbackFunc {
     
    public String sessionId;
    public int funcId;

    public JsCallbackFunc() {
     
    }

    public void callbackJs(final BaseTMFWeb webView, String resultJsonStr) {
     
        try {
     
            if (EmptyUtils.isEmpty(webView)) {
     
                return;
            }

            String var3 = GsonHelper.getInstance().toJson(this);
            final JSONObject var4 = new JSONObject(var3);
            if (EmptyUtils.isEmpty(var4)) {
     
                return;
            }

            if (!TextUtils.isEmpty(resultJsonStr)) {
     
                JSONObject var5 = new JSONObject(resultJsonStr);
                if (!EmptyUtils.isEmpty(var5)) {
     
                    Iterator var6 = var5.keys();

                    while(var6.hasNext()) {
     
                        String var7 = var6.next().toString();
                        if (!TextUtils.isEmpty(var7)) {
     
                            var4.put(var7, var5.get(var7));
                        }
                    }
                }
            }

            Runnable var9 = new Runnable() {
     
                public void run() {
     
                    webView.mTmfWebView.loadUrl(String.format("javascript:handleMessageFromTcs('onCallback', %s)", var4.toString()));
                    if (TMFWebConfig.DEBUG) {
     
                        Log.i(TMFWebConfig.TAG, "【CallbackH5】callback result: " + var4.toString());
                    }

                }
            };
            webView.getWebViewHolder().getHandler().post(var9);
        } catch (Throwable var8) {
     
            if (TMFWebConfig.DEBUG) {
     
                Log.e(TMFWebConfig.TAG, "【CallbackH5】callback exception: " + var8.getMessage());
            }
        }

    }
}

这个就是JsCallBackFun提供的class文件,而我们每次将值传给H5都是通过这个桥的对象来传递。你应该也看到了我在execute中新增了EventBus对象,这个主要是用来处理完操作将值回传给H5的,比如扫完码后将值给插件,插件通过桥,将值放在callBackData中,然后H5去取值。但是我们自己定义的插件中必须要注册插件,注册的方式如下:

ITMFWeb mWebContainer;
//这里我们是自己封装的,通过H5ContainerBuilder来创建ITMFWeb对象
 mWebContainer = H5ContainerBuilder.createH5Container(this, webviewX5Client2, webviewX5ChromeClient2);
 //注册插件
 mWebContainer.addJsApi(new addScan());

注册完插件就可以确保能被h5调到,扫码完将结果个插件的代码:

    YTCallbackEvent routeEvent = new YTCallbackEvent();
    routeEvent.type = RouterConfig.QRCODE_SCAN;
    routeEvent.data = result;
    EventBus.getDefault().post(routeEvent);
    //在插件addScan中接收事件
      @Subscribe
    public void onQrCodeResultEvent(YTCallbackEvent event){
     
        if (event.type.equals(RouterConfig.QRCODE_SCAN)){
     
            String result = event.data;
             }
    }
    

这里需要将结果给H5,android和H5交互的方式是json,因此需要构造一个json 给H5

 JSONObject backJson = new JSONObject();
  try {
     
         backJson.put(STATUS, STATUS_SUCC);
         backJson.put(CALLBACK_DATA, result == null ? "" : result );
            } catch (JSONException e) {
     
                e.printStackTrace();
            }
  scanFunc.callbackJs(baseTMFWeb, backJson.toString());
            }

可以看到是通过scanFunc.callbackJs()方法将值传回去的,问题就在这里。我构造的json是以下这种格式:

{
     "status":"0","callbackData":"wxp://f2f095552C5DDEC3E3390922B0CA92E017FE262E35FE02F69D0B9CA613B58781DB3?p=eJxdT8tSwzAM%2FBr52JETEisHH9KSHrlQuLuJ2xr8IrgD%2BXuUwAlJMztaSTtaN2mlVNUhNiTrhgiFGT8OabL6gbBCDnHx5qphaKEj6I8izZOdn5Ku1nErOWspSZzN%2BP4ye30rJX9C3UN15ErZxt09uhSzWXZjCsyZty97ZrxZn1dYwrMLd28Kb3F%2FmVMsrCT4IthYXo1308kFqyVu8fvBacnMtCIansDQQNcAteubRLBXG4NAjzAo6BXsUQQ7H0zZvLFZ%2BSezCv%2BzUr5jH4quEX8Az9lUyg%3D%3D"}

但是H5那边拿到的结果就是不合适,和他几番核对后发现原来是我这边将特殊字符://,转义为“//”,但是这个操作到底是怎么形成的,因此通过单步进入JsCallBackFun中,发现java源生的JSONObject居然给我转义了,深深给我埋了一个坑,


```java
   String var3 = GsonHelper.getInstance().toJson(this);
            final JSONObject var4 = new JSONObject(var3);
            if (EmptyUtils.isEmpty(var4)) {
                return;
            }

从var4就发现特殊字符被转义了,因此我知道原因了,一生气之下将这个JsCallBackFunc进行了重写,当然只是为了测试,因此随便写了一个类名为TestClass.java

package com.yitong.mobile.biz.launcher.jsapi.qrcodescan;

import android.text.TextUtils;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.tencent.tmf.webview.api.TMFWebConfig;
import com.tencent.tmf.webview.api.base.BaseTMFWeb;
import com.tencent.tmf.webview.api.utils.EmptyUtils;
import com.tencent.tmf.webview.api.utils.GsonHelper;


import org.json.JSONObject;
import org.json.JSONTokener;

import java.util.Iterator;

public class TestClass {
     
        public String sessionId;
        public int funcId;

        public TestClass() {
     
        }

        public void callbackJs(final BaseTMFWeb webView, String resultJsonStr) {
     
            try {
     
                if (EmptyUtils.isEmpty(webView)) {
     
                    return;
                }

                final ScanResult scanResult = new Gson().fromJson(resultJsonStr, ScanResult.class);
                scanResult.setFuncId(this.funcId);
                scanResult.setSessionId(this.sessionId);

               /* String var3 = GsonHelper.getInstance().toJson(this);
                final JSONObject var4 = new JSONObject(var3);
                if (EmptyUtils.isEmpty(var4)) {
                    return;
                }

                if (!TextUtils.isEmpty(resultJsonStr)) {


                 *//*   ScanResult scanResult = new Gson().fromJson(resultJsonStr, ScanResult.class);
                    var4.put("status", scanResult.getStatus());
                    var4.put("callbackData", scanResult.getCallbackData());*//*

                    *//*JSONObject var5 = new JSONObject(resultJsonStr);
                    if (!EmptyUtils.isEmpty(var5)) {
                        Iterator var6 = var5.keys();

                        while(var6.hasNext()) {
                            String var7 = var6.next().toString();
                            if (!TextUtils.isEmpty(var7)) {
                                var4.put(var7, var5.get(var7));
                            }
                        }
                    }*//*
                }*/

                Runnable var9 = new Runnable() {
     
                    public void run() {
     
                        webView.mTmfWebView.loadUrl(String.format("javascript:handleMessageFromTcs('onCallback', %s)", new Gson().toJson(scanResult)));
                        if (true) {
     
                            Log.i(TMFWebConfig.TAG, "【CallbackH5】callback result: " + new Gson().toJson(scanResult));
                        }

                    }
                };
                webView.getWebViewHolder().getHandler().post(var9);
            } catch (Throwable var8) {
     
                if (true) {
     
                    Log.e(TMFWebConfig.TAG, "【CallbackH5】callback exception: " + var8.getMessage());
                }
            }

        }
    }


package com.yitong.mobile.biz.launcher.jsapi.qrcodescan;

public class ScanResult {
     
    private String status;
    /**
     * callbackData : wxp://f2f095552C5DDEC3E3390922B0CA92E017FE262E35FE02F69D0B9CA613B58781DB3?p=eJxdT8tSwzAM%2FBr52JETEisHH9KSHrlQuLuJ2xr8IrgD%2BXuUwAlJMztaSTtaN2mlVNUhNiTrhgiFGT8OabL6gbBCDnHx5qphaKEj6I8izZOdn5Ku1nErOWspSZzN%2BP4ye30rJX9C3UN15ErZxt09uhSzWXZjCsyZty97ZrxZn1dYwrMLd28Kb3F%2FmVMsrCT4IthYXo1308kFqyVu8fvBacnMtCIansDQQNcAteubRLBXG4NAjzAo6BXsUQQ7H0zZvLFZ%2BSezCv%2BzUr5jH4quEX8Az9lUyg%3D%3D
     */

    private String callbackData;
    public String sessionId;
    public int funcId;

    public String getSessionId() {
     
        return sessionId;
    }

    public void setSessionId(String sessionId) {
     
        this.sessionId = sessionId;
    }

    public int getFuncId() {
     
        return funcId;
    }

    public void setFuncId(int funcId) {
     
        this.funcId = funcId;
    }

    public String getCallbackData() {
     
        return callbackData;
    }

    public void setCallbackData(String callbackData) {
     
        this.callbackData = callbackData;
    }

    public String getStatus() {
     
        return status;
    }

    public void setStatus(String status) {
     
        this.status = status;
    }
}

采用三方的gson来代替源生的JSONObject,避免了特殊字符被转义的发生。
然后在addScan中将scanFunc修改为

private TestClass  scanFunc;

替换了private JsCallbackFunc scanFunc;
接着在调用

 String data = StringEscapeUtils.unescapeJava(backJson.toString());
 scanFunc.callbackJs(baseTMFWeb, data);

这样就可以ok啦,轻松绕过了JscallBackFunc导致的问题。
当然这个问题的核心在于当我们遇到一些class文件后,发现它有问题,如果轻微的绕过修改,代替其工作的功能,当然你可以使用一个base64就能搞定的问题,不用这么麻烦,但是觉得这个思路我还是喜欢的,记录生活的随笔,请勿吐槽,谢谢。

你可能感兴趣的:(sdk问题,android,java)