From acdabb25f4c92e2e39b5cb4620889e2548c5d14c Mon Sep 17 00:00:00 2001 From: Yuichiro Hanada Date: Mon, 9 Sep 2013 23:10:33 +0900 Subject: [PATCH] Add AsyncResultHolder. Change-Id: Icfa685bcda2f5c74f5649f09098d00b4bd321c5a --- .../android/inputmethod/latin/LatinIME.java | 26 ++----- .../latin/utils/AsyncResultHolder.java | 71 ++++++++++++++++++ .../latin/utils/AsyncResultHolderTests.java | 73 +++++++++++++++++++ 3 files changed, 151 insertions(+), 19 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java create mode 100644 tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 3d29c5a0b..921e004ba 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -86,6 +86,7 @@ import com.android.inputmethod.latin.settings.SettingsActivity; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.utils.ApplicationUtils; +import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -107,8 +108,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Locale; import java.util.TreeSet; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; /** * Input method implementation for Qwerty'ish keyboard. @@ -2428,31 +2427,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } - final CountDownLatch latch = new CountDownLatch(1); - final SuggestedWords[] suggestedWordsArray = new SuggestedWords[1]; + final AsyncResultHolder holder = new AsyncResultHolder(); getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { - suggestedWordsArray[0] = suggestedWords; - latch.countDown(); + holder.set(suggestedWords); } } ); - // TODO: Quit blocking the main thread. - try { - // Wait for the result of getSuggestedWords - // We set the time out to avoid ANR. - latch.await(GET_SUGGESTED_WORDS_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // TODO: Cancel all pending "getSuggestedWords" tasks when it failed. We may want to add - // "onGetSuggestionFailed" to "OnGetSuggestedWordsCallback". - Log.e(TAG, "InterruptedException while waiting for getSuggestedWords.", e); - return; - } - if (suggestedWordsArray[0] != null) { - showSuggestionStrip(suggestedWordsArray[0]); + // This line may cause the current thread to wait. + final SuggestedWords suggestedWords = holder.get(null, GET_SUGGESTED_WORDS_TIMEOUT); + if (suggestedWords != null) { + showSuggestionStrip(suggestedWords); } } diff --git a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java new file mode 100644 index 000000000..c2e97a36f --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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.android.inputmethod.latin.utils; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * This class is a holder of a result of asynchronous computation. + * + * @param the type of the result. + */ +public class AsyncResultHolder { + + private final Object mLock = new Object(); + + private E mResult; + private final CountDownLatch mLatch; + + public AsyncResultHolder() { + mLatch = new CountDownLatch(1); + } + + /** + * Sets the result value to this holder. + * + * @param result the value which is set. + */ + public void set(final E result) { + synchronized(mLock) { + if (mLatch.getCount() > 0) { + mResult = result; + mLatch.countDown(); + } + } + } + + /** + * Gets the result value held in this holder. + * Causes the current thread to wait unless the value is set or the specified time is elapsed. + * + * @param defaultValue the default value. + * @param timeOut the time to wait. + * @return if the result is set until the time limit then the result, otherwise defaultValue. + */ + public E get(final E defaultValue, final long timeOut) { + try { + if(mLatch.await(timeOut, TimeUnit.MILLISECONDS)) { + return mResult; + } else { + return defaultValue; + } + } catch (InterruptedException e) { + return defaultValue; + } + } +} diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java new file mode 100644 index 000000000..7fd167977 --- /dev/null +++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * 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.android.inputmethod.latin.utils; + +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.MediumTest; +import android.util.Log; + +@MediumTest +public class AsyncResultHolderTests extends AndroidTestCase { + private static final String TAG = AsyncResultHolderTests.class.getSimpleName(); + + private static final int TIMEOUT_IN_MILLISECONDS = 500; + private static final int MARGIN_IN_MILLISECONDS = 250; + private static final int DEFAULT_VALUE = 2; + private static final int SET_VALUE = 1; + + private void setAfterGivenTime(final AsyncResultHolder holder, final T value, + final long time) { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + Log.d(TAG, "Exception while sleeping", e); + } + holder.set(value); + } + }).start(); + } + + public void testGetWithoutSet() { + final AsyncResultHolder holder = new AsyncResultHolder(); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(DEFAULT_VALUE, resultValue); + } + + public void testGetBeforeSet() { + final AsyncResultHolder holder = new AsyncResultHolder(); + setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS + MARGIN_IN_MILLISECONDS); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(DEFAULT_VALUE, resultValue); + } + + public void testGetAfterSet() { + final AsyncResultHolder holder = new AsyncResultHolder(); + holder.set(SET_VALUE); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(SET_VALUE, resultValue); + } + + public void testGetBeforeTimeout() { + final AsyncResultHolder holder = new AsyncResultHolder(); + setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS - MARGIN_IN_MILLISECONDS); + final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS); + assertEquals(SET_VALUE, resultValue); + } +}