【Android子线程更新UI】


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值