
一、技术概述1.1 技术介绍Android系统中默认所有操作都运行在主线程UI线程中主线程负责处理UI更新、用户交互等操作。如果在主线程中执行耗时操作如网络请求、文件读写、复杂计算会导致界面卡顿甚至ANR应用无响应。多线程技术就是将耗时操作放到子线程中执行避免阻塞主线程提高应用的流畅性和响应速度。1.2 适用场景网络请求、文件上传下载数据库操作、大量数据计算音视频解码、图片处理定时任务、倒计时功能所有耗时超过50ms的操作都应该放到子线程执行1.3 优缺点实现方式优点缺点适用场景ThreadHandler灵活可控功能强大需要手动处理线程切换代码相对繁琐复杂的多线程场景AsyncTask简单易用自动处理线程切换版本兼容性问题不适合长时间耗时操作短时间的后台任务HandlerThread自带消息循环的线程可以处理串行任务不适合并行任务串行执行的后台任务IntentService适合执行后台串行任务执行完自动销毁无法并行处理任务后台串行任务如下载、上传线程池复用线程减少线程创建销毁开销控制并发数配置相对复杂大量频繁的耗时操作二、基本效果使用多线程后可以实现执行网络请求时界面仍然可以响应用户操作不会卡顿游戏倒计时可以在后台运行不会因为用户操作界面而暂停上传头像、下载文件等操作可以在后台执行不影响用户使用其他功能大量数据计算时界面保持流畅三、技术本质Android多线程的本质是CPU时间片的轮转调度多个线程交替执行宏观上看起来是同时运行的。Android多线程核心要解决两个问题耗时操作放到子线程执行避免阻塞主线程子线程执行完成后切换回主线程更新UI因为Android规定只有主线程才能更新UI 运行流程主线程收到耗时操作请求创建子线程将耗时操作放到子线程执行子线程执行耗时操作执行过程中可以通知主线程更新进度子线程执行完成后通过Handler、runOnUiThread等方式切换回主线程主线程根据执行结果更新UI四、核心原理4.1 线程状态状态说明新建状态New创建了Thread对象但还没有调用start()方法就绪状态Runnable调用了start()方法等待CPU调度执行运行状态Running获得CPU时间片正在执行阻塞状态Blocked因为某些原因暂停执行如sleep()、wait()、IO阻塞死亡状态Terminated线程执行完成或异常退出4.2 线程切换原理子线程不能直接更新UI必须切换回主线程常用的切换方式Handler.sendMessage()/post()将消息发送到主线程的消息队列由主线程处理Activity.runOnUiThread()直接在子线程中执行主线程的代码View.post()/postDelayed()将Runnable投递到主线程执行AsyncTask的onProgressUpdate()/onPostExecute()自动切换到主线程五、使用示例5.1 ThreadHandler方式// 主线程中创建Handler private Handler mHandler new Handler(Looper.getMainLooper()) { Override public void handleMessage(NonNull Message msg) { super.handleMessage(msg); switch (msg.what) { case 1001: // 更新UI int progress (int) msg.obj; progressBar.setProgress(progress); break; case 1002: // 任务完成 Toast.makeText(MainActivity.this, 下载完成, Toast.LENGTH_SHORT).show(); break; } } }; // 启动子线程执行耗时操作 new Thread(new Runnable() { Override public void run() { for (int i 0; i 100; i) { try { // 模拟下载耗时 Thread.sleep(50); // 发送进度消息到主线程 Message msg Message.obtain(); msg.what 1001; msg.obj i; mHandler.sendMessage(msg); } catch (InterruptedException e) { e.printStackTrace(); } } // 发送完成消息 mHandler.sendEmptyMessage(1002); } }).start();5.2 runOnUiThread简化方式new Thread(new Runnable() { Override public void run() { // 子线程执行耗时操作 final String result doLongTimeOperation(); // 切换到主线程更新UI runOnUiThread(new Runnable() { Override public void run() { tvResult.setText(result); } }); } }).start();5.3 AsyncTask方式// 三个泛型参数1.传入参数类型 2.进度更新类型 3.返回结果类型 private class DownloadTask extends AsyncTaskString, Integer, Boolean { // 任务开始前执行运行在主线程 Override protected void onPreExecute() { super.onPreExecute(); progressBar.setVisibility(View.VISIBLE); } // 后台执行耗时操作运行在子线程 Override protected Boolean doInBackground(String... urls) { String url urls[0]; for (int i 0; i 100; i) { try { Thread.sleep(50); // 发布进度触发onProgressUpdate publishProgress(i); } catch (InterruptedException e) { e.printStackTrace(); return false; } } return true; } // 进度更新运行在主线程 Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); progressBar.setProgress(values[0]); } // 任务完成运行在主线程 Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); progressBar.setVisibility(View.GONE); if (result) { Toast.makeText(MainActivity.this, 下载完成, Toast.LENGTH_SHORT).show(); } else { Toast.makeText(MainActivity.this, 下载失败, Toast.LENGTH_SHORT).show(); } } } // 使用方式 new DownloadTask().execute(https://example.com/file.apk);5.4 线程池方式// 创建固定大小线程池最多同时3个线程执行 ExecutorService threadPool Executors.newFixedThreadPool(3); // 执行任务 threadPool.execute(new Runnable() { Override public void run() { // 执行耗时操作1 } }); threadPool.execute(new Runnable() { Override public void run() { // 执行耗时操作2 } }); // 关闭线程池 threadPool.shutdown();5.5 在专注力测试APP中的应用示例游戏倒计时功能使用ThreadHandler实现// 游戏倒计时 private Handler mGameHandler new Handler(Looper.getMainLooper()); private Runnable mCountDownRunnable; private int mCountDownTime 30; // 30秒 private void startCountDown() { mCountDownRunnable new Runnable() { Override public void run() { mCountDownTime--; tvTime.setText(剩余时间 mCountDownTime s); if (mCountDownTime 0) { // 每秒执行一次 mGameHandler.postDelayed(this, 1000); } else { // 时间到结束游戏 endGame(); } } }; mGameHandler.post(mCountDownRunnable); } Override protected void onDestroy() { super.onDestroy(); // 移除回调避免内存泄漏 mGameHandler.removeCallbacks(mCountDownRunnable); }六、常见问题与解决方案6.1 ANR问题问题应用出现无响应弹窗原因主线程被耗时操作阻塞超过5秒或者BroadcastReceiver 10秒内没有处理完成解决方案所有耗时操作网络、文件、数据库、复杂计算都放到子线程执行避免在主线程中执行复杂的布局计算、大量循环操作6.2 内存泄漏问题问题非静态内部类线程持有Activity引用Activity销毁后线程还在运行导致Activity无法回收解决方案使用静态内部类弱引用的方式private static class MyRunnable implements Runnable { private WeakReferenceMainActivity mActivity; public MyRunnable(MainActivity activity) { mActivity new WeakReference(activity); } Override public void run() { MainActivity activity mActivity.get(); if (activity ! null) { // 执行操作 } } }Activity销毁时停止正在执行的线程移除Handler的回调和消息6.3 线程安全问题问题多个线程同时访问同一个数据导致数据混乱解决方案使用synchronized关键字同步访问共享数据使用线程安全的集合类如ConcurrentHashMap尽量避免多个线程同时修改同一个变量6.4 子线程更新UI崩溃问题在子线程中直接更新UI抛出CalledFromWrongThreadException异常解决方案使用Handler、runOnUiThread、View.post等方式切换到主线程再更新UI严格遵守只有主线程才能更新UI的规则七、学习总结7.1 学习收获掌握了Android中几种常用的多线程实现方式了解了每种方式的优缺点和适用场景理解了线程切换的原理能够在开发中选择合适的多线程实现方式避免主线程阻塞和ANR问题。7.2 项目应用场景在本次专注力测试APP中多线程的应用场景游戏倒计时、计时功能的实现网络请求登录、上传成绩、获取排行榜等放到子线程执行头像上传、图片加载处理本地数据库操作、文件读写游戏成绩计算、复杂逻辑处理