Android - 等待可运行完成

问题描述 投票:0回答:3

我逐个字母地将文本写入文本视图。只有在显示整个文本后我才想开始运行进度条。我想我可以通过通知/等待来实现它,但我不知道如何实现。有什么帮助吗?

我创建了

Typewriter
类,它扩展了
Textview
:

private Handler mHandler = new Handler();

public Runnable characterAdder = new Runnable() {
  @Override
  public void run() {
    setText(mText.subSequence(0, mIndex++));
    if (mIndex <= mText.length()) {
      mHandler.postDelayed(characterAdder, mDelay);
    }
  }
};

public void animateText(CharSequence text) {
  mText = text;
  mIndex = 0;
  setText("");
  mHandler.removeCallbacks(characterAdder);
  mHandler.postDelayed(characterAdder, mDelay);
}

然后我在主要活动中使用它:

Typewriter writer = (Typewriter)findViewById(R.id.typewriter);
writer.animateText("Lorem ipsum dolor sit amet");
//wait until animation finishes to perform further actions
java android runnable android-handler
3个回答
2
投票

由于我的两个操作都在 UI 线程上运行,所以我没有找到任何同步解决方案。相反,我推迟了第二个操作,直到打印所有字母所需的时间:

Handler handler = new Handler();
handler.postDelayed(new Runnable() {
  public void run() {
    animation.start();
  }
  }, timeNeeded);

0
投票

我确实喜欢在 Kotlin 中使用协程。

val handler = Handler()
var isWaitingHandler = true
GlobalScope.async {
    handler.post {
        // handler does something.
        isWaitingHandler = false    
    }

    while(isWaitingHandler) {
        delay(50)
    }
    Unit
}.await()

这不完全是我写的,但我认为可行。


0
投票

我在这里也遇到了一些问题,我需要执行 UI 更新,而后台线程正在等待 ui 部分完成。

因此,我编写了一个名为 UIUtils 的类,它通过两个静态方法提供了对处理程序调用的轻松访问:

invoke()
invokeAndAwait()

invokeAndAwait()
基本上只是将创建的
Handler
注册到
awaitingCompletion
列表中,然后立即进入等待循环,直到再次从列表中删除
Handler

如果您意外地从 UI 线程调用

invokeAndAwait()
,则传递的可运行对象将作为对
run()
的阻塞调用来执行。

以非阻塞方式调用某些东西:

//Some code here
UiUtils.invoke(() -> view.setText("abc"));
//More code running right away here.

以阻塞/等待的方式调用某些东西:

//Some code here
UiUtils.invokeAndAwait(() -> view.setText("abc"));
//Code here is running, when invocation is completed.

到目前为止,这对于我的需求来说非常完美。这并不理想,因为它只每 20 毫秒检查一次处理程序是否完成,但对于非常非常非常罕见的用例,我们需要等待执行 UI-Action,我可以接受。

public class UiUtils {
    private static Object runningBlockingHandlerListLock = new Object();
    private static Collection<Handler> runningBlockingHandlers = new ArrayList<>();
    
    public static boolean isUIThread(){
        return Thread.currentThread() == Looper.getMainLooper().getThread();
    }
    
    public static void invoke(Runnable r){
        new Handler(Looper.getMainLooper()).post(r);
    }
    
    public static void invokeAndAwait(Runnable r){

        if (isUIThread()){
            //On the UI Thread, we cannot juse InvokeAndWait, but we just can run it right away.
            Log.w(UiUtils.class.getSimpleName(), "invokeAndAwait() called on UI-Thread. Using blocking execution of runnable instead.");
            r.run();
            return;
        }

        Handler h = new Handler(Looper.getMainLooper());
        synchronized (runningBlockingHandlerListLock) {
            runningBlockingHandlers.add(h);
        }

        h.post(() -> {
            try {
                r.run();
            }catch (Exception e){
                Log.e(UiUtils.class.getSimpleName(), "Exception during Mainthread invokation", e);
            }finally {
                Log.d(UiUtils.class.getSimpleName(), "Handler completed: " + h.hashCode());
                synchronized (runningBlockingHandlerListLock) {
                    runningBlockingHandlers.remove(h);
                }
            }
        });

        boolean isActive = true;
        while (isActive){
            try {
                Log.v(UiUtils.class.getSimpleName(), "Awaiting completion of Handler: " + h.hashCode());
                Thread.sleep(20);

                synchronized (runningBlockingHandlerListLock) {
                    isActive = runningBlockingHandlers.contains(h);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.