summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2011-12-13 15:08:40 -0800
committerDianne Hackborn <hackbod@google.com>2011-12-13 15:15:33 -0800
commit33b8ee509f36a0168c8ce5a9091b57ab936f4c13 (patch)
tree380b0161e0fd5fb4c9980dc236dcf3accc3dca49
parent19a06fe93cccb4b1dd224b8456969821a19b07ef (diff)
downloadbase-33b8ee509f36a0168c8ce5a9091b57ab936f4c13.tar.gz
Fix issue #5756204: Crespo IME briefly appears shortened when...
...rotating to landscape When doing spell checking in the same process as the spell checker, we need to make sure it is still done asynchronously. Putting this in I noticed quite a few threading issues in this code, so I also addressed those (which became very obviously a problem with the async stuff here now). Also tweaked the service side to run spell checking at background priority. Change-Id: I01bafe3bec6bceeca911d6bf2f61a486a2fd4c48
-rw-r--r--core/java/android/service/textservice/SpellCheckerService.java23
-rw-r--r--core/java/android/view/textservice/SpellCheckerSession.java187
2 files changed, 124 insertions, 86 deletions
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 28251a6245c9..83ea8748b3f7 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -24,6 +24,7 @@ import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.view.textservice.SuggestionsInfo;
@@ -187,23 +188,39 @@ public abstract class SpellCheckerService extends Service {
@Override
public void onGetSuggestionsMultiple(
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
+ int pri = Process.getThreadPriority(Process.myTid());
try {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mListener.onGetSuggestions(
mSession.onGetSuggestionsMultiple(
textInfos, suggestionsLimit, sequentialWords));
} catch (RemoteException e) {
+ } finally {
+ Process.setThreadPriority(pri);
}
}
@Override
public void onCancel() {
- mSession.onCancel();
+ int pri = Process.getThreadPriority(Process.myTid());
+ try {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ mSession.onCancel();
+ } finally {
+ Process.setThreadPriority(pri);
+ }
}
@Override
public void onClose() {
- mSession.onClose();
- mListener = null;
+ int pri = Process.getThreadPriority(Process.myTid());
+ try {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ mSession.onClose();
+ } finally {
+ Process.setThreadPriority(pri);
+ mListener = null;
+ }
}
public String getLocale() {
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 489587ed36d1..ca6577f1510f 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -21,8 +21,11 @@ import com.android.internal.textservice.ISpellCheckerSessionListener;
import com.android.internal.textservice.ITextServicesManager;
import com.android.internal.textservice.ITextServicesSessionListener;
+import android.os.Binder;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
import android.view.textservice.SpellCheckerInfo;
@@ -205,6 +208,8 @@ public class SpellCheckerSession {
private boolean mOpened;
private ISpellCheckerSession mISpellCheckerSession;
+ private HandlerThread mThread;
+ private Handler mAsyncHandler;
public SpellCheckerSessionListenerImpl(Handler handler) {
mOpened = false;
@@ -216,6 +221,7 @@ public class SpellCheckerSession {
public final TextInfo[] mTextInfos;
public final int mSuggestionsLimit;
public final boolean mSequentialWords;
+ public ISpellCheckerSession mSession;
public SpellCheckerParams(int what, TextInfo[] textInfos, int suggestionsLimit,
boolean sequentialWords) {
mWhat = what;
@@ -225,27 +231,86 @@ public class SpellCheckerSession {
}
}
- private void processTask(SpellCheckerParams scp) {
- switch (scp.mWhat) {
- case TASK_CANCEL:
- processCancel();
- break;
- case TASK_GET_SUGGESTIONS_MULTIPLE:
- processGetSuggestionsMultiple(scp);
- break;
- case TASK_CLOSE:
- processClose();
- break;
+ private void processTask(ISpellCheckerSession session, SpellCheckerParams scp,
+ boolean async) {
+ if (async || mAsyncHandler == null) {
+ switch (scp.mWhat) {
+ case TASK_CANCEL:
+ if (DBG) {
+ Log.w(TAG, "Cancel spell checker tasks.");
+ }
+ try {
+ session.onCancel();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to cancel " + e);
+ }
+ break;
+ case TASK_GET_SUGGESTIONS_MULTIPLE:
+ if (DBG) {
+ Log.w(TAG, "Get suggestions from the spell checker.");
+ }
+ try {
+ session.onGetSuggestionsMultiple(scp.mTextInfos,
+ scp.mSuggestionsLimit, scp.mSequentialWords);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get suggestions " + e);
+ }
+ break;
+ case TASK_CLOSE:
+ if (DBG) {
+ Log.w(TAG, "Close spell checker tasks.");
+ }
+ try {
+ session.onClose();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to close " + e);
+ }
+ break;
+ }
+ } else {
+ // The interface is to a local object, so need to execute it
+ // asynchronously.
+ scp.mSession = session;
+ mAsyncHandler.sendMessage(Message.obtain(mAsyncHandler, 1, scp));
+ }
+
+ if (scp.mWhat == TASK_CLOSE) {
+ // If we are closing, we want to clean up our state now even
+ // if it is pending as an async operation.
+ synchronized (this) {
+ mISpellCheckerSession = null;
+ mHandler = null;
+ if (mThread != null) {
+ mThread.quit();
+ }
+ mThread = null;
+ mAsyncHandler = null;
+ }
}
}
public synchronized void onServiceConnected(ISpellCheckerSession session) {
- mISpellCheckerSession = session;
- mOpened = true;
+ synchronized (this) {
+ mISpellCheckerSession = session;
+ if (session.asBinder() instanceof Binder && mThread == null) {
+ // If this is a local object, we need to do our own threading
+ // to make sure we handle it asynchronously.
+ mThread = new HandlerThread("SpellCheckerSession",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mThread.start();
+ mAsyncHandler = new Handler(mThread.getLooper()) {
+ @Override public void handleMessage(Message msg) {
+ SpellCheckerParams scp = (SpellCheckerParams)msg.obj;
+ processTask(scp.mSession, scp, true);
+ }
+ };
+ }
+ mOpened = true;
+ }
if (DBG)
Log.d(TAG, "onServiceConnected - Success");
while (!mPendingTasks.isEmpty()) {
- processTask(mPendingTasks.poll());
+ processTask(session, mPendingTasks.poll(), false);
}
}
@@ -277,87 +342,43 @@ public class SpellCheckerSession {
return mOpened && mISpellCheckerSession == null;
}
- public boolean checkOpenConnection() {
- if (mISpellCheckerSession != null) {
- return true;
- }
- Log.e(TAG, "not connected to the spellchecker service.");
- return false;
- }
-
private void processOrEnqueueTask(SpellCheckerParams scp) {
if (DBG) {
Log.d(TAG, "process or enqueue task: " + mISpellCheckerSession);
}
- SpellCheckerParams closeTask = null;
- if (mISpellCheckerSession == null) {
- if (scp.mWhat == TASK_CANCEL) {
- while (!mPendingTasks.isEmpty()) {
- final SpellCheckerParams tmp = mPendingTasks.poll();
- if (tmp.mWhat == TASK_CLOSE) {
- // Only one close task should be processed, while we need to remove all
- // close tasks from the queue
- closeTask = tmp;
+ ISpellCheckerSession session;
+ synchronized (this) {
+ session = mISpellCheckerSession;
+ if (session == null) {
+ SpellCheckerParams closeTask = null;
+ if (scp.mWhat == TASK_CANCEL) {
+ while (!mPendingTasks.isEmpty()) {
+ final SpellCheckerParams tmp = mPendingTasks.poll();
+ if (tmp.mWhat == TASK_CLOSE) {
+ // Only one close task should be processed, while we need to remove all
+ // close tasks from the queue
+ closeTask = tmp;
+ }
}
}
+ mPendingTasks.offer(scp);
+ if (closeTask != null) {
+ mPendingTasks.offer(closeTask);
+ }
+ return;
}
- mPendingTasks.offer(scp);
- if (closeTask != null) {
- mPendingTasks.offer(closeTask);
- }
- } else {
- processTask(scp);
- }
- }
-
- private void processCancel() {
- if (!checkOpenConnection()) {
- return;
- }
- if (DBG) {
- Log.w(TAG, "Cancel spell checker tasks.");
- }
- try {
- mISpellCheckerSession.onCancel();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to cancel " + e);
- }
- }
-
- private void processClose() {
- if (!checkOpenConnection()) {
- return;
- }
- if (DBG) {
- Log.w(TAG, "Close spell checker tasks.");
- }
- try {
- mISpellCheckerSession.onClose();
- mISpellCheckerSession = null;
- mHandler = null;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to close " + e);
- }
- }
-
- private void processGetSuggestionsMultiple(SpellCheckerParams scp) {
- if (!checkOpenConnection()) {
- return;
- }
- if (DBG) {
- Log.w(TAG, "Get suggestions from the spell checker.");
- }
- try {
- mISpellCheckerSession.onGetSuggestionsMultiple(
- scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords);
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to get suggestions " + e);
}
+ processTask(session, scp, false);
}
@Override
public void onGetSuggestions(SuggestionsInfo[] results) {
- mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results));
+ synchronized (this) {
+ if (mHandler != null) {
+ mHandler.sendMessage(Message.obtain(mHandler,
+ MSG_ON_GET_SUGGESTION_MULTIPLE, results));
+ }
+ }
}
}