文章目录
public class HandlerActivity extends AppCompatActivity {
private Button btn_update;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
btn_update = findViewById(R.id.btn_update);
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
btn_update.setText("子线程更新UI了子线程更新UI了子线程更新UI了子线程更新UI了");
}
}).start();
}
});
}
}
运行上述代码,点击按钮后会发生Crash。查看报错显示:PID: 15124 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view。
看到这里相信很多朋友已经知道,Android为了线程安全,并不允许我们在UI线程外更新UI。如果两个线程同时更新UI,可能对同一个控件操作造成混乱。很多时候我们做界面刷新都需要通过Handler来通知UI组件更新。除了用Handler完成界面更新外,还可以使用runOnUiThread()来更新,或更高级的事务总线等。本文旨在介绍几种子线程更新UI的方式。
小插曲:发现一个很奇怪的问题。。。上述代码理论上会运行crash,但作者在后续运行中发现代码竟能正常运行,令人匪夷所思。找了一通资料后终于找到了问题所在,详见:https://blog.youkuaiyun.com/u011288271/article/details/113114371?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522e30431960c0a1693b392229ae66b22fb%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=e30431960c0a1693b392229ae66b22fb&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~rank_v31_ecpm-13-113114371-null-null.142
1、开启了硬件加速
2、在宽度固定,高度写死或者 新的UI内容不会改变之前UI的高度
如果是上述两种情况子线程更新UI不会报错!!!
一、new Handler()
public class HandlerActivity extends AppCompatActivity {
private final String TAG = "HandlerActivity";
private Button btn_update;
private Handler handler;
@SuppressLint("HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
btn_update = findViewById(R.id.btn_update);
// 当前线程Id
int currentId = (int) Thread.currentThread().getId();
String currentName = Thread.currentThread().getName();
Log.d(TAG, "currentId: " + currentId + " currentName: " + currentName);
handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == 1) {
btn_update.setText("子线程更新UI了");
}
}
};
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
// 新线程Id
int newId = (int) Thread.currentThread().getId();
String newName = Thread.currentThread().getName();
Log.d(TAG, "newId: " + newId + " newName: " + newName);
handler.sendEmptyMessage(1);
}
}).start();
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 避免内存泄漏
handler.removeCallbacksAndMessages(null);
}
}
20:56:00.998 4956-4956 com.example.myapplication D currentId: 2 currentName: main
20:56:01.078 511-539 system_server I Displayed com.example.myapplication/.demo02.HandlerActivity: +102ms
20:56:01.751 4956-5014 com.example.myapplication D newId: 394 newName: Thread-2
二、runOnUiThread
public class RunOnUiThreadActivity extends AppCompatActivity {
private final String TAG = "RunOnUiThreadActivity";
private Button btn_update;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_run_on_ui_thread);
btn_update = findViewById(R.id.btn_update);
// 当前线程Id
int currentId = (int) Thread.currentThread().getId();
String currentName = Thread.currentThread().getName();
Log.d(TAG, "currentId: " + currentId + " currentName: " + currentName);
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
// 新线程Id
int newId = (int) Thread.currentThread().getId();
String newName = Thread.currentThread().getName();
Log.d(TAG, "newId: " + newId + " newName: " + newName);
btn_update.setText("子线程更新UI了");
}
});
}
}).start();
}
});
}
}
21:07:31.564 5203-5203 com.example.myapplication D currentId: 2 currentName: main
21:07:31.643 511-539 system_server I Displayed com.example.myapplication/.demo02.RunOnUiThreadActivity: +107ms
21:07:32.545 5203-5203 com.example.myapplication D newId: 2 newName: main
三、View.postDelayed(Runnable action, long delayMillis)&View.post(Runnable action)
public class ViewPostActivity extends AppCompatActivity {
private final String TAG = "ViewPostActivity";
private Button btn_update;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_post);
btn_update = findViewById(R.id.btn_update);
// 当前线程Id
int currentId = (int) Thread.currentThread().getId();
String currentName = Thread.currentThread().getName();
Log.d(TAG, "currentId: " + currentId + " currentName: " + currentName);
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
// btn_update.post(new Runnable() {
// @Override
// public void run() {
// // 新线程Id
// int newId = (int) Thread.currentThread().getId();
// String newName = Thread.currentThread().getName();
// Log.d(TAG, "newId: " + newId + " newName: " + newName);
//
// btn_update.setText("子线程更新UI了");
// }
// });
btn_update.postDelayed(new Runnable() {
@Override
public void run() {
// 新线程Id
int newId = (int) Thread.currentThread().getId();
String newName = Thread.currentThread().getName();
Log.d(TAG, "newId: " + newId + " newName: " + newName);
btn_update.setText("子线程更新UI了");
}
}, 3000);
}
}).start();
}
});
}
}
21:15:24.201 5377-5377 com.example.myapplication D currentId: 2 currentName: main
21:15:24.280 511-539 system_server I Displayed com.example.myapplication/.demo02.ViewPostActivity: +114ms
21:15:25.207 5377-5377 com.example.myapplication D newId: 2 newName: main
四、AsyncTask(已废弃)
public class UiUpdateActivity extends AppCompatActivity {
private final String TAG = "UiUpdateActivity";
private TextView mTextView;
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ui_update);
// 当前线程Id
int currentId = (int) Thread.currentThread().getId();
String currentName = Thread.currentThread().getName();
Log.d(TAG, "currentId: " + currentId + " currentName: " + currentName);
mTextView = findViewById(R.id.tv_update);
mTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
UpdateAsyncTask task = new UpdateAsyncTask();
task.execute();
}
});
}
class UpdateAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... strings) {
publishProgress();
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
// 新线程Id
int newId = (int) Thread.currentThread().getId();
String newName = Thread.currentThread().getName();
Log.d(TAG, "newId: " + newId + " newName: " + newName);
mTextView.setText("子线程更新UI了");
}
}
}
2025-03-12 21:24:26.077 28638-28638 UiUpdateActivity com.example.demo D currentId: 2 currentName: main
2025-03-12 21:24:26.181 512-538 ActivityTaskManager system_server I Displayed
com.example.demo/.demo.UiUpdateActivity: +159ms
2025-03-12 21:24:27.210 28638-28638 UiUpdateActivity com.example.demo D newId: 2 newName: main
五、HandlerThread
public class HandleThreadActivity extends AppCompatActivity {
private final String TAG = "HandleThreadActivity";
private Button btn_update;
private HandlerThread mHandlerThread;
private Handler mHandler;
@SuppressLint("HandlerLeak")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handle_thread);
btn_update = findViewById(R.id.btn_update);
// 当前线程Id
int currentId = (int) Thread.currentThread().getId();
String currentName = Thread.currentThread().getName();
Log.d(TAG, "currentId: " + currentId + " currentName: " + currentName);
mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 2) {
btn_update.setText("子线程更新UI了");
}
}
};
btn_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 创建HandlerThread 指定线程名称
mHandlerThread = new HandlerThread("MyThread");
mHandlerThread.start();
mHandler.post(new Runnable() {
@Override
public void run() {
// 新线程Id
int newId = (int) Thread.currentThread().getId();
String newName = Thread.currentThread().getName();
Log.d(TAG, "newId: " + newId + " newName: " + newName);
mHandler.sendEmptyMessage(2);
}
});
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
// 结束时释放资源避免内存泄漏
if (mHandlerThread != null) {
mHandlerThread.quitSafely();
mHandlerThread = null;
}
}
}
21:30:29.136 5725-5725 com.example.myapplication D currentId: 2 currentName: main
21:30:29.204 511-539 system_server I Displayed com.example.myapplication/.demo02.HandleThreadActivity: +95ms
21:30:30.038 5725-5774 com.example.myapplication D newId: 430 newName: MyThread
通过网盘分享的文件:MyApplication.zip
链接: https://pan.baidu.com/s/178tDWVFlS-dgs9A7-PStsw?pwd=ksg1 提取码: ksg1