Android开发-多线程

一.多线程

1.为什么要在Android中使用多线程

  • 提高用户体验或避免ANR

    • 在事件处理代码中需要使用多线程,响应时间超过5s,即会出现ANR(Application is not responding),并因为响应较慢导致用户体验很差

这里写图片描述

2.ANR详解

  • Android的main线程负责处理UI的绘制,为了防止应用程序反应较慢导致系统无法正常运行做如下处理
    • 当用户输入事件(Activity)在5秒内无法得到响应,那么系统会弹出ANR对话框
    • BroadcastReciever超过10秒没执行完也会弹出ANR对话
  • 事件处理的原则:所有可能耗时的操作都放到其他线程去处理

3.异步

  • 应用中有些情况下并不一定需要同步阻塞去等待返回结果,可以通过多线程来实现异步

  • 例如:某个Activity需要从云端获取一些图片,加载图片比较耗时,这时需要使用异步加载,加载完成一个图片刷新一个

4.主线程Activity与子线程

  • 默认启动的第一个Activity成为主线程
  • 由此Activity创建的线程(子线程)无法对主线程控制的内容进行修改

这里写图片描述

这里写图片描述

public class MainActivity extends AppCompatActivity {

     private Button button;

     private TextView textView;

     private int count;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

    }

    private void initView() {

         button=(Button) findViewById(R.id.button);
         textView=(TextView) findViewById(R.id.textView);
         button.setOnClickListener(new ClickEvent());
         count=0;
    }


    public class ClickEvent implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    new Thread(){
                        @Override
                        public void run() {
                             while(count<1000){
                                 count+=10;
                                 textView.setText("你的count值为"+count);
                                 try {
                                     Thread.sleep(1000);
                                 }catch (Exception e){

                                 }
                             }
                        }
                    }.start();
                    break;
            }
        }
    }
}

你如果用非UI线程来更新UI,如上面这段代码,就会出现停止运行的错误

  • 所以只有UI线程才能更新UI

解决方式

  • 方式1:其他线程委托UI线程更新UI
  • 方式2:通过Handler发送Message给UI线程,令UI线程根据Message消息更新UI
  • 方式3:使用Android提供的AsyncTask

二.其他线程委托UI线程

  • Activity.runOnThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable,long)
    此种方式最简单,只适用较简单的情况
  • 就拿倒计时的例子来讲

这里写图片描述

public class MainActivity extends AppCompatActivity {

     private Button button;

     private TextView textView;

     private int count;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

    }

    private void initView() {

         button=(Button) findViewById(R.id.button);
         textView=(TextView) findViewById(R.id.textView);
         button.setOnClickListener(new ClickEvent());
         count=1000;
    }


    public class ClickEvent implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            while(count>0){
                                count-=10;
                                textView.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        textView.setText("你的count的值为"+count);
                                    }
                                });
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                    break;
            }
        }
    }
}

三.Handler

1.Handler线程通讯模型

这里写图片描述

2.Handler线程间通讯

  • Handler
    • Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯
  • Looper
    • Looper负责管理线程的消息队列和消息循环
  • Message
    • Message是线程间通讯的消息载体,两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想要传递的消息
  • MessageQueue
    • MessageQueue是消息队列,先迚先出,它的作用是保存有待线程处理的消息

3.Message

  • 通过obtain获取Message对象,利用Android的内存回收机制,提高效率
Message msg = Message.obtain()
Message msg=hanler.obtainMessage();

这里写图片描述

  • Message传递大量数据

    • 若线程间通讯的数据比较复杂,比如需要使用键值对来存放大量数据,此时需要使用setData()和getData()方法,用Bundle对象来封装数据
Message msg = Message.obtain();
msg.what = 101;
Bundle bundle = new Bundle();
bundle.putInt("number",12);
bundle.putString("Name","Rice");
bundle.putString("Hobby","Swimming");
msg.setData(bundle);
  • 封装好后,使用Handler对象将此Message发送出去

4.Handler的用途

  • 将Message或Runnable对象发送给其他线程
  • 处理来自其他线程的Message

5.Handler的主要方法

