blob: e1ed82f6e4da63c54b7938c26cb004e6ca71a36b [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.dom;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import com.taobao.weex.WXEnvironment;
import com.taobao.weex.WXSDKManager;
import com.taobao.weex.common.WXRuntimeException;
import com.taobao.weex.common.WXThread;
import com.taobao.weex.dom.action.AbstractAddElementAction;
import com.taobao.weex.dom.action.TraceableAction;
import com.taobao.weex.tracing.Stopwatch;
import com.taobao.weex.tracing.WXTracing;
import com.taobao.weex.ui.WXRenderManager;
import com.taobao.weex.utils.WXUtils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
* Class for managing dom operation. This class works as the client in the command pattern, it
* will call {@link DOMActionContextImpl} for creating command object and invoking corresponding
* operation.
* Methods in this class normally need to be invoked in dom thread, otherwise, {@link
* WXRuntimeException} may be thrown.
*/
public final class WXDomManager {
private WXThread mDomThread;
/** package **/
Handler mDomHandler;
private WXRenderManager mWXRenderManager;
private ConcurrentHashMap<String, DOMActionContextImpl> mDomRegistries;
public WXDomManager(WXRenderManager renderManager) {
mWXRenderManager = renderManager;
mDomRegistries = new ConcurrentHashMap<>();
mDomThread = new WXThread("WeeXDomThread", new WXDomHandler(this));
mDomHandler = mDomThread.getHandler();
}
public void sendEmptyMessageDelayed(int what, long delayMillis) {
if (mDomHandler == null || mDomThread == null
|| !mDomThread.isWXThreadAlive() || mDomThread.getLooper() == null) {
return;
}
mDomHandler.sendEmptyMessageDelayed(what, delayMillis);
}
public void sendMessage(Message msg) {
sendMessageDelayed(msg, 0);
}
public void sendMessageDelayed(Message msg, long delay) {
if (msg == null || mDomHandler == null || mDomThread == null
|| !mDomThread.isWXThreadAlive() || mDomThread.getLooper() == null) {
return;
}
mDomHandler.sendMessageDelayed(msg,delay);
}
/**
* Remove the specified dom statement. This is called when {@link WXSDKManager} destroy
* instances.
* @param instanceId {@link com.taobao.weex.WXSDKInstance#mInstanceId} for the instance
*/
public void removeDomStatement(String instanceId) {
if (!WXUtils.isUiThread()) {
throw new WXRuntimeException("[WXDomManager] removeDomStatement");
}
final DOMActionContextImpl statement = mDomRegistries.remove(instanceId);
if (statement != null) {
post(new Runnable() {
@Override
public void run() {
statement.destroy();
}
});
}
}
public void post(Runnable task) {
if (mDomHandler == null || task == null || mDomThread == null || !mDomThread.isWXThreadAlive()
|| mDomThread.getLooper() == null) {
return;
}
mDomHandler.post(WXThread.secure(task));
}
/**
* Destroy current instance
*/
public void destroy() {
if (mDomThread != null && mDomThread.isWXThreadAlive()) {
mDomThread.quit();
}
if (mDomRegistries != null) {
mDomRegistries.clear();
}
mDomHandler = null;
mDomThread = null;
}
private boolean isDomThread() {
return !WXEnvironment.isApkDebugable() || Thread.currentThread().getId() == mDomThread.getId();
}
/**
* Batch the execution of {@link DOMActionContextImpl}
*/
void batch() {
throwIfNotDomThread();
Iterator<Entry<String, DOMActionContextImpl>> iterator = mDomRegistries.entrySet().iterator();
while (iterator.hasNext()) {
iterator.next().getValue().batch();
}
}
void consumeRenderTask(String instanceId) {
throwIfNotDomThread();
DOMActionContextImpl context = mDomRegistries.get(instanceId);
if(context != null) {
context.consumeRenderTasks();
}
}
private void throwIfNotDomThread(){
if (!isDomThread()) {
throw new WXRuntimeException("dom operation must be done in dom thread");
}
}
public void executeAction(String instanceId, DOMAction action, boolean createContext) {
DOMActionContext context = mDomRegistries.get(instanceId);
if(context == null){
if(createContext){
DOMActionContextImpl oldStatement = new DOMActionContextImpl(instanceId, mWXRenderManager);
mDomRegistries.put(instanceId, oldStatement);
context = oldStatement;
}else{
//Instance not existed.
return;
}
}
long domStart = System.currentTimeMillis();
long domNanos = System.nanoTime();
action.executeDom(context);
if (WXTracing.isAvailable()) {
domNanos = System.nanoTime() - domNanos;
if (!(action instanceof AbstractAddElementAction) && action instanceof TraceableAction) {
WXTracing.TraceEvent domExecuteEvent = WXTracing.newEvent("DomExecute", context.getInstanceId(), ((TraceableAction) action).mTracingEventId);
domExecuteEvent.duration = Stopwatch.nanosToMillis(domNanos);
domExecuteEvent.ts = domStart;
domExecuteEvent.submit();
}
}
}
public DOMActionContext getDomContext(String instanceId){
return mDomRegistries.get(instanceId);
}
/**
* @param action
* @param createContext only true when create body
*/
public void postAction(String instanceId,DOMAction action, boolean createContext){
postActionDelay(instanceId, action, createContext, 0);
}
/**
* @param action
* @param createContext only true when create body
*/
public void postActionDelay(String instanceId,DOMAction action,
boolean createContext, long delay){
if(action == null){
return;
}
Message msg = Message.obtain();
msg.what = WXDomHandler.MsgType.WX_EXECUTE_ACTION;
WXDomTask task = new WXDomTask();
task.instanceId = instanceId;
task.args = new ArrayList<>();
task.args.add(action);
task.args.add(createContext);
msg.obj = task;
sendMessageDelayed(msg, delay);
}
public void postRenderTask(@NonNull String instanceId) {
Message msg = Message.obtain();
msg.what = WXDomHandler.MsgType.WX_CONSUME_RENDER_TASKS;
WXDomTask task = new WXDomTask();
task.instanceId = instanceId;
msg.obj = task;
sendMessage(msg);
}
}