现在Handler()已经被弃用,我应该使用什么?

361

我该如何修复这段代码中的弃用警告?或者,是否有其他选项可以实现相同的功能?

我该如何修复这段代码中的弃用警告?或者,是否有其他选项可以实现相同的功能?

Handler().postDelayed({
    context?.let {
        //code
    }
}, 3000)
20个回答

689
只有无参数的构造函数已经被弃用,现在更推荐在构造函数中通过Looper.getMainLooper()方法指定Looper

在Java中使用它

new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
    @Override
    public void run() {
        // Your Code
    }
}, 3000);

在Kotlin中使用它

Handler(Looper.getMainLooper()).postDelayed({
    // Your Code
}, 3000)

来源:developer.android.com

3
直截了当... - Fahmi Eshaq
3
getMainLooper()是什么? - UNREAL
2
@UNREAL 返回应用程序的主线程中运行的主循环器。https://developer.android.com/reference/android/os/Looper#getMainLooper() - Nikunj Paradva

94
д»ҺAPIзә§еҲ«30ејҖе§ӢпјҢжңү2дёӘе·Іејғз”Ёзҡ„жһ„йҖ еҮҪж•°гҖӮ

谷歌在下面解释了原因。

在Handler构造期间隐式选择一个Looper可能会导致一些bug,其中操作会被静默丢失(如果Handler不期望新任务并退出),崩溃(如果有时在没有活动的Looper的线程上创建Handler),或者竞争条件,即与Handler关联的线程不是作者预期的那个。相反,使用Executor或显式指定Looper,使用Looper#getMainLooper,{link android.view.View#getHandler}或类似方法。如果需要兼容性的隐式线程本地行为,请使用new Handler(Looper.myLooper(), callback)来明确告诉读者。

解决方案1:使用Executor

1. 在主线程中执行代码。

Java

// Create an executor that executes tasks in the main thread. 
Executor mainExecutor = ContextCompat.getMainExecutor(this);

// Execute a task in the main thread
mainExecutor.execute(new Runnable() {
    @Override
    public void run() {
        // You code logic goes here.
    }
});

Kotlin
// Create an executor that executes tasks in the main thread.
val mainExecutor = ContextCompat.getMainExecutor(this)

// Execute a task in the main thread
mainExecutor.execute {
    // You code logic goes here.
}

2. 在后台线程中执行代码
Java
// Create an executor that executes tasks in a background thread.
ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();

// Execute a task in the background thread.
backgroundExecutor.execute(new Runnable() {
    @Override
    public void run() {
        // Your code logic goes here.
    }
});

// Execute a task in the background thread after 3 seconds.
backgroundExecutor.schedule(new Runnable() {
    @Override
    public void run() {
        // Your code logic goes here
    }
}, 3, TimeUnit.SECONDS);

Kotlin

// Create an executor that executes tasks in a background thread.
val backgroundExecutor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor()

// Execute a task in the background thread.
backgroundExecutor.execute {
    // Your code logic goes here.
}

// Execute a task in the background thread after 3 seconds.
backgroundExecutor.schedule({
    // Your code logic goes here
}, 3, TimeUnit.SECONDS)

注意:使用后记得关闭执行器。
backgroundExecutor.shutdown(); // or backgroundExecutor.shutdownNow();

3. 在后台线程中执行代码,并在主线程上更新用户界面。

Java

// Create an executor that executes tasks in the main thread. 
Executor mainExecutor = ContextCompat.getMainExecutor(this);

// Create an executor that executes tasks in a background thread.
ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();

// Execute a task in the background thread.
backgroundExecutor.execute(new Runnable() {
    @Override
    public void run() {
        // Your code logic goes here.
        
        // Update UI on the main thread
        mainExecutor.execute(new Runnable() {
            @Override
            public void run() {
                // You code logic goes here.
            }
        });
    }
});

Kotlin
// Create an executor that executes tasks in the main thread. 
val mainExecutor: Executor = ContextCompat.getMainExecutor(this)

// Create an executor that executes tasks in a background thread.
val backgroundExecutor = Executors.newSingleThreadScheduledExecutor()

// Execute a task in the background thread.
backgroundExecutor.execute {
    // Your code logic goes here.

    // Update UI on the main thread
    mainExecutor.execute {
        // You code logic goes here.
    }
}
解决方案2:通过使用以下构造函数之一来明确指定一个 Looper。

1. 在主线程中执行代码

1.1. 使用 Looper 的 Handler

Java

Handler mainHandler = new Handler(Looper.getMainLooper());

Kotlin
val mainHandler = Handler(Looper.getMainLooper())

1.2 使用 Looper 和 Handler.Callback 的处理程序
Java
Handler mainHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        // Your code logic goes here.
        return true;
    }
});

Kotlin
val mainHandler = Handler(Looper.getMainLooper(), Handler.Callback {
    // Your code logic goes here.
    true
})

2. 在后台线程中执行代码

2.1. 带有 Looper 的 Handler

Java

// Create a background thread that has a Looper
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