发送Message:sendEmptyMessage(int)、sendMessage(Message)、sendMessageAtTime(Message,long)、sendMessageDelayed(Message,long)
处理Message:handleMessage(Message
  • Handler Post方法
boolean post(Runnable r):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,立即执行
boolean postAtTime(Runnable r,long uptimeMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,在特定的时间执行
boolean postDelayed(Runnable r,long delayMillis):把一个Runnable入队到消息队列中,UI线程从消息队列中取出这个对象后,延迟delayMills秒执行
void removeCallbacks(Runnable r):从消息队列中移除一个Runnable对象
  • Handler Message方法

    • 在Handler中,与Message发送消息相关的方法有
Message obtainMessage():获取一个Message对象。
boolean sendMessage():发送一个Message对象到消息队列中,并在UI线程取到消息后,立即执行。
boolean sendMessageDelayed():发送一个Message对象到消息队列中,在UI线程取到消息后,延迟执行
boolean sendEmptyMessage(int what):发送一个空的Message对象到队列中,并在UI线程取到消息后,立即执行
void removeMessage():从消息队列中移除一个未响应的消息
boolean sendEmptyMessageDelayed(int what,long delayMillis):发送一个空Message到消息队列中,延迟执行

6.实例一-Handler实现倒计时

这里写图片描述
这里写图片描述

public class MainActivity extends AppCompatActivity {

     private Button button;

     private TextView textView;

     private int count;

    private Handler handler;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

         handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 0x123:
                        textView.setText("你的count值为"+msg.arg1);
                        break;
                    case 0x1:
                        textView.setText("倒计时结束");
                        break;
                    default:
                        break;
                }
            }
        };
    }

    private void initView() {

         button=(Button) findViewById(R.id.button);
         textView=(TextView) findViewById(R.id.textView);
         button.setOnClickListener(new ClickEvent());
         count=10;
    }


    public class ClickEvent implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.button:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            while(count>0){
                                count-=1;

                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }

                                if(count>0) {
                                    Message message = Message.obtain();
                                    message.what = 0x123;
                                    message.arg1 = count;
                                    handler.sendMessage(message);
                                }else{
                                    handler.sendEmptyMessage(0x1);//发送空的
                                }
                            }
                        }
                    }).start();
                    break;
            }
        }
    }
}

7.实例二-Handler更新进度条

这里写图片描述

这里写图片描述

public class MainActivity extends AppCompatActivity {

     private Button button;

     private TextView textView;

    private ProgressBar progressBar;

     private int count;

    private Handler handler;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();

         handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what){
                    case 0x123:
                        textView.setText("进度为"+msg.arg1+"%");
                        progressBar.setProgress(msg.arg1);
                        break;
                    case 0x1:
                        textView.setText("传输结束");
                        break;
                    default:
                        break;
                }
            }
        };
    }

    private void initView() {

         button=(Button) findViewById(R.id.btn_down);
         textView=(TextView) findViewById(R.id.tv_down_cotent);
         progressBar=(ProgressBar) findViewById(R.id.pb_down);
         button.setOnClickListener(new ClickEvent());
         count=10;
    }


    public class ClickEvent implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_down:
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            for(int i=1;i<=10;i++){

                                Message message=Message.obtain();
                                message.what=0x123;
                                message.arg1=i*10;
                                handler.sendMessage(message);

                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }

                            }
                            handler.sendEmptyMessage(0x1);
                        }
                    }).start();
                    break;
            }
        }
    }
}

四.AsyncTask

1.Thread+Handler 缺陷

  • 线程的开销较大.如果每个任务都要创建一个线程,那么程序的效率要低很多
  • 线程无法管理,匿名线程创建并启劢后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负
  • 另外,在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿

2.AsyncTask概述

  • AsyncTask的特点是任务在主UI线程之外运行,而回调方法是在主UI线程中,这就有效地避免了使用Handler带来的麻烦
  • AsyncTask定义了三种泛型类型 Params,Progress和Result

    • Params启动任务执行的输入参数
    • Progress后台任务执行的百分比
    • Result后台执行任务返回的结果
  • 使用AsyncTask简化多线程开发

    • AsyncTask与门用于完成非UI线程更新UI的任务
    • 本质上也是开启新线程执行耗时操作,然后将结果发送给UI线程
    • 优点:简化代码,减少编写线程间通信代码这一繁琐且易出错的过程

