现在 Handler() 已弃用,我该使用啥?

Posted

技术标签:

【中文标题】现在 Handler() 已弃用,我该使用啥?【英文标题】:What do I use now that Handler() is deprecated?现在 Handler() 已弃用,我该使用什么? 【发布时间】:2020-07-16 08:04:05 【问题描述】:

如何修复此代码中的弃用警告?或者,还有其他选择吗?

Handler().postDelayed(
    context?.let 
        //code
    
, 3000)

【问题讨论】:

【参考方案1】:

我有 3 个解决方案

    明确指定 Looper:
    Handler(Looper.getMainLooper()).postDelay(
        // code
    )
    
    指定隐式线程本地行为:
    Handler(Looper.myLooper()!!).postDelay(
        // code
    )
    
    使用Thread:
    Thread(
        try
            Thread.sleep(3000)
         catch (e : Exception) 
            throw e
        
         // code
    ).start()
    `
    

【讨论】:

【参考方案2】:

根据文档(https://developer.android.com/reference/android/os/Handler#Handler()):

在 Handler 构造过程中隐式选择 Looper 可能会导致操作丢失(如果 Handler 不期待新任务并退出)、崩溃(如果有时在没有 Looper 活动的线程上创建处理程序)或竞争条件,其中处理程序关联的线程不是作者预期的。相反,使用 Executor 或显式指定 Looper,使用 Looper#getMainLooper、link android.view.View#getHandler 或类似方法。如果为了兼容性需要隐式线程本地行为,请使用 new Handler(Looper.myLooper()) 让读者清楚。

我们应该停止使用没有 Looper 的构造函数,而是指定一个 Looper。

【讨论】:

【参考方案3】:

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);

【讨论】:

【参考方案4】:

Java 答案

我写了一个易于使用的方法。您可以直接在项目中使用此方法。 delayTimeMillis 可以是 2000,表示这段代码将在 2 秒后运行。

private void runJobWithDelay(int delayTimeMillis)
    new Handler(Looper.getMainLooper()).postDelayed(new Runnable() 
        @Override
        public void run() 
            //todo: you can call your method what you want.
        
    , delayTimeMillis);

【讨论】:

【参考方案5】:

使用生命周期范围更容易。内部活动或片段。

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

或使用处理程序

        Handler(Looper.myLooper()!!)

【讨论】:

如何避免!!运算符? 它可以为空,所以你必须写!!确保它不为空【参考方案6】:

在 Kotlin 中使用这种结构是个好主意

companion object Run 
   fun after(delay: Long, process: () -> Unit) 
      Handler(Looper.getMainLooper()).postDelayed(
          process()
      , delay)
   

稍后调用

Run.after(SPLASH_TIME_OUT) 
   val action = SplashFragmentDirections.actionSplashFragmentToLogin()
   v.findNavController().navigate(action)

【讨论】:

【参考方案7】:

协程 Kotlin

private val SPLASH_SCREEN_TIME_OUT_CONST: Long = 3000

override fun onCreate(savedInstanceState: Bundle?) 
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_splash)
    window.setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN
    )
    GlobalScope.launch 
        delay(SPLASH_SCREEN_TIME_OUT_CONST)
        goToIntro()
    



private fun goToIntro()
    startActivity(Intent(this, IntroActivity::class.java))
    finish()

【讨论】:

我认为 GlobalScope 与 Handler 之间没有区别。 GlobalScope 不知道生命周期(应用程序进程除外)。在我看来,根据 GlobalScope,生命周期范围或自定义范围更方便。【参考方案8】:

如果你使用 Handler 和 Runnable 的变量,那么像这样使用它。

private Handler handler;
private Runnable runnable;

handler = new Handler(Looper.getMainLooper());
    handler.postDelayed(runnable = () -> 
        // Do delayed stuff here
         handler.postDelayed(runnable, 1000);
    , delay);

您还需要删除 onDestroy() 中的回调

@Override
public void onDestroy() 
    super.onDestroy();
    if (handler != null) 
        handler.removeCallbacks(runnable);
    

【讨论】:

【参考方案9】:

仅不推荐使用无参数构造函数,现在最好通过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)

【讨论】:

【参考方案10】:

我经常用这个

代码:

Handler(Looper.myLooper() ?: return).postDelayed(
           // Code what do you want
        , 3000)

截图:

【讨论】:

【参考方案11】:

从 API 级别 30 开始,有 2 个构造函数被弃用。

Handler()

Handler(Handler.Callback)

Google 解释了以下原因。

隐式选择 Looper 处理程序构造可能导致操作静默的错误 丢失(如果处理程序不期待新任务并退出),崩溃 (如果有时在没有 Looper 的线程上创建处理程序 活动)或竞争条件,其中与处理程序关联的线程 与作者的预期不同。相反,使用 Executor 或 明确指定 Looper,使用 Looper#getMainLooper, link android.view.View#getHandler,或类似的。如果隐式线程 本地行为是兼容性所必需的,请使用新的 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.
    
);

科特林

// 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);

科特林

// 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.在后台线程中执行代码并在主线程中更新UI。

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.
            
        );
    
);

科特林

// 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。

Handler(Looper)

Handler(Looper, Handler.Callback)

1.在主线程中执行代码

1.1. 带有 Looper 的处理程序

Java

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

科特林

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;
    
);

科特林

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

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

2.1. 带有 Looper 的处理程序

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()); 

科特林

// 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;
    
);

科特林

// 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.在后台线程中执行代码并在主线程中更新UI。

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;
    
);

科特林

// 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
)

【讨论】:

好东西!干杯【参考方案12】:

如果您想避免 Kotlin 中的空值检查(?!!),如果您的 Handler 正在处理一些与 UI 相关的事情,则可以使用 Looper.getMainLooper(),如下所示:

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

注意:如果您使用的是片段,请使用requireContext() 而不是this@MainActivity

【讨论】:

【参考方案13】:

在Handler构造函数中提供一个looper

Handler(Looper.getMainLooper())

【讨论】:

【参考方案14】:

使用 Executor 而不是处理程序获取更多信息Executor。 要实现后期延迟,请使用ScheduledExecutorService

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

【讨论】:

这不是一个糟糕的答案,事实上甚至google recommends this。【参考方案15】:

例如,当从头开始创建全屏活动时,Android Studio 4.0.1 会生成 handler() 等代码。我知道我们被鼓励使用 Kotlin,我确实这样做了,但我不时使用示例项目来获得一个想法。 当 AS 实际生成代码时,我们被 AS 责备,这似乎很奇怪。解决错误并修复它们可能是一项有用的学术活动,但也许 AS 可以为我们这些爱好者生成新的干净代码......

【讨论】:

【参考方案16】:

使用这个

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

【讨论】:

【参考方案17】:

不推荐使用的函数是 Handler 的构造函数。请改用Handler(Looper.myLooper()) .postDelayed(runnable, delay)

【讨论】:

这在 Kotlin 中不起作用,因为 Looper.myLooper() 返回一个 Looper?(可能为 null 值)。 @EllenSpertus 然后添加一个空检查,或者使用 Looper.myLooper()!如果它为空,它将抛出一个 NPE。如果你在一个带有 looper 的线程上,它将返回非空值。如果不是,它将返回 null 并且应该以任何语言抛出异常。【参考方案18】:

考虑使用协程

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

【讨论】:

ActivityFragment 内部:lifecycleScope.launch delay(3000L)

以上是关于现在 Handler() 已弃用,我该使用啥?的主要内容,如果未能解决你的问题,请参考以下文章

我该如何调整这个已弃用的 StratifiedKFold 代码

我尝试在教程中的 setFlags 中使用此标志,但已弃用,我该怎么办

GeotriggerHandlerReceiver 在 PlotProjects 中已弃用。有啥替代方法可以使用?

GUITexture 已弃用,那么我应该使用啥来代替它?

我应该使用啥来代替已弃用的 `onerror` 属性?

在 iOS7 中,我应该使用啥来代替已弃用的 GKLeaderboardViewController?