【问题标题】:Android deprecated Tasks.call - replacementAndroid 已弃用 Tasks.call - 替换
【发布时间】:2021-12-24 05:59:34
【问题描述】:

在我的 android 应用程序中,我可以选择将数据库备份到 Google Drive。为此,我正在使用 DriveServiceHelper 类,但我刚刚注意到,在 Android 11 中,Task.call 已被弃用。

      public Task<FileList> queryFiles() {
    return Tasks.call(mExecutor, () ->
            mDriveService.files().list().setSpaces("drive").execute());
}

然后我从我的 BackupActivity 调用 queryFilesbackup 方法:

  public void backup(View v) {
        driveServiceHelper.queryFiles()
                .addOnSuccessListener(fileList -> {
                  // another code
                })
                .addOnFailureListener(e -> showMsgSnack(getString(R.string.uploaderror)));

我没有找到任何解决方案来避免该类的完全返工。

我尝试了什么:

我尝试用 runnable 替换,也可以调用,但它不起作用,因为预期返回的是 Task,而不是 Filelist。

我也尝试使用TaskCompletionSource:

public Task<FileList> queryFiles(int delay) throws IOException, ExecutionException, InterruptedException {

    new Thread(
            new Runnable() {

                @Override
                public void run() {
                    TaskCompletionSource<FileList> taskCompletionSource = new TaskCompletionSource<>();

                    FileList result = null;
                    try {
                        result = mDriveService.files().list().setSpaces("drive").execute();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    FileList finalResult = result;
                    new Handler().postDelayed(() -> taskCompletionSource.setResult(finalResult), delay);

                    return taskCompletionSource.getTask();
                }
            }).start();
    
}

但返回不是来自 void 类型的方法。

【问题讨论】:

  • @Shark 你能帮忙举个例子吗,在我的问题中,我添加了我对 TaskCompletionSource 的尝试,但它不能从主线程调用。
  • 那么你为什么不把它包装在一个 Runnable 中,然后等待它的未来呢?但这也会阻塞你的主线程......你应该开始在后台线程上执行这些方法。
  • 好吧,我也用runnable试过了,但是从runnable我不能返回任务,因为我想Task应该会返回,因为我从另一个类driveServiceHelper.queryFiles() .addOnSuccessListener (fileList -> {...我真的很难完成这几个星期,请您发布一些我可以用于我的案例的示例吗?

标签: android android-asynctask google-drive-api


【解决方案1】:

我的意思是这样的:

 public Task<FileList> queryFiles(int delay) throws IOException {
        Task<FileList> retVal;
        final FutureValue<Task<FileList>> future = new FutureValue<>();

        // now run this bit in a runnable
        /*
        TaskCompletionSource<FileList> taskCompletionSource = new TaskCompletionSource<>();
    
        FileList result = mDriveService.files().list().setSpaces("drive").execute();
        new Handler().postDelayed(() -> taskCompletionSource.setResult(result), delay);
    
        return taskCompletionSource.getTask();
        */

        new Thread(
                new Runnable() {

                    @Override
                    public void run() {
                            TaskCompletionSource<FileList> taskCompletionSource = new TaskCompletionSource<>();
    
                            FileList result = mDriveService.files().list().setSpaces("drive").execute();
                            new Handler().postDelayed(() -> taskCompletionSource.setResult(result), delay);
    
                            // and we replace the return statement with something else
                            // return taskCompletionSource.getTask();
                            future.set(taskCompletionSource.getTask());
                   }
        }).start();

       // And block (wait) for future to finish so we can return it, deadlocking the main thread...

//      return future.get();

      //FIXME do either this
//      retVal = future.get();

      // For bonus points, we'll do a timed wait instead -- OR THIS
        try {
            retVal = future.get(30, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            Log.d(LOG_TAG, "Exception "+e+" happened!", e);
        } catch (InterruptedException | ExecutionException e) {
            Log.d(LOG_TAG, "Exception "+e+" happened!", e);
        }

    return retVal;
}

这应该会让你走上解决问题的道路。

但是,如果使用 Task&lt;&gt; 的唯一原因是您可以向这些方法添加成功/失败侦听器 - 我强烈建议您想出更好的方法,实际运行在后台线程而不是您的线程上'重新召唤他们。

FutureValue 类:

/**
 * Implementation of {@link Future}, allowing waiting for value to be set (from another thread).
 * Use {@link #set(Object)} to set value, {@link #get()} or {@link #get(long, TimeUnit)} to retrieve
 * value.
 * TODO: tests
 *
 * @param <T> type of awaited value
 */
public class FutureValue<T> implements Future<T> {
    private static final String LOGTAG = "FutureValue";
    private static final long NANOS_IN_MILLI = TimeUnit.MILLISECONDS.toNanos(1);

    private volatile T value;
    private volatile boolean isDone = false;
    private volatile boolean isCanceled = false;

    /**
     * Sets value awaited by this future.
     *
     * @param value value
     */
    public synchronized void set(T value) {
        this.value = value;
        isDone = true;
        notifyAll();
    }

    /** {@inheritDoc} */
    @Override
    public synchronized boolean cancel(boolean mayInterruptIfRunning) {
        isCanceled = true;
        notifyAll();
        return !isDone;
    }

    /** {@inheritDoc} */
    @Override
    public boolean isCancelled() {
        return isCanceled;
    }

    /** {@inheritDoc} */
    @Override
    public boolean isDone() {
        return isDone;
    }

    /** {@inheritDoc} */
    @Override
    public synchronized T get() {
        while (!isDone) {
            if (isCanceled) {
                return value;
            }
            try {
                wait();
            } catch (InterruptedException ignored) {
                Log.w(LOGTAG, "We're just gonna ignore this exception: " + ignored, ignored);
            }
        }

        return value;
    }

    /** {@inheritDoc} */
    @Override
    public synchronized T get(long timeout, @NonNull TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException {

        final long targetTime = System.nanoTime() + unit.toNanos(timeout);

        while (!isDone && !isCanceled) {
            try {
                final long waitTimeNanos = targetTime - System.nanoTime();
                if (waitTimeNanos <= 0) {
                    throw new TimeoutException();
                }
                wait(waitTimeNanos / NANOS_IN_MILLI, (int) (waitTimeNanos % NANOS_IN_MILLI));
            } catch (InterruptedException ignored) {
                Log.w(LOGTAG, "We're just gonna ignore this exception: " + ignored, ignored);
            }
        }

        return value;
    }

}

【讨论】:

  • 感谢您的回答。如果我用 Future 来做,那么 new FutureValue(); 就会出现一些问题。不知道,我尝试用 new Future 和 future.set(taskCompletionSource.getTask()); 替换它- 设置未知命令。如果我在没有 Future 的情况下尝试它,只是使用 runnable(请参阅我的问题更新),那么我又回到了开始,我无法返回 taskCompletionSource.getTask();来自可运行的虚空。
  • 你不能只用一个 Runnable 来做到这一点——runnable 必须在某个地方设置它的“返回值”(这就是我们使用未来的原因)并且你需要能够等待足够长的时间等待在某处设置的值。 Future 可以做这两件事,这也是它存在的两个原因之一。
  • 哦,哎呀,我的错,让我在这里添加更多代码......
  • 啊,这是一门课,好的。因此,在执行完这一切之后,当我单击备份按钮时,什么也没有发生。在控制台中经过一段时间后,我发生了超时错误异常 java.util.concurrent.TimeoutException,然后当然是 java.lang.NullPointerException,因为没有结果。在我的旧代码中,单击后立即调用 Tasks.call,备份在几秒钟内就在谷歌驱动器上完成,但现在由于某种原因它超时
  • 那么其他东西坏了。任务现在还执行吗?您是否考虑过使用 Tasks.await() 而不实际使用此 FutureValue?
【解决方案2】:

好的,经过数小时的测试,我尝试了这个解决方案,现在这似乎有效:(使用 executorService,并且在 Handler 中需要一个 Looper。)

public Task<FileList> queryFiles() {
    final TaskCompletionSource<FileList> tcs = new TaskCompletionSource<FileList>();
    ExecutorService service = Executors.newFixedThreadPool(1);

    service.execute(
            new Runnable() {
                @Override
                public void run() {

                    FileList result = null;
                    try {
                        result = mDriveService.files().list().setSpaces("drive").execute();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    FileList finalResult = result;
                    new Handler(Looper.getMainLooper()).postDelayed(() -> tcs.setResult(finalResult), 1000);

                }
            });

    return tcs.getTask();

    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多