public class

SelfDestructiveThread

extends java.lang.Object

 java.lang.Object

↳androidx.core.provider.SelfDestructiveThread

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.provider.SelfDestructiveThread android.support.v4.provider.SelfDestructiveThread

Overview

Background thread which is destructed after certain period after all pending activities are finished.

Summary

Constructors
publicSelfDestructiveThread(java.lang.String threadName, int priority, int destructAfterMillisec)

Methods
public intgetGeneration()

Returns the thread generation.

public booleanisRunning()

Returns true if the thread is alive.

public voidpostAndReply(java.util.concurrent.Callable<java.lang.Object> callable, SelfDestructiveThread.ReplyCallback<java.lang.Object> reply)

Execute the specific callable object on this thread and call the reply callback on the calling thread once it finishes.

public java.lang.ObjectpostAndWait(java.util.concurrent.Callable<java.lang.Object> callable, int timeoutMillis)

Execute the specified callable object on this thread and returns the returned value to the caller.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Constructors

public SelfDestructiveThread(java.lang.String threadName, int priority, int destructAfterMillisec)

Methods

public boolean isRunning()

Returns true if the thread is alive.

public int getGeneration()

Returns the thread generation.

public void postAndReply(java.util.concurrent.Callable<java.lang.Object> callable, SelfDestructiveThread.ReplyCallback<java.lang.Object> reply)

Execute the specific callable object on this thread and call the reply callback on the calling thread once it finishes.

public java.lang.Object postAndWait(java.util.concurrent.Callable<java.lang.Object> callable, int timeoutMillis)

Execute the specified callable object on this thread and returns the returned value to the caller. If the execution takes longer time than specified timeout duration, this function throws InterruptedException.

Source

/*
 * 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 androidx.core.provider;


import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

import androidx.annotation.GuardedBy;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;

import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Background thread which is destructed after certain period after all pending activities are
 * finished.
 *
 * @deprecated Not being used by any cross library, and should not be used, internal
 * implementation detail.
 */
@Deprecated
@RestrictTo(LIBRARY_GROUP_PREFIX)
public class SelfDestructiveThread {
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private HandlerThread mThread;

    @GuardedBy("mLock")
    private Handler mHandler;

    @GuardedBy("mLock")
    private int mGeneration;  // The thread generation. Only for testing purpose.

    private static final int MSG_INVOKE_RUNNABLE = 1;
    private static final int MSG_DESTRUCTION = 0;

    private Handler.Callback mCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_INVOKE_RUNNABLE:
                    onInvokeRunnable((Runnable) msg.obj);
                    return true;
                case MSG_DESTRUCTION:
                    onDestruction();
                    return true;
            }
            return true;
        }
    };

    // Milliseconds the thread will be destructed after the last activity.
    private final int mDestructAfterMillisec;
    private final int mPriority;  // The priority of the thread.
    private final String mThreadName;  // The name of the thread.

    public SelfDestructiveThread(
            String threadName, int priority, int destructAfterMillisec) {
        mThreadName = threadName;
        mPriority = priority;
        mDestructAfterMillisec = destructAfterMillisec;
        mGeneration = 0;
    }

    /**
     * Returns true if the thread is alive.
     */
    @VisibleForTesting
    public boolean isRunning() {
        synchronized (mLock) {
            return mThread != null;
        }
    }

    /**
     * Returns the thread generation.
     */
    @VisibleForTesting
    public int getGeneration() {
        synchronized (mLock) {
            return mGeneration;
        }
    }

    private void post(Runnable runnable) {
        synchronized (mLock) {
            if (mThread == null) {
                mThread = new HandlerThread(mThreadName, mPriority);
                mThread.start();
                mHandler = new Handler(mThread.getLooper(), mCallback);
                mGeneration++;
            }
            mHandler.removeMessages(MSG_DESTRUCTION);
            mHandler.sendMessage(mHandler.obtainMessage(MSG_INVOKE_RUNNABLE, runnable));
        }
    }

    /**
     * Reply callback for postAndReply
     *
     * @param <T> A type which will be received as the argument.
     */
    public interface ReplyCallback<T> {
        /**
         * Called when the task was finished.
         */
        void onReply(T value);
    }

    /**
     * Execute the specific callable object on this thread and call the reply callback on the
     * calling thread once it finishes.
     */
    @SuppressWarnings("deprecation")
    public <T> void postAndReply(final Callable<T> callable, final ReplyCallback<T> reply) {
        final Handler calleeHandler = CalleeHandler.create();
        post(new Runnable() {
            @Override
            public void run() {
                T t;
                try {
                    t = callable.call();
                } catch (Exception e) {
                    t = null;
                }
                final T result = t;
                calleeHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        reply.onReply(result);
                    }
                });
            }
        });
    }

    /**
     * Execute the specified callable object on this thread and returns the returned value to the
     * caller.
     *
     * If the execution takes longer time than specified timeout duration, this function throws
     * InterruptedException.
     */
    public <T> T postAndWait(final Callable<T> callable, int timeoutMillis)
            throws InterruptedException {
        final ReentrantLock lock = new ReentrantLock();
        final Condition cond = lock.newCondition();

        final AtomicReference<T> holder = new AtomicReference<>();
        final AtomicBoolean running = new AtomicBoolean(true);
        post(new Runnable() {
            @Override
            public void run() {
                try {
                    holder.set(callable.call());
                } catch (Exception e) {
                    // Do nothing.
                }
                lock.lock();
                try {
                    running.set(false);
                    cond.signal();
                } finally {
                    lock.unlock();
                }
            }
        });

        lock.lock();
        try {
            if (!running.get()) {
                return holder.get();  // already finished.
            }
            long remaining = TimeUnit.MILLISECONDS.toNanos(timeoutMillis);
            for (;;) {
                try {
                    remaining = cond.awaitNanos(remaining);
                } catch (InterruptedException e) {
                    // ignore
                }
                if (!running.get()) {
                    return holder.get();  // Successfully finished.
                }
                if (remaining <= 0) {
                    throw new InterruptedException("timeout");  // Timeout
                }
            }
        } finally {
            lock.unlock();
        }
    }

    void onInvokeRunnable(Runnable runnable) {
        runnable.run();
        synchronized (mLock) {
            mHandler.removeMessages(MSG_DESTRUCTION);
            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_DESTRUCTION),
                    mDestructAfterMillisec);
        }
    }

    void onDestruction() {
        synchronized (mLock) {
            if (mHandler.hasMessages(MSG_INVOKE_RUNNABLE)) {
                // This happens if post() is called after onDestruction and before synchronization
                // block.
                return;
            }
            mThread.quit();
            mThread = null;
            mHandler = null;
        }
    }
}