Maybe fix issue #3093599: java.lang.IndexOutOfBoundsException...

...Invalid index 0, size is 0 at
android.app.ActivityThread.performPauseActivity(ActivityThread.java:2326)

It looks like if an arrow key is dispatched between the time the
list view is told its data set has changed and it does the resulting
layout pass, we could try to move the position to a now invalid
index.  This may prevent that from happening.

Also put in a better error message if saving state of a fragment
whose target is no longer in the fragment manager.

And fix a bug in PackageManager where we could return a null from
queryIntentActivities().

And add a new API to find out whether a fragment is being removed,
to help fix issue #3306021: NPE at
android.app.AlertDialog.getDefaultDialogTheme(AlertDialog.java)

Next, for new HC apps we can delay committing data to
storage until the activity is stopped.

Finally, use the new multi-threaded AyncTask executor in a few
places, so we don't have worked blocked by long-running tasks from
the application.

Change-Id: I27b2aafedf2e1bf3a2316309889613fa539760f3
This commit is contained in:
Dianne Hackborn
2011-01-24 13:15:09 -08:00
parent 169fafe679
commit 5d9d03a023
9 changed files with 149 additions and 6 deletions

View File

@@ -29889,6 +29889,17 @@
visibility="public"
>
</method>
<method name="isRemoving"
return="boolean"
abstract="false"
native="false"
synchronized="false"
static="false"
final="true"
deprecated="not deprecated"
visibility="public"
>
</method>
<method name="isResumed"
return="boolean"
abstract="false"

View File

