现在 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
【讨论】:
在Activity
或Fragment
内部:lifecycleScope.launch delay(3000L)
以上是关于现在 Handler() 已弃用,我该使用啥?的主要内容,如果未能解决你的问题,请参考以下文章
我该如何调整这个已弃用的 StratifiedKFold 代码
我尝试在教程中的 setFlags 中使用此标志,但已弃用,我该怎么办