/**
*
* 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;
}
}