【发布时间】:2020-07-16 08:04:05
【问题描述】:
如何修复此代码中的弃用警告?或者,还有其他选择吗?
Handler().postDelayed({
context?.let {
//code
}
}, 3000)
【问题讨论】:
标签: java android kotlin android-handler
如何修复此代码中的弃用警告?或者,还有其他选择吗?
Handler().postDelayed({
context?.let {
//code
}
}, 3000)
【问题讨论】:
标签: java android kotlin android-handler
我有 3 个解决方案:
Handler(Looper.getMainLooper()).postDelay({
// code
})
Handler(Looper.myLooper()!!).postDelay({
// code
})
Thread:
Thread({
try{
Thread.sleep(3000)
} catch (e : Exception) {
throw e
}
// code
}).start()
`
【讨论】:
根据文档(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。
【讨论】:
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);
【讨论】:
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);
}
【讨论】:
使用生命周期范围更容易。内部活动或片段。
lifecycleScope.launch {
delay(2000)
// Do your stuff
}
或使用处理程序
Handler(Looper.myLooper()!!)
【讨论】:
在 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)
}
【讨论】:
协程 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()
}
【讨论】:
如果你使用 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);
}
}
【讨论】:
仅不推荐使用无参数构造函数,现在最好通过Looper.getMainLooper() 方法在构造函数中指定Looper。
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
// Your Code
}
}, 3000);
Handler(Looper.getMainLooper()).postDelayed({
// Your Code
}, 3000)
【讨论】:
从 API 级别 30 开始,有 2 个构造函数被弃用。
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。
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
})
【讨论】:
如果您想避免 Kotlin 中的空值检查(? 或 !!),如果您的 Handler 正在处理一些与 UI 相关的事情,则可以使用 Looper.getMainLooper(),如下所示:
Handler(Looper.getMainLooper()).postDelayed({
Toast.makeText(this@MainActivity, "LOOPER", Toast.LENGTH_SHORT).show()
}, 3000)
注意:如果您使用的是片段,请使用requireContext() 而不是this@MainActivity。
【讨论】:
在Handler构造函数中提供一个looper
Handler(Looper.getMainLooper())
【讨论】:
使用 Executor 而不是处理程序获取更多信息Executor。
要实现后期延迟,请使用ScheduledExecutorService:
ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = () -> {
public void run() {
// Do something
}
};
worker.schedule(runnable, 2000, TimeUnit.MILLISECONDS);
【讨论】:
例如,当从头开始创建全屏活动时,Android Studio 4.0.1 会生成 handler() 等代码。我知道我们被鼓励使用 Kotlin,我确实这样做了,但我不时使用示例项目来获得一个想法。 当 AS 实际生成代码时,我们被 AS 责备,这似乎很奇怪。解决错误并修复它们可能是一项有用的学术活动,但也许 AS 可以为我们这些爱好者生成新的干净代码......
【讨论】:
使用这个
Looper.myLooper()?.let {
Handler(it).postDelayed({
//Your Code
},2500)
}
【讨论】:
不推荐使用的函数是 Handler 的构造函数。请改用Handler(Looper.myLooper()) .postDelayed(runnable, delay)
【讨论】:
Looper.myLooper() 返回一个 Looper?(可能为 null 值)。
考虑使用协程
scope.launch {
delay(3000L)
// do stuff
}
【讨论】:
Activity 或Fragment 内部:lifecycleScope.launch { delay(3000L) }