3.AsyncTask要点

  • AsyncTask为抽象类,必须先子类化
  • onPreExecute():开始执行前的准备工作
  • doInBackground(Params …):开始执行后台处理,并调用publishProgress(Progress )方法来更新实时的任务进度
  • onProgressUpdate(Progress …):在publishProgress()方法被调用后,UI线程将调用这个方法从而在界面上展示任务的进展情况
  • onPostExecute(Result):执行完成后的操作,传送结果给UI线程

  • Task的实例必须在UI 线程中创建

  • execute方法必须在UI线程中调用
  • 不要手动的调用onPreExecute(),onPostExecute(Result),doInBackground(Params…),onProgressUpdate(Progress…)这几个方法,需要在UI线程中实例化这个task来调用
  • 该task只能被执行一次,否则多次调用时将会出现异常
  • doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示迚度的参数,第三个为doInBackground返回和onPostExecute传入的参数
(1).构造参数解读

这里写图片描述

这里写图片描述

private class task extends AsyncTask<String, String, String>
AsyncTask<>的参数类型由用户设定,这里设为三个String
第一个String代表输入到任务的参数类型,也即是doInBackground()的参数类型,调用execute()方法时传入的参数类型
第二个String代表处理过程中的参数类型,也就是doInBackground()执行过程中的产出参数类型,通过
publishProgress()发消息,传递给onProgressUpdate()一般用来更新界面
第三个String代表任务结束的产出类型,也就是doInBackground()的返回值类型,和onPostExecute()的参数类型

4.AsyncTask实例-更新进度条

public class MainActivity extends AppCompatActivity {

    private Button button1,button2;

    private ProgressBar progressBar;

    private TextView textView;

    private MyTask myTask;


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();


    }

    private void initView() {

        button1=(Button) findViewById(R.id.btn_download_async);
        button2=(Button) findViewById(R.id.btn_cancle_async);

        button1.setOnClickListener(new ClickEvent());
        button2.setOnClickListener(new ClickEvent());

        progressBar=(ProgressBar) findViewById(R.id.pb_async);
        textView=(TextView) findViewById(R.id.tv_async);

    }


    public class ClickEvent implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.btn_download_async:
                    if(myTask==null){
                        myTask=new MyTask();
                    }
                    myTask.execute(100);
                    break;
                case R.id.btn_cancle_async:
                    if (myTask != null && myTask.getStatus() == AsyncTask.Status.RUNNING) {
                        myTask.cancel(true);
                        myTask = null;
                    }
                    break;
            }
        }
    }

    public  class MyTask extends AsyncTask<Integer,Integer,String>{

        // 后面尖括号内分别是
        // 参数(例子里是线程休息时间),
        // 进度(publishProgress用到),
        // 返回值 类型
        // 第一个执行方法

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            button1.setEnabled(false);
            button2.setEnabled(true);
            textView.setText("点击按钮开始下载");
        }

        // 第二个执行方法,onPreExecute()执行完后执行
        // @Override
        protected String doInBackground(Integer... params) {
            for(int i=0;i<=100;i++){
                publishProgress(i);
                try {
                    Thread.sleep(params[0]);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(isCancelled())
                    return  null;
            }
            return "执行完毕";
        }
        // 这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数
        // 但是这里取到的是一个数组,所以要用progress[0]来取值
        // 第n+1个参数就用progress[n]来取值
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            if(isCancelled())
                return;
            textView.setText("当前已下载"+values[0]+"%");
            progressBar.setProgress(values[0]);
        }

        // doInBackground返回时触发,换句话说,就是doInBackground执行完后触发
        // 这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"
        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
            textView.setText(s);
            button1.setEnabled(true);
            button2.setEnabled(false);
            myTask=null;
        }


        @Override
        protected void onCancelled() {
            super.onCancelled();
            button1.setEnabled(true);
            button2.setEnabled(false);
            progressBar.setProgress(0);
            myTask=null;
        }
    }
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值