打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Android Thread线程

Android线程主要用来操作一些耗时的任务,可以在后台运行,例如请求网络、下载数据、上传数据和加载图片等。

无论何时启动App,所有的组件都会运行在一个单独的线程中(默认的)叫作主线程。这个线程主要用于处理UI的操作并为视图组件和小部件分发事件等,因此主线程也被称作UI线程。

如果你在UI线程中运行一个耗时操作,那么UI就会被锁住,直到这个耗时操作结束。对于用户体验来说,这是非常糟糕的。

1 Thread线程的使用方式

先看一下Thread线程的使用方式,代码如下:

public class MainActivity extends AppCompatActivity {    private TextView tv_text;    private Thread thread;

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv_text = findViewById(R.id.tv_text); // 在UI线程中开启一个子线程 thread = new Thread(new Runnable() { @Override public void run() { //一些耗时操作 //... //操作完成后可以通过Handler发送消息回调给UI线程 Message msg = Message.obtain(); msg.what = 1; msg.obj = "向UI线程中发送消息!"; handler.sendMessage(msg); } }); thread.start(); }

private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: // 把Thread线程发送来的消息显示到屏幕上 tv_text.setText("what=" + msg.what + ", " + msg.obj); break; } } };}

2 HandlerThread

Android提供了封装好的Thread,叫作HandlerThread,基本使用方法如下

首先需要创建handlerThread类型的线程对象,代码如下:

  HandlerThread myThread = new HandlerThread("MyThread");  //接着启动线程,代码如下:   myThread.start();

然后创建与myThread线程相关联的Handler对象,代码如下:

Handler myHandler = new Handler(myThread.getLooper()) {      public void handleMessage(Message msg) {          Log.i("looper", Thread.currentThread().getName());      }  };

3 线程池

线程池会用到ExecutorService和Executors。ExecutorService是一个接口,继承了Executors接口,定义了一些操作线程池的生命周期的方法。Executors接口提供了4种线程池,分别是newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor和newScheduledThreadPool。

newFixedThreadPool创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,代码如下:

ExecutorService executorService = Executors.newFixedThreadPool(5);    for (int i = 0; i < 20; i++) {        Runnable syncRunnable = new Runnable() {            @Override            public void run() {                Log.e(TAG, Thread.currentThread().getName());            }        };        executorService.execute(syncRunnable);      }

运行结果:总共会创建5个线程,开始时会执行5个线程,当5个线程都处于活动状态时,再次提交的任务会加入队列等待其他线程运行结束,当线程处于空闲状态时会被下一个任务复用。


newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过需要,可灵活回收空闲线程,代码如下:

ExecutorService executorService = Executors.newCachedThreadPool();    for (int i = 0; i < 100; i++) {        Runnable syncRunnable = new Runnable() {            @Override            public void run() {                Log.e(TAG, Thread.currentThread().getName());            }        };        executorService.execute(syncRunnable);    }

运行结果:可以看出缓存线程池大小是不定值,可以根据需要创建不同数量的线程,在使用缓存型线程池时,先查看池中有没有以前创建的线程。如果有,就复用;如果没有,就创建新的线程加入池中。缓存型池通常用于执行一些生存期很短的异步型任务。


newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,以保证所有任务按照指定顺序(FIFO、LIFO、优先级)执行,代码如下:

ExecutorService executorService = Executors.newSingleThreadExecutor();      for (int i = 0; i < 20; i++) {          Runnable syncRunnable = new Runnable() {              @Override              public void run() {                  Log.e(TAG, Thread.currentThread().getName());              }          };          executorService.execute(syncRunnable);      }

运行结果:只会创建一个线程,当上一个线程执行完之后才会执行第二个线程。


newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行,代码如下:

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);    for (int i = 0; i < 20; i++) {        Runnable syncRunnable = new Runnable() {            @Override            public void run() {                Log.e(TAG, Thread.currentThread().getName());            }        };        executorService.schedule(syncRunnable, 5000, TimeUnit.MILLISECONDS);    }

运行结果和newFixedThreadPool线程池类似,总共会创建5个线程,不同的是newScheduledThreadPool线程池是延迟一定时间之后才执行。

当然除了这些内置的线程池,也可以通过ThreadPoolExecutor定义自己的线程池。

4 线程池

由于线程池使用起来比较麻烦,而单独使用Thread又不方便管理线程,而且可能会造成线程重复创建,开销严重。

为了降低开发者的开发难度,AsyncTask应运而生。AsyncTask是对线程池的一个封装,使用其自定义的Executor来调度线程的执行方式(并发还是串行),并使用Handler来完成子线程和主线程数据的共享。

AsyncTask是Android提供的轻量级异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度,最后给UI主线程反馈执行的结果。

AsyncTaskRun asyncTaskRun = new AsyncTaskRun();asyncTaskRun.execute("http://square.github.io/picasso/static/sample.png");

AsyncTaskRun 定义如下:

public class AsyncTaskRun extends AsyncTask<String, Void, Bitmap> {      /**        * 在doInBackground方法中进行异步任务的处理        */      @Override      protected Bitmap doInBackground(String... strings) {          //要执行的具体任务逻辑          return null;      }

/** * 用于异步处理前的操作 */ @Override protected void onPreExecute() { super.onPreExecute(); }

/** * 用于异步处理结束后返回结果到UI线程的操作 */ @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); }

/** * 用于更新任务操作进度 */ @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); }

/** * 任务关闭回调 */ @Override protected void onCancelled(Bitmap bitmap) { super.onCancelled(bitmap); } }

doInBackground(Params... params):当线程池执行任务时调用该函数在子线程中处理比较耗时的操作,如执行下载任务的逻辑就写在这里。此函数是抽象函数,必须实现。

onPreExecute():该函数是在任务没被线程池执行之前调用在UI线程中运行,比如在开始下载文件操作前执行的逻辑就可以写在这里,也可以不用实现。

onPostExecute(Result result):该函数是任务在线程池中执行结束后回调给UI主线程的结果,如下载结果,也可以不用实现这个函数。

 onProgressUpdate(Progress... values):该函数是任务在线程池中执行处于执行中的状态时,回调给UI主线程的任务进度,比如上传或者下载进度。如果不需要获取任务进度的话,也可以不用实现这个函数。

 onPostExecute(Result result):该函数是任务在线程池中执行结束后回调给UI主线程的结果,如下载结果,也可以不用实现这个函数。

onCancelled(Result result)及onCancelled():表示任务关闭。

使用AsyncTask的注意事项:

· 必须在UI线程中创建AsyncTask实例;

· 只能在UI线程中调用AsyncTask的execute方法;

· AsyncTask被重写的4个方法是系统自动调用的,不应手动调用;

· 每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常;

· AsyncTask的4个方法中,只有doInBackground方法是运行在其他线程中,其他3个方法都是运行在UI线程中,也就说其他3个方法都可以进行UI的更新操作。


完毕

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Android中使用多线程的各种姿势
『进阶之路』—— 线程池
探讨android更新UI的几种方法
Android 多线程编程的总结
Android之多线程工作
线程、多线程与线程池详解
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服