赞
踩
android.view.ViewRootImpl$CalledFromWrongThreadException
异常import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.example.myapplication1.R; import java.util.concurrent.TimeUnit; public class EventHandlerActivity extends AppCompatActivity { private static final int CALCULATE_KEY = 2024; private Button calculateBtn; private TextView resultTv; // 创建Handler实例,用于在主线程中更新UI private Handler myHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(@NonNull Message msg) { if (msg.what == CALCULATE_KEY) { resultTv.setText("Result: " + msg.obj); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_event_handler); calculateBtn = findViewById(R.id.calculateBtn); resultTv = findViewById(R.id.resultTv); calculateBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 点击按钮后,开始执行复杂计算 calculateFunc(); } }); } private void calculateFunc() { // 创建一个新线程来执行耗时操作 new Thread(new Runnable() { @Override public void run() { long result = factorial(5); // 模拟复杂的耗时计算 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // 发送消息给Handler,以便在主线程中更新UI // 另外,为了避免频繁地创建和销毁 Message 对象,可以使用 Message.obtain() 方法从消息池中获取一个消息实例,以减少内存分配和垃圾回收的频率 Message message = Message.obtain(); message.what = CALCULATE_KEY; message.obj = "calculate result = " + result; myHandler.sendMessage(message); // sendMessageDelayed(Message msg, long delayMillis): 在指定的延迟时间后发送Messag, delayMillis为单位为毫秒 // myHandler.sendMessageDelayed(message,5000); } }).start(); } private long factorial(int num){ if (num < 2) return 1; return num * factorial(num - 1); } }
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".handle.EventHandlerActivity"> <Button android:id="@+id/calculateBtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Calculate Factorial" android:textAllCaps="false"/> <TextView android:id="@+id/resultTv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/calculateBtn" android:layout_marginTop="16dp"/> </RelativeLayout>
Handler是在主线程中创建的,handleMessage()方法也会在主线程中执行
,故不存在UI操作引起的线程安全问题主要组件:
基本使用如下:
public void sendAlarm(){ Intent intent = new Intent(ALARM_ACTION); // 创建用于广播的延迟意图 PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_IMMUTABLE); // 从系统服务中获取闹钟管理器 AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); // 闹钟的触发时机 long triggerTime = SystemClock.elapsedRealtime() + 2 * 1000; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 允许在空闲时发送广播(Android6.0之后新增的方法) alarmManager.setAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerTime, pendingIntent); } else { // 设置一次性闹钟,延迟若干秒后,携带延迟意图发送闹钟广播(Android6.0之后,set方法在暗屏时不保证发送广播) alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerTime,pendingIntent); } // 设置重复闹钟,每隔一定时间间隔就发送闹钟广播(从Android4.4开始,setRepeating方法不保证按时发送广播) // alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),1000,pendingIntent); // 取消闹钟 // alarmManager.cancel(pendingIntent); // 获取下一个闹钟的信息 // alarmManager.getNextAlarmClock(); }
@SystemService(Context.ALARM_SERVICE) public class AlarmManager { private static final String TAG = "AlarmManager"; // ... /** @hide */ @IntDef(prefix = { "RTC", "ELAPSED" }, value = { RTC_WAKEUP, RTC, ELAPSED_REALTIME_WAKEUP, ELAPSED_REALTIME, }) // ... @Retention(RetentionPolicy.SOURCE) public @interface AlarmType {} public void set(@AlarmType int type, long triggerAtMillis, @NonNull PendingIntent operation) { setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null, (Handler) null, null, null); } // ... }
参数说明:
RTC_WAKEUP、RTC、ELAPSED_REALTIME_WAKEUP、ELAPSED_REALTIME
,WAKEUP
的会唤醒CPU,带ELAPSED_REALTIME
的从系统开机算起前面我们知道了如何异步地处理消息,实现原理,现在再来全面地看看消息异步处理解决的问题:
实现异步处理的方式:Handler(sendMessage和sendMessageDelayed方法)、Timer、Alarm机制、WorkManager、Coroutine,篇幅受限,这里不多讲解
参考资料:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。