blob: f5441230ac6e85e721d9ef32292a84986696ba0b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.Intent;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.Menu;
import com.alibaba.fastjson.JSONArray;
import com.taobao.weex.WXSDKInstance;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.adapter.IWXUserTrackAdapter;
import com.taobao.weex.common.Destroyable;
import com.taobao.weex.common.WXErrorCode;
import com.taobao.weex.common.WXException;
import com.taobao.weex.common.WXModule;
import com.taobao.weex.ui.config.ConfigModuleFactory;
import com.taobao.weex.ui.module.WXDomModule;
import com.taobao.weex.ui.module.WXTimerModule;
import com.taobao.weex.utils.WXExceptionUtils;
import com.taobao.weex.utils.WXLogUtils;
import com.taobao.weex.utils.cache.RegisterCache;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* Manager class for weex module. There are two types of modules in weex, one is instance-level module,
* the other is global-level module. Instance-level module will be created every time an instance
* is created, while global-level module will be singleton in {@link com.taobao.weex.WXSDKEngine}.
*/
public class WXModuleManager {
/**
* module class object dictionary
*/
private static volatile ConcurrentMap<String, ModuleFactoryImpl> sModuleFactoryMap = new ConcurrentHashMap<>();
private static Map<String, WXModule> sGlobalModuleMap = new HashMap<>();
private static Map<String, WXDomModule> sDomModuleMap = new HashMap<>();
/**
* module object dictionary
* K : instanceId, V : Modules
*/
private static Map<String, Map<String, WXModule>> sInstanceModuleMap = new ConcurrentHashMap<>();
public static boolean registerModule(Map<String, RegisterCache.ModuleCache> moduleCacheMap) {
if (moduleCacheMap.isEmpty())
return true;
final Iterator<Entry<String, RegisterCache.ModuleCache>> iterator = moduleCacheMap.entrySet().iterator();
WXBridgeManager.getInstance()
.postWithName(new Runnable() {
@Override
public void run() {
Map<String, Object> modules = new HashMap<>();
while (iterator.hasNext()) {
Entry<String, RegisterCache.ModuleCache> next = iterator.next();
RegisterCache.ModuleCache value = next.getValue();
String moduleName = value.name;
if (TextUtils.equals(moduleName, WXDomModule.WXDOM)) {
WXLogUtils.e("Cannot registered module with name 'dom'.");
continue;
}
if (sModuleFactoryMap != null && sModuleFactoryMap.containsKey(moduleName)) {
WXLogUtils.w("WXComponentRegistry Duplicate the Module name: " + moduleName);
}
ModuleFactory factory = value.factory;
try {
registerNativeModule(moduleName, factory);
} catch (WXException e) {
WXLogUtils.e("registerNativeModule" + e);
}
if (value.global) {
try {
WXModule wxModule = factory.buildInstance();
wxModule.setModuleName(moduleName);
sGlobalModuleMap.put(moduleName, wxModule);
} catch (Exception e) {
WXLogUtils.e(moduleName + " class must have a default constructor without params. ", e);
}
}
try {
sModuleFactoryMap.put(moduleName, new ModuleFactoryImpl(factory));
} catch (Throwable e) {
}
modules.put(moduleName, factory.getMethods());
}
WXSDKManager.getInstance().registerModules(modules);
}
},null,"registerModule From Cache");
return true;
}
/**
* Register module to JavaScript and Android
*/
public static boolean registerModule(final String moduleName, final ModuleFactory factory, final boolean global) throws WXException {
if (moduleName == null || factory == null) {
return false;
}
if (TextUtils.equals(moduleName,WXDomModule.WXDOM)) {
WXLogUtils.e("Cannot registered module with name 'dom'.");
return false;
}
if(RegisterCache.getInstance().cacheModule(moduleName,factory,global)) {
return true;
}
//execute task in js thread to make sure register order is same as the order invoke register method.
WXBridgeManager.getInstance()
.postWithName(new Runnable() {
@Override
public void run() {
if (sModuleFactoryMap != null && sModuleFactoryMap.containsKey(moduleName)) {
WXLogUtils.w("WXComponentRegistry Duplicate the Module name: " + moduleName);
}
try {
registerNativeModule(moduleName, factory);
} catch (WXException e) {
WXLogUtils.e("registerNativeModule" + e);
}
if (global) {
try {
WXModule wxModule = factory.buildInstance();
wxModule.setModuleName(moduleName);
sGlobalModuleMap.put(moduleName, wxModule);
} catch (Exception e) {
WXLogUtils.e(moduleName + " class must have a default constructor without params. ", e);
}
}
registerJSModule(moduleName, factory);
try {
sModuleFactoryMap.put(moduleName, new ModuleFactoryImpl(factory));
} catch (Throwable e) {
}
}
},null,"registerModule");
return true;
}
static boolean registerNativeModule(String moduleName, ModuleFactory factory) throws WXException {
if (factory == null) {
return false;
}
try {
if (!sModuleFactoryMap.containsKey(moduleName) ) {
sModuleFactoryMap.put(moduleName, new ModuleFactoryImpl(factory));
}
}catch (ArrayStoreException e){
e.printStackTrace();
//ignore:
//may throw this exception:
//java.lang.String cannot be stored in an array of type java.util.HashMap$HashMapEntry[]
WXLogUtils.e("[WXModuleManager] registerNativeModule Error moduleName:" + moduleName + " Error:" + e.toString());
}
return true;
}
static boolean registerJSModule(String moduleName, ModuleFactory factory) {
Map<String, Object> modules = new HashMap<>();
modules.put(moduleName, factory.getMethods());
WXSDKManager.getInstance().registerModules(modules);
return true;
}
static Object callModuleMethod(final String instanceId, String moduleStr, String methodStr, JSONArray args) {
ModuleFactory factory = sModuleFactoryMap.get(moduleStr).mFactory;
if(factory == null){
WXLogUtils.e("[WXModuleManager] module factory not found.");
return null;
}
final WXModule wxModule = findModule(instanceId, moduleStr,factory);
if (wxModule == null) {
return null;
}
WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
wxModule.mWXSDKInstance = instance;
final Invoker invoker = factory.getMethodInvoker(methodStr);
try {
if(instance != null) {
IWXUserTrackAdapter userTrackAdapter = WXSDKManager.getInstance().getIWXUserTrackAdapter();
if(userTrackAdapter != null) {
HashMap<String, Serializable> data = new HashMap<String, Serializable>();
data.put(IWXUserTrackAdapter.MONITOR_ERROR_CODE, "101");
data.put(IWXUserTrackAdapter.MONITOR_ARG, moduleStr + "." + methodStr);
data.put(IWXUserTrackAdapter.MONITOR_ERROR_MSG, instance.getBundleUrl());
userTrackAdapter.commit(instance.getContext(), null, IWXUserTrackAdapter.INVOKE_MODULE, null, data);
}
return dispatchCallModuleMethod(instance,wxModule,args,invoker);
} else {
WXLogUtils.e("callModuleMethod >>> instance is null");
return null;
}
} catch (Exception e) {
WXLogUtils.e("callModuleMethod >>> invoke module:" + moduleStr + ", method:" + methodStr + " failed. ", e);
return null;
} finally {
if (wxModule instanceof WXDomModule || wxModule instanceof WXTimerModule) {
wxModule.mWXSDKInstance = null;
}
}
}
private static Object dispatchCallModuleMethod(@NonNull WXSDKInstance instance, @NonNull WXModule wxModule,
@NonNull JSONArray args, @NonNull Invoker invoker) throws Exception{
if(!instance.isPreRenderMode()) {
return instance.getNativeInvokeHelper().invoke(wxModule,invoker,args);
}
// we are in preRender mode
if(invoker.isRunOnUIThread()) {/*ASYNC CALL*/
// DOMAction moduleInvocationAction = Actions.getModuleInvocationAction(wxModule,args,invoker);
// WXSDKManager.getInstance().getWXDomManager().postAction(instance.getInstanceId(), moduleInvocationAction,false);
return null;
} else {/*SYNC CALL*/
return instance.getNativeInvokeHelper().invoke(wxModule,invoker,args);
}
}
public static boolean hasModule(String module) {
return sGlobalModuleMap.containsKey(module) || sModuleFactoryMap.containsKey(module);
}
private static WXModule findModule(String instanceId, String moduleStr,ModuleFactory factory) {
// find WXModule
WXModule wxModule = sGlobalModuleMap.get(moduleStr);
//not global module
if (wxModule == null) {
Map<String, WXModule> moduleMap = sInstanceModuleMap.get(instanceId);
if (moduleMap == null) {
moduleMap = new ConcurrentHashMap<>();
sInstanceModuleMap.put(instanceId, moduleMap);
}
// if cannot find the Module, create a new Module and save it
wxModule = moduleMap.get(moduleStr);
if (wxModule == null) {
try {
if(factory instanceof ConfigModuleFactory){
WXSDKInstance instance = WXSDKManager.getInstance().getSDKInstance(instanceId);
wxModule = ((ConfigModuleFactory) factory).buildInstance(instance);
}else{
wxModule = factory.buildInstance();
}
wxModule.setModuleName(moduleStr);
} catch (Exception e) {
WXLogUtils.e(moduleStr + " module build instace failed.", e);
return null;
}
moduleMap.put(moduleStr, wxModule);
}
}
return wxModule;
}
/**Hook Activity life cycle callback begin***/
public static void onActivityCreate(String instanceId){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onActivityCreate();
} else {
WXLogUtils.w("onActivityCreate can not find the " + key + " module");
}
}
}
}
public static void onActivityStart(String instanceId){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onActivityStart();
} else {
WXLogUtils.w("onActivityStart can not find the " + key + " module");
}
}
}
}
public static void onActivityPause(String instanceId){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onActivityPause();
} else {
WXLogUtils.w("onActivityPause can not find the " + key + " module");
}
}
}
}
public static void onActivityResume(String instanceId){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onActivityResume();
} else {
WXLogUtils.w("onActivityResume can not find the " + key + " module");
}
}
}
}
public static void onActivityStop(String instanceId){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onActivityStop();
} else {
WXLogUtils.w("onActivityStop can not find the " + key + " module");
}
}
}
}
public static void onActivityDestroy(String instanceId){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onActivityDestroy();
} else {
WXLogUtils.w("onActivityDestroy can not find the " + key + " module");
}
}
}
}
public static boolean onActivityBack(String instanceId){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
return module.onActivityBack();
} else {
WXLogUtils.w("onActivityCreate can not find the " + key + " module");
}
}
}
return false;
}
public static void onActivityResult(String instanceId,int requestCode, int resultCode, Intent data){
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onActivityResult(requestCode, resultCode, data);
} else {
WXLogUtils.w("onActivityResult can not find the " + key + " module");
}
}
}
}
public static boolean onCreateOptionsMenu(String instanceId,Menu menu) {
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onCreateOptionsMenu(menu);
} else {
WXLogUtils.w("onActivityResult can not find the " + key + " module");
}
}
}
return false;
}
public static void onRequestPermissionsResult(String instanceId ,int requestCode, String[] permissions, int[] grantResults) {
Map<String, WXModule> modules = sInstanceModuleMap.get(instanceId);
if(modules!=null) {
for (String key : modules.keySet()) {
WXModule module = modules.get(key);
if (module != null) {
module.onRequestPermissionsResult(requestCode, permissions, grantResults);
} else {
WXLogUtils.w("onActivityResult can not find the " + key + " module");
}
}
}
}
public static void destroyInstanceModules(String instanceId) {
sDomModuleMap.remove(instanceId);
Map<String, WXModule> moduleMap = sInstanceModuleMap.remove(instanceId);
if (moduleMap == null || moduleMap.size() < 1) {
return;
}
Iterator<Entry<String, WXModule>> iterator = moduleMap.entrySet().iterator();
Entry<String, WXModule> entry;
while (iterator.hasNext()) {
entry = iterator.next();
WXModule module = entry.getValue();
if(module instanceof Destroyable){
((Destroyable)module).destroy();
}
}
}
public static void createDomModule(WXSDKInstance instance){
if(instance != null) {
sDomModuleMap.put(instance.getInstanceId(), new WXDomModule(instance));
}
}
public static void destoryDomModule(String instanceID){
sDomModuleMap.remove(instanceID);
}
public static WXDomModule getDomModule(String instanceId){
return sDomModuleMap.get(instanceId);
}
public static void reload(){
if (sModuleFactoryMap != null && sModuleFactoryMap.size() > 0) {
for (Map.Entry<String, ModuleFactoryImpl> entry : sModuleFactoryMap.entrySet()) {
try {
registerJSModule(entry.getKey(), entry.getValue().mFactory);
} catch (Throwable e) {
}
}
}
}
/**
* registerWhenCreateInstance
*/
public static void registerWhenCreateInstance(){
if (sModuleFactoryMap != null && sModuleFactoryMap.size() > 0) {
for (Map.Entry<String, ModuleFactoryImpl> entry : sModuleFactoryMap.entrySet()) {
try {
if (!entry.getValue().hasRigster) {
registerJSModule(entry.getKey(), entry.getValue().mFactory);
}
} catch (Throwable e) {
}
}
}
}
/**
* resetAllModuleState
*/
public static void resetAllModuleState() {
if (sModuleFactoryMap != null && sModuleFactoryMap.size() > 0) {
for (Map.Entry<String, ModuleFactoryImpl> entry : sModuleFactoryMap.entrySet()) {
entry.getValue().hasRigster = false;
}
}
}
/**
* resetModuleState
* @param module
* @param state
*/
public static void resetModuleState(String module, boolean state) {
if (sModuleFactoryMap != null && sModuleFactoryMap.size() > 0) {
for (Map.Entry<String, ModuleFactoryImpl> entry : sModuleFactoryMap.entrySet()) {
try {
if (entry.getKey() != null && entry.getKey().equals(module)) {
entry.getValue().hasRigster = state;
}
} catch (Throwable e) {
}
}
}
}
}