Repository: incubator-weex Updated Branches: refs/heads/0.14-dev 64b77b1d4 -> 6dc2c8ee1
* [android] Fix the problem of clearTimeout and clearInterval. Due the implementation of [int auto-boxing](https://stackoverflow.com/questions/3130311/weird-integer-boxing-in-java) and [messageQueue](https://stackoverflow.com/questions/36190851/android-os-handler-removemessages-doesnt-work-when-poxing-value-type-as-msg-obj), clearTimeout and clearTimeout won't work when funId is greater than 127. It works fine when funId is smaller than 127. Ref this [demo](http://dotwe.org/vue/1c7b541231eb86db98afe3c1b48bcdcc) Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/fefe2e8a Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/fefe2e8a Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/fefe2e8a Branch: refs/heads/0.14-dev Commit: fefe2e8a8787b126c22c227dbac39bb979300579 Parents: 202c180 Author: YorkShen <shenyua...@gmail.com> Authored: Mon Jun 12 12:06:43 2017 +0800 Committer: YorkShen <shenyua...@gmail.com> Committed: Mon Jun 12 14:39:53 2017 +0800 ---------------------------------------------------------------------- .../taobao/weex/ui/module/WXTimerModule.java | 87 +++++++++++--------- .../weex/ui/module/WXTimerModuleTest.java | 44 +++++++--- 2 files changed, 80 insertions(+), 51 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/fefe2e8a/android/sdk/src/main/java/com/taobao/weex/ui/module/WXTimerModule.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/main/java/com/taobao/weex/ui/module/WXTimerModule.java b/android/sdk/src/main/java/com/taobao/weex/ui/module/WXTimerModule.java index 2220859..caa28a6 100644 --- a/android/sdk/src/main/java/com/taobao/weex/ui/module/WXTimerModule.java +++ b/android/sdk/src/main/java/com/taobao/weex/ui/module/WXTimerModule.java @@ -18,56 +18,61 @@ */ package com.taobao.weex.ui.module; +import static com.taobao.weex.bridge.WXBridgeManager.KEY_ARGS; +import static com.taobao.weex.bridge.WXBridgeManager.KEY_METHOD; +import static com.taobao.weex.bridge.WXBridgeManager.METHOD_CALLBACK; +import static com.taobao.weex.bridge.WXBridgeManager.METHOD_CALL_JS; +import static com.taobao.weex.common.WXJSBridgeMsgType.MODULE_INTERVAL; +import static com.taobao.weex.common.WXJSBridgeMsgType.MODULE_TIMEOUT; + import android.os.Handler; import android.os.Message; +import android.support.annotation.IntDef; import android.support.annotation.IntRange; import android.support.annotation.VisibleForTesting; - +import android.util.SparseArray; import com.taobao.weex.WXSDKManager; import com.taobao.weex.annotation.JSMethod; import com.taobao.weex.bridge.WXBridgeManager; import com.taobao.weex.bridge.WXHashMap; import com.taobao.weex.bridge.WXJSObject; import com.taobao.weex.common.Destroyable; -import com.taobao.weex.common.WXJSBridgeMsgType; import com.taobao.weex.common.WXModule; import com.taobao.weex.dom.action.Actions; import com.taobao.weex.utils.WXJsonUtils; import com.taobao.weex.utils.WXLogUtils; - +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.HashMap; -import static com.taobao.weex.bridge.WXBridgeManager.KEY_ARGS; -import static com.taobao.weex.bridge.WXBridgeManager.KEY_METHOD; -import static com.taobao.weex.bridge.WXBridgeManager.METHOD_CALLBACK; -import static com.taobao.weex.bridge.WXBridgeManager.METHOD_CALL_JS; - public class WXTimerModule extends WXModule implements Destroyable, Handler.Callback { + @IntDef({MODULE_TIMEOUT, MODULE_INTERVAL}) + @Retention(RetentionPolicy.SOURCE) + @interface MessageType {} + private final static String TAG = "timer"; private Handler handler; + private SparseArray<Integer> antiIntAutoBoxing; public WXTimerModule() { handler = new Handler(WXBridgeManager.getInstance().getJSLooper(), this); + antiIntAutoBoxing = new SparseArray<>(); } - @VisibleForTesting - void setHandler(Handler handler) { - this.handler = handler; - } @JSMethod(uiThread = false) public void setTimeout(@IntRange(from = 1) int funcId, @IntRange(from = 0) int delay) { if(mWXSDKInstance != null) { - postOrHoldMessage(WXJSBridgeMsgType.MODULE_TIMEOUT, funcId, delay, Integer.parseInt(mWXSDKInstance.getInstanceId())); + postOrHoldMessage(MODULE_TIMEOUT, funcId, delay, Integer.parseInt(mWXSDKInstance.getInstanceId())); } } @JSMethod(uiThread = false) public void setInterval(@IntRange(from = 1) int funcId, @IntRange(from = 0) int interval) { if(mWXSDKInstance != null) { - postOrHoldMessage(WXJSBridgeMsgType.MODULE_INTERVAL, funcId, interval, Integer.parseInt(mWXSDKInstance.getInstanceId())); + postOrHoldMessage(MODULE_INTERVAL, funcId, interval, Integer.parseInt(mWXSDKInstance.getInstanceId())); } } @@ -76,7 +81,7 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call if (funcId <= 0) { return; } - removeOrHoldMessage(WXJSBridgeMsgType.MODULE_TIMEOUT, funcId); + removeOrHoldMessage(MODULE_TIMEOUT, funcId); } @JSMethod(uiThread = false) @@ -84,7 +89,7 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call if (funcId <= 0) { return; } - removeOrHoldMessage(WXJSBridgeMsgType.MODULE_INTERVAL, funcId); + removeOrHoldMessage(MODULE_INTERVAL, funcId); } @Override @@ -92,6 +97,7 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call if (handler != null) { WXLogUtils.d(TAG, "Timer Module removeAllMessages: "); handler.removeCallbacksAndMessages(null); + antiIntAutoBoxing.clear(); } } @@ -103,7 +109,7 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call int what = msg.what; WXLogUtils.d(TAG, "Timer Module handleMessage : " + msg.what); switch (what) { - case WXJSBridgeMsgType.MODULE_TIMEOUT: + case MODULE_TIMEOUT: if (msg.obj == null) { break; } @@ -111,11 +117,11 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call WXBridgeManager.getInstance().invokeExecJS(String.valueOf(msg.arg1), null, METHOD_CALL_JS, args, true); ret = true; break; - case WXJSBridgeMsgType.MODULE_INTERVAL: + case MODULE_INTERVAL: if (msg.obj == null) { break; } - postMessage(WXJSBridgeMsgType.MODULE_INTERVAL, (Integer) msg.obj, msg.arg2, msg.arg1); + postMessage(MODULE_INTERVAL, (Integer) msg.obj, msg.arg2, msg.arg1); args = createTimerArgs(msg.arg1, (Integer) msg.obj, true); WXBridgeManager.getInstance().invokeExecJS(String.valueOf(msg.arg1), null, METHOD_CALL_JS, args, true); ret = true; @@ -127,6 +133,11 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call return ret; } + @VisibleForTesting + void setHandler(Handler handler) { + this.handler = handler; + } + private WXJSObject[] createTimerArgs(int instanceId, int funcId, boolean keepAlive) { ArrayList<Object> argsList = new ArrayList<>(); argsList.add(funcId); @@ -142,23 +153,7 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call WXJsonUtils.fromObjectToJSONString(tasks))}; } - private void postMessage(int what, - @IntRange(from = 1) int funcId, - @IntRange(from = 0) int interval, int instanceId) { - if (interval < 0 || funcId <= 0) { - WXLogUtils.e(TAG, "interval < 0 or funcId <=0"); - } else { - Message message = Message.obtain(); - message.what = what; - message.arg1 = instanceId; - message.arg2 = interval; - message.obj = funcId; - handler.sendMessageDelayed(message, interval); - } - } - - - private void postOrHoldMessage(final int what,final int funcId,final int interval,final int instanceId) { + private void postOrHoldMessage(@MessageType final int what,final int funcId,final int interval,final int instanceId) { if(mWXSDKInstance.isPreRenderMode()) { WXSDKManager.getInstance().getWXDomManager().postAction(mWXSDKInstance.getInstanceId(), Actions.getExecutableRenderAction(new Runnable() { @Override @@ -171,17 +166,31 @@ public class WXTimerModule extends WXModule implements Destroyable, Handler.Call } } - private void removeOrHoldMessage(final int what,final int funcId) { + private void removeOrHoldMessage(@MessageType final int what,final int funcId) { if(mWXSDKInstance.isPreRenderMode()) { WXSDKManager.getInstance().getWXDomManager().postAction(mWXSDKInstance.getInstanceId(), Actions.getExecutableRenderAction(new Runnable() { @Override public void run() { - handler.removeMessages(what, funcId); + handler.removeMessages(what, antiIntAutoBoxing.get(funcId, funcId)); } }),false); } else { - handler.removeMessages(what, funcId); + handler.removeMessages(what, antiIntAutoBoxing.get(funcId, funcId)); } } + private void postMessage(@MessageType int what, + @IntRange(from = 1) int funcId, + @IntRange(from = 0) int interval, int instanceId) { + if (interval < 0 || funcId <= 0) { + WXLogUtils.e(TAG, "interval < 0 or funcId <=0"); + } else { + if(antiIntAutoBoxing.get(funcId) == null) { + antiIntAutoBoxing.put(funcId, funcId); + } + Message message = handler + .obtainMessage(what, instanceId, interval, antiIntAutoBoxing.get(funcId)); + handler.sendMessageDelayed(message, interval); + } + } } http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/fefe2e8a/android/sdk/src/test/java/com/taobao/weex/ui/module/WXTimerModuleTest.java ---------------------------------------------------------------------- diff --git a/android/sdk/src/test/java/com/taobao/weex/ui/module/WXTimerModuleTest.java b/android/sdk/src/test/java/com/taobao/weex/ui/module/WXTimerModuleTest.java index 52703b3..7fe508c 100644 --- a/android/sdk/src/test/java/com/taobao/weex/ui/module/WXTimerModuleTest.java +++ b/android/sdk/src/test/java/com/taobao/weex/ui/module/WXTimerModuleTest.java @@ -18,9 +18,15 @@ */ package com.taobao.weex.ui.module; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.when; + import android.os.Handler; import android.os.Message; - import com.taobao.weappplus_sdk.BuildConfig; import com.taobao.weex.InitConfig; import com.taobao.weex.WXSDKEngine; @@ -28,7 +34,7 @@ import com.taobao.weex.WXSDKInstanceTest; import com.taobao.weex.bridge.WXBridgeManager; import com.taobao.weex.bridge.WXBridgeManagerTest; import com.taobao.weex.common.WXThread; - +import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -43,20 +49,14 @@ import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLooper; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.when; - @RunWith(RobolectricTestRunner.class) -@Config(constants = BuildConfig.class, sdk = 19) +@Config(constants = BuildConfig.class) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) @PrepareForTest(WXBridgeManager.class) public class WXTimerModuleTest { public final static int VALID_FUNC_ID = 20; + public final static int NO_CACHING_FUNC_ID = 565654; public final static int INVALID_FUNC_ID = 0; public final static int DELAY = 50; public final static int IMMEDIATELY = 0; @@ -95,6 +95,7 @@ public class WXTimerModuleTest { Mockito.verify(module, times(1)).handleMessage(any(Message.class)); } + @SuppressWarnings("Range") @Test public void testSetTimeoutError1() throws Exception { module.setTimeout(INVALID_FUNC_ID, DELAY); @@ -102,6 +103,7 @@ public class WXTimerModuleTest { Mockito.verify(module, never()).handleMessage(any(Message.class)); } + @SuppressWarnings("Range") @Test public void testSetTimeoutError2() throws Exception { module.setTimeout(VALID_FUNC_ID, INVALID_DELAY); @@ -109,6 +111,7 @@ public class WXTimerModuleTest { Mockito.verify(module, never()).handleMessage(any(Message.class)); } + @SuppressWarnings("Range") @Test public void testSetIntervalError1() throws Exception { module.setInterval(INVALID_FUNC_ID, DELAY); @@ -116,6 +119,7 @@ public class WXTimerModuleTest { Mockito.verify(module, never()).handleMessage(any(Message.class)); } + @SuppressWarnings("Range") @Test public void testSetIntervalError2() throws Exception { module.setInterval(VALID_FUNC_ID, INVALID_DELAY); @@ -163,7 +167,7 @@ public class WXTimerModuleTest { public void testClearTimeout() throws Exception { module.setTimeout(VALID_FUNC_ID, DELAY); module.clearTimeout(VALID_FUNC_ID); - mLooper.idle(DELAY); + mLooper.idle(DELAY, TimeUnit.MILLISECONDS); Mockito.verify(module, never()).handleMessage(any(Message.class)); } @@ -171,7 +175,23 @@ public class WXTimerModuleTest { public void testClearInterval() throws Exception { module.setInterval(VALID_FUNC_ID, DELAY); module.clearInterval(VALID_FUNC_ID); - mLooper.idle(DELAY); + mLooper.idle(DELAY, TimeUnit.MILLISECONDS); + Mockito.verify(module, never()).handleMessage(any(Message.class)); + } + + @Test + public void setClearTimeout2(){ + module.setTimeout(NO_CACHING_FUNC_ID, DELAY); + module.clearTimeout(NO_CACHING_FUNC_ID); + mLooper.idle(DELAY, TimeUnit.MILLISECONDS); + Mockito.verify(module, never()).handleMessage(any(Message.class)); + } + + @Test + public void setClearInterval2(){ + module.setInterval(NO_CACHING_FUNC_ID, DELAY); + module.clearInterval(NO_CACHING_FUNC_ID); + mLooper.idle(DELAY, TimeUnit.MILLISECONDS); Mockito.verify(module, never()).handleMessage(any(Message.class)); } }