- 多线程开发为复杂的耗时处理功能提高了效率,同时也不影响UI界面的显示效果,是在Android开发或者Java开发中经常用到的一种开发机制
- 首先理解多线程的概念:
- 多线程并不是真正的多个任务在同一时间点上并发执行,而是分时间片来执行,即同一个时间点上执行的任务只有一个,但是从一小段时间来看,却是许多任务并发执行
- 多线程需要注重资源的分配
- 需要注意Java中多线程机制
- 主线程:即应用的UI线程
- 辅线程:即工作线程,不能直接操作UI
- Android中的多线程
- Handler类
- post方法
- post方法并没有新起线程,仍然运行在UI线程中,其本质上还是UI线程,只是是UI现成的子线程,可以优化UI操作性能
- 与View自带的post方法原理相同,都是在UI子线程中操作UI变化
-
Handler handler=new Handler(); handler.post(new Runnable() { @Override public void run() { //ToDo } });
- 注意:进行UI操作,例如修改UI控件属性等,虽然直接修改一般不会出现问题,但是如果在UI执行耗时操作时会造成卡顿,通常使用post来通过UI子线程来操作
- sendMessage方法
- post方法只适合在单独操作UI的情况下使用,如果其中涉及到耗时操作以及UI操作,例如:UI与Service通信,根据Service返回的结果修改UI,Service通常是一个比较耗时的操作,在这种情况下就不能直接使用post将耗时操作放到UI线程中,而是需要开启辅线程
- 辅线程是独立于UI线程之外的工作线程,在工作线程中能够处理复杂的业务逻辑计算等耗时操作,但是不能直接操作UI
- 辅线程(工作线程)与UI线程之间的交互
- UI线程中创建Handler
-
Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //UI 操作 } };
- 辅线程中创建Message消息,并向主线程的handler传递消息
-
Message message=new Message(); message.setData(new Bundle()); handler.sendMessage(message);
- 这样就可以实现工作线程执行耗时操作,UI线程负责界面处理
- post方法
- HandlerThread类
- HandlerThread可以另起新线程,但不能操作UI,属于工作线程
- 通过HandlerThread的Looper创建的Handler使用post方法也不能操作UI,只有通过主线程的Looper创建的Handler才能操作UI,即Handler的构造方法中不传参时表示的是使用主线程的Looper创建实例
- HandlerThread使用
- 创建实例
-
HandlerThread ht = new HandlerThread("handler thread"); ht.start();
- 实例化后必须使用start启动
- 通过HandlerThread实例的Looper创建Handler实例
-
Handler handler=new Handler(ht.getLooper());
- 通过Handler实例进行post方法调用
-
handler.post(new Runnable(){//这里run()方法其实还是在等ht.start()调用 public void run(){ Log.e("当前线程:",Thread.currentThread().getName());//这里打印的会是handler thread setTitle("哈哈");//这样必定报错 } });
- 在run中如果操作UI会直接报错,因为这个县城是工作线程,不能执行UI操作,而如果想要执行UI操作,需要通过sendMessage来实现,在UI线程中再实例化一个Handler实例,重写handlerMessage方法,然后在run中向该实例发送消息
- 这样实现会发现比较繁琐而且低效,不建议使用这种方法,即HandlerThread不建议使用
- 并且一个HandlerThread中创建的多个Handler,在post的时候并不是并打的,而是顺序执行的
- Java中创建线程方法
- Java提供了通用的多线程类与接口,能够简单而直接创建工作线程,不需要使用HandlerThread创建一个handler
- 通过new Thread(){run}.start();
-
new Thread(){ @Override public void run() { super.run(); //ToDo } }.start();
- 该方法是通过匿名类来实例化Thread的子类实例的,在run中处理复杂操作,并发送消息到UI线程中,完成与UI线程通信
- 通过new Thread(Runnable).start();
-
new Thread(new Runnable() { @Override public void run() { } }).start();
- 通过Runnable接口实例化Thread实例,与匿名类一样,只是两种不同的实例化方法
- AsyncTask异步任务
- Android中提供一种异步任务类,能够实现简单的工作线程与主线程操作,是Handler、Message的结合,使用更加简单
- 创建异步任务
- 注意AsyncTask中的5个可重写方法
- onPreExcute:是在异步任务执行前执行的操作,可以操作UI线程
- doInBackground(params):在工作线程中执行的操作
- onProgressUpdate(values):在工作线程执行过程中如果需要操作UI,就可以在doInBackground中使用publicProgress();来广播进度,在onProgressUpdate中就会收到进度值
- onPostExcute(retult):任务执行结束后调用,即将doInBackground中返回的结果传到该方法中
- 在实例化AsyncTask的时候可以直接指定不同方法中参数的类型,即AsyncTask是泛型类
- 在整个过程中只有一个方法doInBackground是在工作线层中运行的,其他三个方法都是在主线程中运行的
- 实例化
-
AsyncTask<String,String,String> asyncTask=new AsyncTask<String, String, String>() { @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected String doInBackground(String... params) { return null; } @Override protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); } @Override protected void onPostExecute(String s) { super.onPostExecute(s); } };
- 与Thread+Handler+Message实现多线程比较
- AsyncTask本质上就是Thread+Handler+Message的封装
- 在Activity被重新创建(设备状态改变,例如屏幕方向变化引起的Activity重启)时,任务会被取消
- 在线程池中只能容纳5个后台运行程序,再多会造成阻塞,这里表示的是通过AsyncTask创建的线程,用Thread创建的不算
- 执行方式不同,AsyncTask通过excute来执行,Thread通过start来执行
- 注意AsyncTask中的5个可重写方法
- ExecutorService线程池
- 线程池适合处理大量线程的操作,例如多线程加载1000张图片,多线程批量下载
- 实例化线程池
- 单线程
-
ExecutorService service=Executors.newSingleThreadExecutor();
- 固定数量线程
-
ExecutorService service=Executors.newFixedThreadPool(10);
- 动态线程
-
ExecutorService service=Executors.newCachedThreadPool();
- 执行线程操作:
- submit(Runnable):创建并执行一个工作线程
- Handler类