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的更新操作。
完毕
联系客服