// Create a handler to execute tasks in the background thread.
Handler backgroundHandler = new Handler(handlerThread.getLooper()); 

Kotlin
// Create a background thread that has a Looper
val handlerThread = HandlerThread("HandlerThread")
handlerThread.start()


// Create a handler to execute tasks in the background thread.
val backgroundHandler = Handler(handlerThread.looper)

2.2. 使用 Looper 和 Handler.Callback 的处理程序

Java

// Create a background thread that has a Looper
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

// Create a handler to execute taks in the background thread.
Handler backgroundHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        // Your code logic goes here.
        return true;
    }
});

Kotlin
// Create a background thread that has a Looper
val handlerThread = HandlerThread("HandlerThread")
handlerThread.start()


// Create a handler to execute taks in the background thread.
val backgroundHandler = Handler(handlerThread.looper, Handler.Callback {
    // Your code logic goes here.
    true
})

注意:使用后记得释放线程。
handlerThread.quit(); // or handlerThread.quitSafely();

3. 在后台线程中执行代码,并在主线程上更新用户界面。
Java
// Create a handler to execute code in the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

// Create a background thread that has a Looper
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

// Create a handler to execute in the background thread
Handler backgroundHandler = new Handler(handlerThread.getLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message message) {
        // Your code logic goes here.
        
        // Update UI on the main thread.
        mainHandler.post(new Runnable() {
            @Override
            public void run() {
                
            }
        });
        
        return true;
    }
});

Kotlin
// Create a handler to execute code in the main thread
val mainHandler = Handler(Looper.getMainLooper())

// Create a background thread that has a Looper
val handlerThread = HandlerThread("HandlerThread")
handlerThread.start()

// Create a handler to execute in the background thread
val backgroundHandler = Handler(handlerThread.looper, Handler.Callback {
    // Your code logic goes here.

    // Update UI on the main thread.
    mainHandler.post {
        
    }
    true
})

9
太棒了!干杯。 - madhu527
天才!!最佳答案 - KK_07k11A0585

79

如果你想在 Kotlin 中避免空检查 (?!!),当你的 Handler 处理一些 UI 相关的事情时,你可以使用 Looper.getMainLooper(),像这样:

Handler(Looper.getMainLooper()).postDelayed({
   Toast.makeText(this@MainActivity, "LOOPER", Toast.LENGTH_SHORT).show()
}, 3000)

注意:如果你正在使用fragment,请使用requireContext(),而不是this@MainActivity


29

废弃的函数是 Handler 的构造函数。请使用 Handler(Looper.myLooper()).postDelayed(runnable, delay) 代替。


这在 Kotlin 中不起作用,因为 Looper.myLooper() 返回一个 Looper?(可能为空值)。 - Ellen Spertus
4
@EllenSpertus 然后加入一个null检查,或者使用Looper.myLooper()!! ,如果它为null,则会抛出NPE。如果您在具有looper的线程上,则会返回非null值。如果没有,则将返回null,并且应在任何语言中引发异常。 - Gabe Sechan

24

考虑使用协程

scope.launch {
    delay(3000L)
    // do stuff
}

15
ActivityFragment中:lifecycleScope.launch { delay(3000L) }说明:这段代码使用了Kotlin协程,延迟3秒钟后执行。lifecycleScope确保在此ActivityFragment的生命周期结束时取消该协程,以避免内存泄漏和其他潜在的问题。 - Mahmudul Hasan Shohag

22

使用生命周期作用域会更加容易,在活动或片段内。

 lifecycleScope.launch {
     delay(2000)
     // Do your stuff
 }

或使用处理程序

        Handler(Looper.myLooper()!!)

如何避免使用 !! 运算符? - Jéwôm'
它可能为空,因此您必须编写 !! 以确保它不为空。 - Shaon
1
执行 !! 可能会导致异常,因为循环器可能为空,而 Handler 需要一个非空值。 - Bawa

18

我有3种解决方案:

  1. 明确指定 Looper:
  2. Handler(Looper.getMainLooper()).postDelayed({
        // code
    }, duration)
    
    指定隐式线程本地行为:
    Handler(Looper.myLooper()!!).postDelayed({
        // code
    }, duration)
    
    使用 Thread:
    Thread({
        try{
            Thread.sleep(3000)
        } catch (e : Exception) {
            throw e
        }
         // code
    }).start()
    

13

Handler()Handler(Handler.Callback callback)构造函数已被弃用,因为它们可能导致错误和崩溃。请明确地使用Executor或Looper。

对于Java

Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
  @Override
  public void run() {
    //do your work here
   }
}, 1000);

10

使用这个

Looper.myLooper()?.let {
    Handler(it).postDelayed({
        //Your Code
    },2500)
}

9

使用 Executor 而不是 handler 来获取更多信息。
要实现延迟发布,请使用 ScheduledExecutorService

ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = () -> {
    public void run() {
        // Do something
    }
};
worker.schedule(runnable, 2000, TimeUnit.MILLISECONDS);

这并不是一个糟糕的答案,事实上甚至谷歌也推荐这样做 - iFarbod

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接