@@ -2385,7 +2385,9 @@ public final class ActivityThread {
performPauseActivity(token, finished, r.isPreHoneycomb());
// Make sure any pending writes are now committed.
QueuedWork.waitToFinish();
if (r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Tell the activity manager we have paused.
try {
@@ -2583,6 +2585,11 @@ public final class ActivityThread {
updateVisibility(r, show);
// Make sure any pending writes are now committed.
if (!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Tell activity manager we have been stopped.
try {
ActivityManagerNative.getDefault().activityStopped(
@@ -2647,6 +2654,12 @@ public final class ActivityThread {
}
r.stopped = true;
}
// Make sure any pending writes are now committed.
if (!r.isPreHoneycomb()) {
QueuedWork.waitToFinish();
}
// Tell activity manager we slept.
try {
ActivityManagerNative.getDefault().activitySlept(r.token);

View File

@@ -357,6 +357,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
// True if the fragment is in the list of added fragments.
boolean mAdded;
// If set this fragment is being removed from its activity.
boolean mRemoving;
// True if the fragment is in the resumed state.
boolean mResumed;
@@ -638,6 +641,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* Return <code>getActivity().getResources()</code>.
*/
final public Resources getResources() {
if (mActivity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
return mActivity.getResources();
}
@@ -689,7 +695,16 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* Return true if the fragment is currently added to its activity.
*/
final public boolean isAdded() {
return mActivity != null && mActivity.mFragments.mAdded.contains(this);
return mActivity != null && mAdded;
}
/**
* Return true if this fragment is currently being removed from its
* activity. This is <em>not</em> whether its activity is finishing, but
* rather whether it is in the process of being removed from its activity.
*/
final public boolean isRemoving() {
return mRemoving;
}
/**
@@ -787,6 +802,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
if (mLoaderManager != null) {
return mLoaderManager;
}
if (mActivity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mCheckedForLoaderManager = true;
mLoaderManager = mActivity.getLoaderManager(mIndex, mLoadersStarted, true);
return mLoaderManager;
@@ -797,6 +815,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* containing Activity.
*/
public void startActivity(Intent intent) {
if (mActivity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mActivity.startActivityFromFragment(this, intent, -1);
}
@@ -805,6 +826,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
* containing Activity.
*/
public void startActivityForResult(Intent intent, int requestCode) {
if (mActivity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mActivity.startActivityFromFragment(this, intent, requestCode);
}
@@ -1217,6 +1241,7 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener
writer.print(" mWho="); writer.print(mWho);
writer.print(" mBackStackNesting="); writer.println(mBackStackNesting);
writer.print(prefix); writer.print("mAdded="); writer.print(mAdded);
writer.print(" mRemoving="); writer.print(mRemoving);
writer.print(" mResumed="); writer.print(mResumed);
writer.print(" mFromLayout="); writer.print(mFromLayout);
writer.print(" mInLayout="); writer.println(mInLayout);

View File

@@ -28,6 +28,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.util.DebugUtils;
import android.util.Log;
import android.util.LogWriter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuInflater;
@@ -968,6 +970,7 @@ final class FragmentManagerImpl extends FragmentManager {
makeActive(fragment);
if (DEBUG) Log.v(TAG, "add: " + fragment);
fragment.mAdded = true;
fragment.mRemoving = false;
if (fragment.mHasMenu) {
mNeedMenuInvalidate = true;
}
@@ -984,6 +987,7 @@ final class FragmentManagerImpl extends FragmentManager {
mNeedMenuInvalidate = true;
}
fragment.mAdded = false;
fragment.mRemoving = true;
moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
transition, transitionStyle);
if (inactive) {
@@ -1376,6 +1380,14 @@ final class FragmentManagerImpl extends FragmentManager {
}
if (f.mTarget != null) {
if (f.mTarget.mIndex < 0) {
String msg = "Failure saving state: " + f
+ " has target not in fragment manager: " + f.mTarget;
Slog.e(TAG, msg);
dump(" ", null, new PrintWriter(new LogWriter(
Log.ERROR, TAG, Log.LOG_ID_SYSTEM)), new String[] { });
throw new IllegalStateException(msg);
}
if (fs.mSavedFragmentState == null) {
fs.mSavedFragmentState = new Bundle();
}

View File

@@ -195,7 +195,7 @@ public abstract class AsyncTaskLoader<D> extends Loader<D> {
}
}
if (DEBUG) Slog.v(TAG, "Executing: " + mTask);
mTask.execute((Void[]) null);
mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
}
}

View File

@@ -899,7 +899,7 @@ public abstract class ContentProvider implements ComponentCallbacks {
return null;
}
};
task.execute((Object[])null);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Object[])null);
return fds[0];
} catch (IOException e) {

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2011 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 android.util;
import java.io.Writer;
/** @hide */
public class LogWriter extends Writer {
private final int mPriority;
private final String mTag;
private final int mBuffer;
private StringBuilder mBuilder = new StringBuilder(128);
/**
* Create a new Writer that sends to the log with the given priority
* and tag.
*
* @param priority The desired log priority:
* {@link android.util.Log#VERBOSE Log.VERBOSE},
* {@link android.util.Log#DEBUG Log.DEBUG},
* {@link android.util.Log#INFO Log.INFO},
* {@link android.util.Log#WARN Log.WARN}, or
* {@link android.util.Log#ERROR Log.ERROR}.
* @param tag A string tag to associate with each printed log statement.
*/
public LogWriter(int priority, String tag) {
mPriority = priority;
mTag = tag;
mBuffer = Log.LOG_ID_MAIN;
}
/**
* @hide
* Same as above, but buffer is one of the LOG_ID_ constants from android.util.Log.
*/
public LogWriter(int priority, String tag, int buffer) {
mPriority = priority;
mTag = tag;
mBuffer = buffer;
}
@Override public void close() {
flushBuilder();
}
@Override public void flush() {
flushBuilder();
}
@Override public void write(char[] buf, int offset, int count) {
for(int i = 0; i < count; i++) {
char c = buf[offset + i];
if ( c == '\n') {
flushBuilder();
}
else {
mBuilder.append(c);
}
}
}
private void flushBuilder() {
if (mBuilder.length() > 0) {
Log.println_native(mBuffer, mPriority, mTag, mBuilder.toString());
mBuilder.delete(0, mBuilder.length());
}
}
}

View File

@@ -2706,7 +2706,7 @@ public class ListView extends AbsListView {
int startPos = (mSelectedPosition != INVALID_POSITION) ?
mSelectedPosition - 1 :
firstPosition + getChildCount() - 1;
if (startPos < 0) {
if (startPos < 0 || startPos >= mAdapter.getCount()) {
return INVALID_POSITION;
}
if (startPos > last) {

View File

@@ -2087,7 +2087,7 @@ class PackageManagerService extends IPackageManager.Stub {
return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,
resolvedType, flags, pkg.activities);
}
return null;
return new ArrayList<ResolveInfo>();
}
}