/** * * Apache License * Version 2.0, January 2004 * http://www.apache.org/licenses/ * * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION * * 1. Definitions. * * "License" shall mean the terms and conditions for use, reproduction, * and distribution as defined by Sections 1 through 9 of this document. * * "Licensor" shall mean the copyright owner or entity authorized by * the copyright owner that is granting the License. * * "Legal Entity" shall mean the union of the acting entity and all * other entities that control, are controlled by, or are under common * control with that entity. For the purposes of this definition, * "control" means (i) the power, direct or indirect, to cause the * direction or management of such entity, whether by contract or * otherwise, or (ii) ownership of fifty percent (50%) or more of the * outstanding shares, or (iii) beneficial ownership of such entity. * * "You" (or "Your") shall mean an individual or Legal Entity * exercising permissions granted by this License. * * "Source" form shall mean the preferred form for making modifications, * including but not limited to software source code, documentation * source, and configuration files. * * "Object" form shall mean any form resulting from mechanical * transformation or translation of a Source form, including but * not limited to compiled object code, generated documentation, * and conversions to other media types. * * "Work" shall mean the work of authorship, whether in Source or * Object form, made available under the License, as indicated by a * copyright notice that is included in or attached to the work * (an example is provided in the Appendix below). * * "Derivative Works" shall mean any work, whether in Source or Object * form, that is based on (or derived from) the Work and for which the * editorial revisions, annotations, elaborations, or other modifications * represent, as a whole, an original work of authorship. For the purposes * of this License, Derivative Works shall not include works that remain * separable from, or merely link (or bind by name) to the interfaces of, * the Work and Derivative Works thereof. * * "Contribution" shall mean any work of authorship, including * the original version of the Work and any modifications or additions * to that Work or Derivative Works thereof, that is intentionally * submitted to Licensor for inclusion in the Work by the copyright owner * or by an individual or Legal Entity authorized to submit on behalf of * the copyright owner. For the purposes of this definition, "submitted" * means any form of electronic, verbal, or written communication sent * to the Licensor or its representatives, including but not limited to * communication on electronic mailing lists, source code control systems, * and issue tracking systems that are managed by, or on behalf of, the * Licensor for the purpose of discussing and improving the Work, but * excluding communication that is conspicuously marked or otherwise * designated in writing by the copyright owner as "Not a Contribution." * * "Contributor" shall mean Licensor and any individual or Legal Entity * on behalf of whom a Contribution has been received by Licensor and * subsequently incorporated within the Work. * * 2. Grant of Copyright License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * copyright license to reproduce, prepare Derivative Works of, * publicly display, publicly perform, sublicense, and distribute the * Work and such Derivative Works in Source or Object form. * * 3. Grant of Patent License. Subject to the terms and conditions of * this License, each Contributor hereby grants to You a perpetual, * worldwide, non-exclusive, no-charge, royalty-free, irrevocable * (except as stated in this section) patent license to make, have made, * use, offer to sell, sell, import, and otherwise transfer the Work, * where such license applies only to those patent claims licensable * by such Contributor that are necessarily infringed by their * Contribution(s) alone or by combination of their Contribution(s) * with the Work to which such Contribution(s) was submitted. If You * institute patent litigation against any entity (including a * cross-claim or counterclaim in a lawsuit) alleging that the Work * or a Contribution incorporated within the Work constitutes direct * or contributory patent infringement, then any patent licenses * granted to You under this License for that Work shall terminate * as of the date such litigation is filed. * * 4. Redistribution. You may reproduce and distribute copies of the * Work or Derivative Works thereof in any medium, with or without * modifications, and in Source or Object form, provided that You * meet the following conditions: * * (a) You must give any other recipients of the Work or * Derivative Works a copy of this License; and * * (b) You must cause any modified files to carry prominent notices * stating that You changed the files; and * * (c) You must retain, in the Source form of any Derivative Works * that You distribute, all copyright, patent, trademark, and * attribution notices from the Source form of the Work, * excluding those notices that do not pertain to any part of * the Derivative Works; and * * (d) If the Work includes a "NOTICE" text file as part of its * distribution, then any Derivative Works that You distribute must * include a readable copy of the attribution notices contained * within such NOTICE file, excluding those notices that do not * pertain to any part of the Derivative Works, in at least one * of the following places: within a NOTICE text file distributed * as part of the Derivative Works; within the Source form or * documentation, if provided along with the Derivative Works; or, * within a display generated by the Derivative Works, if and * wherever such third-party notices normally appear. The contents * of the NOTICE file are for informational purposes only and * do not modify the License. You may add Your own attribution * notices within Derivative Works that You distribute, alongside * or as an addendum to the NOTICE text from the Work, provided * that such additional attribution notices cannot be construed * as modifying the License. * * You may add Your own copyright statement to Your modifications and * may provide additional or different license terms and conditions * for use, reproduction, or distribution of Your modifications, or * for any such Derivative Works as a whole, provided Your use, * reproduction, and distribution of the Work otherwise complies with * the conditions stated in this License. * * 5. Submission of Contributions. Unless You explicitly state otherwise, * any Contribution intentionally submitted for inclusion in the Work * by You to the Licensor shall be under the terms and conditions of * this License, without any additional terms or conditions. * Notwithstanding the above, nothing herein shall supersede or modify * the terms of any separate license agreement you may have executed * with Licensor regarding such Contributions. * * 6. Trademarks. This License does not grant permission to use the trade * names, trademarks, service marks, or product names of the Licensor, * except as required for reasonable and customary use in describing the * origin of the Work and reproducing the content of the NOTICE file. * * 7. Disclaimer of Warranty. Unless required by applicable law or * agreed to in writing, Licensor provides the Work (and each * Contributor provides its Contributions) on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied, including, without limitation, any warranties or conditions * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A * PARTICULAR PURPOSE. You are solely responsible for determining the * appropriateness of using or redistributing the Work and assume any * risks associated with Your exercise of permissions under this License. * * 8. Limitation of Liability. In no event and under no legal theory, * whether in tort (including negligence), contract, or otherwise, * unless required by applicable law (such as deliberate and grossly * negligent acts) or agreed to in writing, shall any Contributor be * liable to You for damages, including any direct, indirect, special, * incidental, or consequential damages of any character arising as a * result of this License or out of the use or inability to use the * Work (including but not limited to damages for loss of goodwill, * work stoppage, computer failure or malfunction, or any and all * other commercial damages or losses), even if such Contributor * has been advised of the possibility of such damages. * * 9. Accepting Warranty or Additional Liability. While redistributing * the Work or Derivative Works thereof, You may choose to offer, * and charge a fee for, acceptance of support, warranty, indemnity, * or other liability obligations and/or rights consistent with this * License. However, in accepting such obligations, You may act only * on Your own behalf and on Your sole responsibility, not on behalf * of any other Contributor, and only if You agree to indemnify, * defend, and hold each Contributor harmless for any liability * incurred by, or claims asserted against, such Contributor by reason * of your accepting any such warranty or additional liability. * * END OF TERMS AND CONDITIONS * * APPENDIX: How to apply the Apache License to your work. * * To apply the Apache License to your work, attach the following * boilerplate notice, with the fields enclosed by brackets "[]" * replaced with your own identifying information. (Don't include * the brackets!) The text should be enclosed in the appropriate * comment syntax for the file format. We also recommend that a * file or class name and description of purpose be included on the * same "printed page" as the copyright notice for easier * identification within third-party archives. * * Copyright 2016 Alibaba Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.taobao.weex.bridge; import android.content.Context; import android.os.Handler; import android.os.Handler.Callback; import android.os.Looper; import android.os.Message; import android.text.TextUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.taobao.weex.WXEnvironment; import com.taobao.weex.WXRenderErrorCode; import com.taobao.weex.WXSDKInstance; import com.taobao.weex.WXSDKManager; import com.taobao.weex.adapter.IWXUserTrackAdapter; import com.taobao.weex.common.IWXBridge; import com.taobao.weex.common.IWXDebugProxy; import com.taobao.weex.common.WXConfig; import com.taobao.weex.common.WXErrorCode; import com.taobao.weex.common.WXException; import com.taobao.weex.common.WXJSBridgeMsgType; import com.taobao.weex.common.WXPerformance; import com.taobao.weex.common.WXRefreshData; import com.taobao.weex.common.WXRuntimeException; import com.taobao.weex.common.WXThread; import com.taobao.weex.dom.WXDomModule; import com.taobao.weex.ui.module.WXTimerModule; import com.taobao.weex.utils.WXFileUtils; import com.taobao.weex.utils.WXHack; import com.taobao.weex.utils.WXHack.HackDeclaration.HackAssertionException; import com.taobao.weex.utils.WXHack.HackedClass; import com.taobao.weex.utils.WXJsonUtils; import com.taobao.weex.utils.WXLogUtils; import com.taobao.weex.utils.WXUtils; import com.taobao.weex.utils.WXViewUtils; import com.taobao.weex.utils.batch.BactchExecutor; import com.taobao.weex.utils.batch.Interceptor; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; /** * Manager class for communication between JavaScript and Android. * <ol> * <li> * Handle Android to JavaScript call, can be one of the following * <ul> * <li>{@link #createInstance(String, String, Map, String)}</li> * <li>{@link #destroyInstance(String)}</li> * <li>{@link #refreshInstance(String, WXRefreshData)}</li> * <li>{@link #registerModules(Map)}</li> * <li>{@link #registerComponents(List)}</li> * <li>{@link #invokeCallJSBatch(Message)}</li> * </ul> * </li> * <li> * Handle JavaScript to Android call * </li> * <li> * Handle next tick of message. * </li> * </ol> */ public class WXBridgeManager implements Callback,BactchExecutor { public static final String METHOD_CREATE_INSTANCE = "createInstance"; public static final String METHOD_DESTROY_INSTANCE = "destroyInstance"; public static final String METHOD_CALL_JS = "callJS"; public static final String METHOD_SET_TIMEOUT = "setTimeoutCallback"; public static final String METHOD_REGISTER_MODULES = "registerModules"; public static final String METHOD_REGISTER_COMPONENTS = "registerComponents"; public static final String METHOD_FIRE_EVENT = "fireEvent"; public static final String METHOD_CALLBACK = "callback"; public static final String METHOD_REFRESH_INSTANCE = "refreshInstance"; public static final String KEY_METHOD = "method"; public static final String KEY_ARGS = "args"; // args public static final String MODULE = "module"; public static final String METHOD = "method"; public static final String ARGS = "args"; private static final String UNDEFINED = "-1"; private static final int INIT_FRAMEWORK_OK = 1; private static long LOW_MEM_VALUE = 80; static WXBridgeManager mBridgeManager; private WXDomModule sDomModule; /** * next tick tasks, can set priority */ private WXHashMap<String, ArrayList<WXHashMap<String, Object>>> mNextTickTasks = new WXHashMap<>(); /** * JSThread */ private WXThread mJSThread; /** package **/ Handler mJSHandler; private IWXBridge mWXBridge; private IWXDebugProxy mWxDebugProxy; private boolean mMock = false; /** * Whether JS Framework(main.js) has been initialized. */ private boolean mInit =false; private boolean isJSFrameworkInit(){ return mInit; } private List<List<Map<String, String>>> mRegisterComponentFailList = new ArrayList<>(8); private List<Map<String, Object>> mRegisterModuleFailList = new ArrayList<>(8); private List<String> mDestroyedInstanceId = new ArrayList<>(); private StringBuilder mLodBuilder = new StringBuilder(50); private Interceptor mInterceptor; private WXBridgeManager() { launchInspector(WXEnvironment.sRemoteDebugMode); if (mWXBridge == null) { mWXBridge = new WXBridge(); } mJSThread = new WXThread("WeexJSBridgeThread", this); mJSHandler = mJSThread.getHandler(); } public static WXBridgeManager getInstance() { if (mBridgeManager == null) { synchronized (WXBridgeManager.class) { if (mBridgeManager == null) { mBridgeManager = new WXBridgeManager(); } } } return mBridgeManager; } private void launchInspector(boolean remoteDebug) { if (WXEnvironment.isApkDebugable()) { try { if (mWxDebugProxy != null) { mWxDebugProxy.stop(); } HackedClass<Object> debugProxyClass = WXHack.into("com.taobao.weex.devtools.debug.DebugServerProxy"); mWxDebugProxy = (IWXDebugProxy) debugProxyClass.constructor(Context.class, WXBridgeManager.class) .getInstance(WXEnvironment.getApplication(), WXBridgeManager.this); if (mWxDebugProxy != null) { mWxDebugProxy.start(); if (remoteDebug) { mWXBridge = mWxDebugProxy.getWXBridge(); } else { if (mWXBridge != null && !(mWXBridge instanceof WXBridge)) { mWXBridge = null; } } } } catch (HackAssertionException e) { WXLogUtils.e("launchInspector HackAssertionException ", e); } } } public boolean callModuleMethod(String instanceId, String moduleStr, String methodStr, JSONArray args) { return WXModuleManager.callModuleMethod(instanceId, moduleStr, methodStr, args); } /** * Model switch. For now, debug model and release model are supported */ public void restart() { mInit = false; launchInspector(WXEnvironment.sRemoteDebugMode); if (mWXBridge == null) { mWXBridge = new WXBridge(); } } /** * Set current Instance * @param instanceId {@link WXSDKInstance#mInstanceId} */ public synchronized void setStackTopInstance(final String instanceId) { post(new Runnable() { @Override public void run() { mNextTickTasks.setStackTopInstance(instanceId); } }, instanceId); } @Override public void post(Runnable r){ if(mInterceptor != null && mInterceptor.take(r)){ //task is token by the interceptor return; } if (mJSHandler == null){ return; } mJSHandler.post(WXThread.secure(r)); } @Override public void setInterceptor(Interceptor interceptor) { mInterceptor = interceptor; } public void post(Runnable r, Object token) { if (mJSHandler == null) { return; } if (isJSThread() && r != null) { r.run(); } else { Message m = Message.obtain(mJSHandler, WXThread.secure(r)); m.obj = token; m.sendToTarget(); } } void setTimeout(String callbackId, String time) { Message message = Message.obtain(); message.what = WXJSBridgeMsgType.SET_TIMEOUT; TimerInfo timerInfo = new TimerInfo(); timerInfo.callbackId = callbackId; timerInfo.time = (long) Float.parseFloat(time); message.obj = timerInfo; mJSHandler.sendMessageDelayed(message, timerInfo.time); } public void sendMessageDelayed(Message message, long delayMillis){ if (message == null || mJSHandler == null || mJSThread == null || !mJSThread.isWXThreadAlive() || mJSThread.getLooper() == null) { return; } mJSHandler.sendMessageDelayed(message,delayMillis); } public void removeMessage(int what,Object obj){ if (mJSHandler == null || mJSThread == null || !mJSThread.isWXThreadAlive() || mJSThread.getLooper() == null) { return; } mJSHandler.removeMessages(what, obj); } /** * Dispatch the native task to be executed. * @param instanceId {@link WXSDKInstance#mInstanceId} * @param tasks tasks to be executed * @param callback next tick id */ public int callNative(String instanceId, String tasks, String callback) { if (TextUtils.isEmpty(tasks)) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.e("[WXBridgeManager] callNative: call Native tasks is null"); } WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg("[WXBridgeManager] callNative: call Native tasks is null"); commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE); return IWXBridge.INSTANCE_RENDERING_ERROR; } if (WXEnvironment.isApkDebugable()) { mLodBuilder.append("[WXBridgeManager] callNative >>>> instanceId:").append(instanceId) .append(", tasks:").append(tasks).append(", callback:").append(callback); WXLogUtils.d(mLodBuilder.substring(0)); mLodBuilder.setLength(0); } if(mDestroyedInstanceId!=null &&mDestroyedInstanceId.contains(instanceId)){ return IWXBridge.DESTROY_INSTANCE; } long start = System.currentTimeMillis(); JSONArray array = JSON.parseArray(tasks); if(WXSDKManager.getInstance().getSDKInstance(instanceId)!=null) { WXSDKManager.getInstance().getSDKInstance(instanceId).jsonParseTime(System.currentTimeMillis() - start); } int size = array.size(); if (size > 0) { try { JSONObject task; for (int i = 0; i < size; ++i) { task = (JSONObject) array.get(i); if (task != null && WXSDKManager.getInstance().getSDKInstance(instanceId) != null) { if (TextUtils.equals(WXDomModule.WXDOM, (String) task.get(MODULE))) { sDomModule = getDomModule(instanceId); sDomModule.callDomMethod(task); sDomModule.mWXSDKInstance = null; } else { WXModuleManager.callModuleMethod(instanceId, (String) task.get(MODULE), (String) task.get(METHOD), (JSONArray) task.get(ARGS)); } } } } catch (Exception e) { WXLogUtils.e("[WXBridgeManager] callNative exception: ", e); WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg("[WXBridgeManager] callNative exception "+e.getCause()); commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE); } } if (UNDEFINED.equals(callback)) { return IWXBridge.INSTANCE_RENDERING_ERROR; } // get next tick getNextTick(instanceId, callback); return IWXBridge.INSTANCE_RENDERING; } public int callAddElement(String instanceId, String ref,String dom,String index, String callback){ if (WXEnvironment.isApkDebugable()) { mLodBuilder.append("[WXBridgeManager] callNative::callAddElement >>>> instanceId:").append(instanceId) .append(", ref:").append(ref).append(", dom:").append(dom).append(", callback:").append(callback); WXLogUtils.d(mLodBuilder.substring(0)); mLodBuilder.setLength(0); } if(mDestroyedInstanceId!=null && mDestroyedInstanceId.contains(instanceId)){ return IWXBridge.DESTROY_INSTANCE; } if (WXSDKManager.getInstance().getSDKInstance(instanceId) != null) { long start = System.currentTimeMillis(); JSONObject domObject = JSON.parseObject(dom); if (WXSDKManager.getInstance().getSDKInstance(instanceId) != null) { WXSDKManager.getInstance().getSDKInstance(instanceId).jsonParseTime(System.currentTimeMillis() - start); } sDomModule = getDomModule(instanceId); sDomModule.addElement(ref, domObject, Integer.parseInt(index)); } if (UNDEFINED.equals(callback)) { return IWXBridge.INSTANCE_RENDERING_ERROR; } // get next tick getNextTick(instanceId, callback); return IWXBridge.INSTANCE_RENDERING; } private void getNextTick(final String instanceId, final String callback) { addJSTask(METHOD_CALLBACK,instanceId, callback, "{}"); sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH); } private void addJSTask(final String method, final String instanceId, final Object... args) { post(new Runnable() { @Override public void run() { if (args == null || args.length == 0) { return; } ArrayList<Object> argsList = new ArrayList<>(); for (Object arg : args) { argsList.add(arg); } WXHashMap<String, Object> task = new WXHashMap<>(); task.put(KEY_METHOD, method); task.put(KEY_ARGS, argsList); if (mNextTickTasks.get(instanceId) == null) { ArrayList<WXHashMap<String, Object>> list = new ArrayList<>(); list.add(task); mNextTickTasks.put(instanceId, list); } else { mNextTickTasks.get(instanceId).add(task); } } }); } private void sendMessage(String instanceId, int what) { Message msg = Message.obtain(mJSHandler); msg.obj = instanceId; msg.what = what; msg.sendToTarget(); } /** * Initialize JavaScript framework * @param framework String representation of the framework to be init. */ public synchronized void initScriptsFramework(String framework) { Message msg = mJSHandler.obtainMessage(); msg.obj = framework; msg.what = WXJSBridgeMsgType.INIT_FRAMEWORK; msg.setTarget(mJSHandler); msg.sendToTarget(); } @Deprecated public void fireEvent(final String instanceId, final String ref, final String type, final Map<String, Object> data){ this.fireEvent(instanceId, ref, type, data, null); } /** * Do not direct invoke this method in Components, use {@link WXSDKInstance#fireEvent(String, String, Map, Map)} instead. * @param instanceId * @param ref * @param type * @param data * @param domChanges */ @Deprecated public void fireEvent(final String instanceId, final String ref, final String type, final Map<String, Object> data,final Map<String, Object> domChanges) { fireEventOnNode(instanceId,ref,type,data,domChanges); } /** * Notify the JavaScript about the event happened on Android */ public void fireEventOnNode(final String instanceId, final String ref, final String type, final Map<String, Object> data,final Map<String, Object> domChanges) { if (TextUtils.isEmpty(instanceId) || TextUtils.isEmpty(ref) || TextUtils.isEmpty(type) || mJSHandler == null) { return; } if (!checkMainThread()) { throw new WXRuntimeException( "fireEvent must be called by main thread"); } addJSTask(METHOD_FIRE_EVENT, instanceId, ref, type, data,domChanges); sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH); } private boolean checkMainThread() { return Looper.myLooper() == Looper.getMainLooper(); } /** * Invoke JavaScript callback. Use {@link JSCallback} instead. * @see #callback(String, String, String) */ @Deprecated public void callback(String instanceId, String callback,String data) { callback(instanceId, callback,data,false); } /** * Invoke JavaScript callback. Use {@link JSCallback} instead. */ @Deprecated public void callback(final String instanceId, final String callback, final Map<String, Object> data){ callback(instanceId,callback,data,false); } /** * Use {@link JSCallback} instead. * @param instanceId Weex Instance Id * @param callback callback referenece handle * @param data callback data * @param keepAlive if keep callback instance alive for later use */ @Deprecated public void callback(final String instanceId, final String callback, final Object data,boolean keepAlive) { callbackJavascript(instanceId,callback,data,keepAlive); } /** * Callback to Javascript function. * @param instanceId Weex Instance Id * @param callback callback referenece handle * @param data callback data * @param keepAlive if keep callback instance alive for later use */ void callbackJavascript(final String instanceId, final String callback, final Object data, boolean keepAlive) { if (TextUtils.isEmpty(instanceId) || TextUtils.isEmpty(callback) || mJSHandler == null) { return; } addJSTask(METHOD_CALLBACK, instanceId, callback, data,keepAlive); sendMessage(instanceId, WXJSBridgeMsgType.CALL_JS_BATCH); } /** * Refresh instance */ public void refreshInstance(final String instanceId, final WXRefreshData jsonData) { if (TextUtils.isEmpty(instanceId) || jsonData == null) { return; } mJSHandler.postDelayed(WXThread.secure(new Runnable() { @Override public void run() { invokeRefreshInstance(instanceId, jsonData); } }), 0); } private void invokeRefreshInstance(String instanceId, WXRefreshData refreshData) { try { if (!isJSFrameworkInit()) { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); if (instance != null) { instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance failed!"); } String err = "[WXBridgeManager] invokeRefreshInstance: framework.js uninitialized."; WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg(err); commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE); WXLogUtils.e(err); return; } long start = System.currentTimeMillis(); if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("refreshInstance >>>> instanceId:" + instanceId + ", data:" + refreshData.data + ", isDirty:" + refreshData.isDirty); } if (refreshData.isDirty) { return; } WXJSObject instanceIdObj = new WXJSObject(WXJSObject.String, instanceId); WXJSObject dataObj = new WXJSObject(WXJSObject.JSON, refreshData.data == null ? "{}" : refreshData.data); WXJSObject[] args = {instanceIdObj, dataObj}; invokeExecJS(instanceId, null, METHOD_REFRESH_INSTANCE, args); WXLogUtils.renderPerformanceLog("invokeRefreshInstance", System.currentTimeMillis() - start); } catch (Throwable e) { String err = "[WXBridgeManager] invokeRefreshInstance " + e.getCause(); WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg(err); commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_JS_EXECUTE); WXLogUtils.e(err); } } public void commitJSBridgeAlarmMonitor(String instanceId, WXErrorCode errCode) { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); if (instance == null) { return; } errCode.appendErrMsg(" url:"+instance.getBundleUrl()); instance.commitUTStab(IWXUserTrackAdapter.JS_BRIDGE, errCode); } public void commitAlert(final String type, final WXErrorCode errorCode) { final IWXUserTrackAdapter userTrackAdapter = WXSDKManager.getInstance().getIWXUserTrackAdapter(); if (userTrackAdapter == null || TextUtils.isEmpty(type) || errorCode == null) { return; } WXSDKManager.getInstance().postOnUiThread(new Runnable() { @Override public void run() { WXPerformance performance = null; if (errorCode != WXErrorCode.WX_SUCCESS) { performance = new WXPerformance(); performance.errCode = errorCode.getErrorCode(); performance.errMsg = errorCode.getErrorMsg(); WXLogUtils.e(performance.toString()); } userTrackAdapter.commit(WXEnvironment.getApplication(), null, type, performance, null); } }, 0); } /** * Create instance. */ public void createInstance(final String instanceId, final String template, final Map<String, Object> options, final String data) { if ( TextUtils.isEmpty(instanceId) || TextUtils.isEmpty(template) || mJSHandler == null) { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); if (instance != null) { instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance fail!"); } return; } post(new Runnable() { @Override public void run() { long start = System.currentTimeMillis(); invokeCreateInstance(instanceId, template, options, data); final long totalTime = System.currentTimeMillis() - start; WXSDKManager.getInstance().postOnUiThread(new Runnable() { @Override public void run() { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); if (instance != null) { instance.createInstanceFinished(totalTime); } } }, 0); } }, instanceId); } private void invokeCreateInstance(String instanceId, String template, Map<String, Object> options, String data) { initFramework(""); if (mMock) { mock(instanceId); } else { if (!isJSFrameworkInit()) { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); if (instance != null) { instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance " + "fail!"); } String err = "[WXBridgeManager] invokeCreateInstance: framework.js uninitialized."; WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg(err); commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE); WXLogUtils.e(err); return; } try { if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("createInstance >>>> instanceId:" + instanceId + ", options:" + WXJsonUtils.fromObjectToJSONString(options) + ", data:" + data); } WXJSObject instanceIdObj = new WXJSObject(WXJSObject.String, instanceId); WXJSObject instanceObj = new WXJSObject(WXJSObject.String, template); WXJSObject optionsObj = new WXJSObject(WXJSObject.JSON, options == null ? "{}" : WXJsonUtils.fromObjectToJSONString(options)); WXJSObject dataObj = new WXJSObject(WXJSObject.JSON, data == null ? "{}" : data); WXJSObject[] args = {instanceIdObj, instanceObj, optionsObj, dataObj}; invokeExecJS(instanceId, null, METHOD_CREATE_INSTANCE, args); } catch (Throwable e) { WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); if (instance != null) { instance.onRenderError(WXRenderErrorCode.WX_CREATE_INSTANCE_ERROR, "createInstance failed!"); } String err = "[WXBridgeManager] invokeCreateInstance " + e.getCause(); WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg(err); commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE); WXLogUtils.e(err); } } } private void mock(String instanceId) { } public void destroyInstance(final String instanceId) { if ( mJSHandler == null || TextUtils.isEmpty(instanceId)) { return; } if(mDestroyedInstanceId!=null) { mDestroyedInstanceId.add(instanceId); } // clear message with instanceId mJSHandler.removeCallbacksAndMessages(instanceId); post(new Runnable() { @Override public void run() { removeTaskByInstance(instanceId); invokeDestroyInstance(instanceId); } }, instanceId); } private void removeTaskByInstance(String instanceId) { mNextTickTasks.removeFromMapAndStack(instanceId); } private void invokeDestroyInstance(String instanceId) { try { if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("destroyInstance >>>> instanceId:" + instanceId); } if(sDomModule!=null && sDomModule.mWXSDKInstance!=null && TextUtils.equals(instanceId,sDomModule.mWXSDKInstance.getInstanceId())){ sDomModule.mWXSDKInstance=null; } WXJSObject instanceIdObj = new WXJSObject(WXJSObject.String, instanceId); WXJSObject[] args = {instanceIdObj}; invokeExecJS(instanceId, null, METHOD_DESTROY_INSTANCE, args); } catch (Throwable e) { String err = "[WXBridgeManager] invokeDestroyInstance " + e.getCause(); WXErrorCode.WX_ERR_INVOKE_NATIVE.appendErrMsg(err); commitJSBridgeAlarmMonitor(instanceId, WXErrorCode.WX_ERR_INVOKE_NATIVE); WXLogUtils.e(err); } } @Override public boolean handleMessage(Message msg) { if (msg == null) { return false; } int what = msg.what; switch (what) { case WXJSBridgeMsgType.INIT_FRAMEWORK: invokeInitFramework(msg); break; case WXJSBridgeMsgType.CALL_JS_BATCH: invokeCallJSBatch(msg); break; case WXJSBridgeMsgType.SET_TIMEOUT: TimerInfo timerInfo = (TimerInfo) msg.obj; if(timerInfo == null){ break; } WXJSObject obj = new WXJSObject(WXJSObject.String, timerInfo.callbackId); WXJSObject[] args = {obj}; invokeExecJS("", null, METHOD_SET_TIMEOUT, args); break; case WXJSBridgeMsgType.MODULE_TIMEOUT: if(msg.obj == null){ break; } args = createTimerArgs(msg.arg1, (Integer) msg.obj, false); invokeExecJS(String.valueOf(msg.arg1), null, METHOD_CALL_JS, args); break; case WXJSBridgeMsgType.MODULE_INTERVAL: if(msg.obj == null){ break; } WXTimerModule.setInterval((Integer) msg.obj, msg.arg2, msg.arg1); args = createTimerArgs(msg.arg1, (Integer) msg.obj, true); invokeExecJS(String.valueOf(msg.arg1), null, METHOD_CALL_JS, args); break; default: break; } return false; } private void invokeExecJS(String instanceId, String namespace, String function, WXJSObject[] args){ if (WXEnvironment.isApkDebugable()) { mLodBuilder.append("callJS >>>> instanceId:").append(instanceId) .append("function:").append(function) .append(" tasks:").append(WXJsonUtils.fromObjectToJSONString(args)); WXLogUtils.d(mLodBuilder.substring(0)); mLodBuilder.setLength(0); } // if(mDestroyedInstanceId!=null && !mDestroyedInstanceId.contains(instanceId)) { mWXBridge.execJS(instanceId, namespace, function, args); // }else{ // WXLogUtils.w("invokeExecJS: instanceId: "+instanceId+"was destroy !! ExecJS abandon !!"); // } } private WXJSObject[] createTimerArgs(int instanceId, int funcId, boolean keepAlive) { ArrayList<Object> argsList = new ArrayList<>(); argsList.add(funcId); argsList.add(new HashMap<>()); argsList.add(keepAlive); WXHashMap<String, Object> task = new WXHashMap<>(); task.put(KEY_METHOD, METHOD_CALLBACK); task.put(KEY_ARGS, argsList); Object[] tasks={task}; return new WXJSObject[]{ new WXJSObject(WXJSObject.String, String.valueOf(instanceId)), new WXJSObject(WXJSObject.JSON, WXJsonUtils.fromObjectToJSONString(tasks))}; } private void invokeInitFramework(Message msg) { String framework = ""; if (msg.obj != null) { framework = (String) msg.obj; } if(WXUtils.getAvailMemory(WXEnvironment.getApplication()) > LOW_MEM_VALUE) { initFramework(framework); } } private void initFramework(String framework){ if (!isJSFrameworkInit()) { if (TextUtils.isEmpty(framework)) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.d("weex JS framework from assets"); } framework = WXFileUtils.loadAsset("main.js", WXEnvironment.getApplication()); } if (TextUtils.isEmpty(framework)) { mInit = false; WXErrorCode.WX_ERR_JS_FRAMEWORK.appendErrMsg("JS Framework is empty!"); commitAlert(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_FRAMEWORK); return; } try { long start = System.currentTimeMillis(); if(mWXBridge.initFramework(framework, assembleDefaultOptions())==INIT_FRAMEWORK_OK){ WXEnvironment.sJSLibInitTime = System.currentTimeMillis() - start; WXLogUtils.renderPerformanceLog("initFramework", WXEnvironment.sJSLibInitTime); WXEnvironment.sSDKInitTime = System.currentTimeMillis() - WXEnvironment.sSDKInitStart; WXLogUtils.renderPerformanceLog("SDKInitTime", WXEnvironment.sSDKInitTime); mInit = true; execRegisterFailTask(); WXEnvironment.JsFrameworkInit = true; registerDomModule(); commitAlert(IWXUserTrackAdapter.JS_FRAMEWORK,WXErrorCode.WX_SUCCESS); }else{ WXLogUtils.e("[WXBridgeManager] invokeInitFramework ExecuteJavaScript fail"); WXErrorCode.WX_ERR_JS_FRAMEWORK.appendErrMsg("[WXBridgeManager] invokeInitFramework ExecuteJavaScript fail"); commitAlert(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_FRAMEWORK); } } catch (Throwable e) { WXLogUtils.e("[WXBridgeManager] invokeInitFramework ", e); WXErrorCode.WX_ERR_JS_FRAMEWORK.appendErrMsg("[WXBridgeManager] invokeInitFramework exception!"); commitAlert(IWXUserTrackAdapter.JS_FRAMEWORK, WXErrorCode.WX_ERR_JS_FRAMEWORK); } } } @SuppressWarnings("unchecked") private void invokeCallJSBatch(Message message) { if (mNextTickTasks.isEmpty() || !isJSFrameworkInit()) { if (!isJSFrameworkInit()) { WXLogUtils.e("[WXBridgeManager] invokeCallJSBatch: framework.js uninitialized."); } return; } try { Object instanceId = message.obj; Object task = null; Stack<String> instanceStack = mNextTickTasks.getInstanceStack(); int size = instanceStack.size(); for (int i = size - 1; i >= 0; i--) { instanceId = instanceStack.get(i); task = mNextTickTasks.remove(instanceId); if (task != null && !((ArrayList) task).isEmpty()) { break; } } task = ((ArrayList) task).toArray(); WXJSObject[] args = { new WXJSObject(WXJSObject.String, instanceId), new WXJSObject(WXJSObject.JSON, WXJsonUtils.fromObjectToJSONString(task))}; invokeExecJS(String.valueOf(instanceId), null, METHOD_CALL_JS, args); } catch (Throwable e) { WXLogUtils.e("WXBridgeManager", e); WXErrorCode.WX_ERR_JS_EXECUTE.appendErrMsg(e.toString()); commitJSBridgeAlarmMonitor(message.obj.toString(), WXErrorCode.WX_ERR_JS_EXECUTE); } // If task is not empty, loop until it is empty if (!mNextTickTasks.isEmpty()) { mJSHandler.sendEmptyMessage(WXJSBridgeMsgType.CALL_JS_BATCH); } } private WXParams assembleDefaultOptions() { Map<String, String> config = WXEnvironment.getConfig(); WXParams wxParams = new WXParams(); wxParams.setPlatform(config.get("os")); wxParams.setOsVersion(config.get("sysVersion")); wxParams.setAppVersion(config.get("appVersion")); wxParams.setWeexVersion(config.get("weexVersion")); wxParams.setDeviceModel(config.get("sysModel")); wxParams.setShouldInfoCollect(config.get("infoCollect")); wxParams.setLogLevel(config.get(WXConfig.logLevel)); String appName = config.get("appName"); if (!TextUtils.isEmpty(appName)) { wxParams.setAppName(appName); } wxParams.setDeviceWidth(TextUtils.isEmpty(config.get("deviceWidth")) ? String.valueOf(WXViewUtils.getScreenWidth(WXEnvironment.sApplication)) : config.get("deviceWidth")); wxParams.setDeviceHeight(TextUtils.isEmpty(config.get("deviceHeight")) ? String.valueOf(WXViewUtils.getScreenHeight(WXEnvironment.sApplication)) : config.get("deviceHeight")); return wxParams; } private void execRegisterFailTask() { int moduleCount = mRegisterModuleFailList.size(); if (moduleCount > 0) { for (int i = 0; i < moduleCount; ++i) { registerModules(mRegisterModuleFailList.get(i)); } mRegisterModuleFailList.clear(); WXLogUtils.e("[WXBridgeManager] execRegisterFailTask register module fail"); } int componentCount = mRegisterComponentFailList.size(); if (componentCount > 0) { for (int i = 0; i < componentCount; ++i) { registerComponents(mRegisterComponentFailList.get(i)); } mRegisterComponentFailList.clear(); WXLogUtils.e("[WXBridgeManager] execRegisterFailTask register component fail"); } } /** * Register Android module * @param modules the format is like * {'dom':['updateAttrs','updateStyle'],'event':['openUrl']} */ public void registerModules(final Map<String, Object> modules) { if ( mJSHandler == null || modules == null || modules.size() == 0) { return; } post(new Runnable() { @Override public void run() { invokeRegisterModules(modules); } }, null); } /** * Registered component */ public void registerComponents(final List<Map<String, String>> components) { if ( mJSHandler == null || components == null || components.size() == 0) { return; } post(new Runnable() { @Override public void run() { invokeRegisterComponents(components); } }, null); } private boolean isJSThread() { return mJSThread != null && mJSThread.getId() == Thread.currentThread().getId(); } private void invokeRegisterModules(Map<String, Object> modules) { if (modules == null || !isJSFrameworkInit()) { if (!isJSFrameworkInit()) { WXLogUtils.e("[WXBridgeManager] invokeCallJSBatch: framework.js uninitialized."); } mRegisterModuleFailList.add(modules); return; } WXJSObject[] args = {new WXJSObject(WXJSObject.JSON, WXJsonUtils.fromObjectToJSONString(modules))}; try { mWXBridge.execJS("", null, METHOD_REGISTER_MODULES, args); } catch (Throwable e) { WXLogUtils.e("[WXBridgeManager] invokeRegisterModules:", e); } } private void invokeRegisterComponents(List<Map<String, String>> components) { if (components == null || !isJSFrameworkInit()) { if (!isJSFrameworkInit()) { WXLogUtils.e("[WXBridgeManager] invokeCallJSBatch: framework.js uninitialized."); } mRegisterComponentFailList.add(components); return; } WXJSObject[] args = {new WXJSObject(WXJSObject.JSON, WXJsonUtils.fromObjectToJSONString(components))}; try { mWXBridge.execJS("", null, METHOD_REGISTER_COMPONENTS, args); } catch (Throwable e) { WXLogUtils.e("[WXBridgeManager] invokeRegisterComponents ", e); } } public void destroy() { if (mJSThread != null) { mJSThread.quit(); } mBridgeManager = null; if(mDestroyedInstanceId!=null){ mDestroyedInstanceId.clear(); } } /** * Report JavaScript Exception */ public void reportJSException(String instanceId, String function, String exception) { if (WXEnvironment.isApkDebugable()) { WXLogUtils.e("reportJSException >>>> instanceId:" + instanceId + ", exception function:" + function + ", exception:" + exception); } StringBuilder errorMsg=new StringBuilder(); WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId); if (instance != null) { // TODO add errCode instance.onJSException(null, function, exception); errorMsg.append("bundleUrl:"+instance.getBundleUrl()); }else{ errorMsg.append(" bundleUrl:instance is null!"); } errorMsg.append(" exception function:"+function); errorMsg.append(" exception:"+exception); WXErrorCode.WX_ERR_JS_EXECUTE.appendErrMsg(errorMsg.toString()); commitJSBridgeAlarmMonitor(instanceId,WXErrorCode.WX_ERR_JS_EXECUTE); } public static class TimerInfo { public String callbackId; public long time; public String instanceId; } private void registerDomModule() throws WXException { if (sDomModule == null) sDomModule = new WXDomModule(); /** Tell Javascript Framework what methods you have. This is Required.**/ Map<String,Object> domMap=new HashMap<>(); domMap.put(WXDomModule.WXDOM,WXDomModule.METHODS); registerModules(domMap); } private WXDomModule getDomModule(String instanceId) { sDomModule.mWXSDKInstance = WXSDKManager.getInstance().getSDKInstance(instanceId); return sDomModule; } }