Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8439ac08 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Start accepting Executors instead of Handlers.

After discussion in API council, our new best-practices are to have
developers provide an Executor to dispatch callbacks/listeners on,
instead of the previous guidance of using a Handler.

Define Context.getMainExecutor() to easily obtain an Executor
associated with the main thread of application.  This allows new
APIs to require a @NonNull Executor.  Also define a new
@CallbackExecutor auto-doc annotation that explains background and
points developers at new Context method above.

Test: cts-tradefed run commandAndExit cts-dev -m CtsContentTestCases -t android.content.cts.ContextTest#testMainLooper
Test: cts-tradefed run commandAndExit cts-dev -m CtsContentTestCases -t android.content.cts.ContextTest#testMainExecutor
Bug: 70348426
Change-Id: I536892bcd80fcfba1bb1ddf67648c08a26d7ddd2
parent ff38f236
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -6313,7 +6313,7 @@ package android.app.admin {
    method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
    method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
    method public void addUserRestriction(android.content.ComponentName, java.lang.String);
    method public void addUserRestriction(android.content.ComponentName, java.lang.String);
    method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
    method public boolean bindDeviceAdminServiceAsUser(android.content.ComponentName, android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
    method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, android.os.Handler);
    method public boolean clearApplicationUserData(android.content.ComponentName, java.lang.String, android.app.admin.DevicePolicyManager.OnClearApplicationUserDataListener, java.util.concurrent.Executor);
    method public void clearCrossProfileIntentFilters(android.content.ComponentName);
    method public void clearCrossProfileIntentFilters(android.content.ComponentName);
    method public deprecated void clearDeviceOwnerApp(java.lang.String);
    method public deprecated void clearDeviceOwnerApp(java.lang.String);
    method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
    method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
@@ -9050,6 +9050,7 @@ package android.content {
    method public abstract java.io.File[] getExternalMediaDirs();
    method public abstract java.io.File[] getExternalMediaDirs();
    method public abstract java.io.File getFileStreamPath(java.lang.String);
    method public abstract java.io.File getFileStreamPath(java.lang.String);
    method public abstract java.io.File getFilesDir();
    method public abstract java.io.File getFilesDir();
    method public java.util.concurrent.Executor getMainExecutor();
    method public abstract android.os.Looper getMainLooper();
    method public abstract android.os.Looper getMainLooper();
    method public abstract java.io.File getNoBackupFilesDir();
    method public abstract java.io.File getNoBackupFilesDir();
    method public abstract java.io.File getObbDir();
    method public abstract java.io.File getObbDir();
+41 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.annotation;

import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;

import android.content.Context;
import android.os.AsyncTask;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.concurrent.Executor;

/**
 * @paramDoc Callback and listener events are dispatched through this
 *           {@link Executor}, providing an easy way to control which thread is
 *           used. To dispatch events through the main thread of your
 *           application, you can use {@link Context#getMainExecutor()}. To
 *           dispatch events through a shared thread pool, you can use
 *           {@link AsyncTask#THREAD_POOL_EXECUTOR}.
 * @hide
 */
@Retention(SOURCE)
@Target(PARAMETER)
public @interface CallbackExecutor {
}
+44 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2017 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.annotation;

import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

/**
 * A program element annotated @Condemned is one that programmers are
 * blocked from using, typically because it's about to be completely destroyed.
 * <p>
 * This is a stronger version of &#64;Deprecated, and it's typically used to
 * mark APIs that only existed temporarily in a preview SDK, and which only
 * continue to exist temporarily to support binary compatibility.
 *
 * @hide
 */
@Retention(SOURCE)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Condemned {
}
+7 −0
Original line number Original line Diff line number Diff line
@@ -80,6 +80,7 @@ import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Environment;
import android.os.GraphicsEnvironment;
import android.os.GraphicsEnvironment;
import android.os.Handler;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.LocaleList;
import android.os.Looper;
import android.os.Looper;
@@ -172,6 +173,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.Objects;
import java.util.TimeZone;
import java.util.TimeZone;
import java.util.concurrent.Executor;


final class RemoteServiceException extends AndroidRuntimeException {
final class RemoteServiceException extends AndroidRuntimeException {
    public RemoteServiceException(String msg) {
    public RemoteServiceException(String msg) {
@@ -242,6 +244,7 @@ public final class ActivityThread extends ClientTransactionHandler {
    final ApplicationThread mAppThread = new ApplicationThread();
    final ApplicationThread mAppThread = new ApplicationThread();
    final Looper mLooper = Looper.myLooper();
    final Looper mLooper = Looper.myLooper();
    final H mH = new H();
    final H mH = new H();
    final Executor mExecutor = new HandlerExecutor(mH);
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    // List of new activities (via ActivityRecord.nextIdle) that should
    // List of new activities (via ActivityRecord.nextIdle) that should
    // be reported when next we idle.
    // be reported when next we idle.
@@ -1943,6 +1946,10 @@ public final class ActivityThread extends ClientTransactionHandler {
        return mLooper;
        return mLooper;
    }
    }


    public Executor getExecutor() {
        return mExecutor;
    }

    public Application getApplication() {
    public Application getApplication() {
        return mInitialApplication;
        return mInitialApplication;
    }
    }
+6 −0
Original line number Original line Diff line number Diff line
@@ -90,6 +90,7 @@ import java.io.InputStream;
import java.nio.ByteOrder;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Objects;
import java.util.concurrent.Executor;


class ReceiverRestrictedContext extends ContextWrapper {
class ReceiverRestrictedContext extends ContextWrapper {
    ReceiverRestrictedContext(Context base) {
    ReceiverRestrictedContext(Context base) {
@@ -249,6 +250,11 @@ class ContextImpl extends Context {
        return mMainThread.getLooper();
        return mMainThread.getLooper();
    }
    }


    @Override
    public Executor getMainExecutor() {
        return mMainThread.getExecutor();
    }

    @Override
    @Override
    public Context getApplicationContext() {
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
        return (mPackageInfo != null) ?
Loading