多线程异步机制Handler以及AsyncTask

Posted 曹半斤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程异步机制Handler以及AsyncTask相关的知识,希望对你有一定的参考价值。

android当中多线程的写法跟JAVA当中相差不了太多,只需要新建一个类继承自Thread类即可。然后重写父类的run方法。并在里面写耗时逻辑即可

class MyThread extends Thread {
@Override
public void run() {
// 处理具体的逻辑
}
}

启动线程

new MyThread().start();

当然也可以像下面这样写,这样的写法更加适合,因为使用继承的方式耦合性有点高

class MyThread implements Runnable {
@Override
public void run() {
// 处理具体的逻辑
}
}

启动线程也发生了相应的改变:

MyThread myThread = new MyThread();
new Thread(myThread).start();

不过我们通常会这样写:

new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
}
}).start();

由于Android更新UI界面的操作不能放在子线程当中运行,只能放在主线程当中运行,和许多其他的 GUI 库一样,Android的 UI 也是线程不安全的。也就是说,如果想要更新应用程序里的 UI元素,则必须在主线程中进行,否则就会出现异常。比如:

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.change_text:
            new Thread(new Runnable() {
                @Override
                public void run() {
                    text.setText("Nice to meet you");
                }
            }).start();
            break;
        default:
            break;
        }
    }

就会出现如下异常
这里写图片描述

有的时候我们必须要在子线程里面做一些耗时操作,来更新UI,比如要写一个网速实时更新,那怎么做呢。Android为我们提供了Handler,异步消息处理机制。我们这样写:

public class MultithreadActivity extends Activity {
    private Button change;
    private TextView text;
    private static final int CHANGETEXT = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.multithreadactivity);
        change = (Button) findViewById(R.id.change);
        text = (TextView) findViewById(R.id.text);
        change.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {

                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = CHANGETEXT;
                        handler.sendMessage(message);
                    }
                }).start();
            }
        });
    }

    @SuppressLint("HandlerLeak")
    Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
            case CHANGETEXT:
                text.setText("JAVA");
                break;
            default:
                break;
            }
        }
    };
}

这样就不会报错。以上我们就是利用了Handler来异步处理UI更新操作。

我们来看看异步消息处理机制是怎样的?

1,Message是现成之间传递的消息,它可以携带少量信息,用于在不同线程之间交换数据
2,Handler顾名思义就是处理者的意思,发送和处理消息的,发送一般就是用sendmessage方法,最后传到handlemessage当中处理
3,MessageQueue 是消息队列的意思,用于存放handler发送的消息,这一部分消息一直存在消息队列中等待被处理,每个线程当中只会有一个MessageQueue。
4,Looper Looper是每个线程中的 MessageQueue的管家,调用 Looper的 loop()方法后,就会
进入到一个无限循环当中,然后每当发现 MessageQueue中存在一条消息,就会将它取
出, 并传递到 Handler的 handleMessage()方法中。 每个线程中也只会有一个 Looper对象。

下面是整个异步流程处理机制示意图:

这里写图片描述

下面我们来看下一个服务Service与Handler结合,周期性实时获取网速服务的例子:

public class Net_Service extends Service {

    private long total_data = TrafficStats.getTotalRxBytes();
    // private Handler mHandler;
    // 几秒刷新一次
    private final int count = 2;
    private Runnable mRunnable;

    @Override
    public void onCreate() {
        super.onCreate();
        /**
         * 定义线程周期性地获取网速
         */
        mRunnable = new Runnable(){
            @Override
            public void run() {
                // 定时器
                ExampleApplication.getHandle().postDelayed(mRunnable, count * 1000);
                Message msg = ExampleApplication.getHandle().obtainMessage();
                msg.what = 1;
                msg.arg1 = getNetSpeed();
                ExampleApplication.getHandle().sendMessage(msg);
            }
        };
        Log.i("NetService", "创建了服务");
    }

    /**
     * 核心方法,得到当前网速
     * 
     * @return
     */
    private int getNetSpeed() {
        long traffic_data = TrafficStats.getTotalRxBytes() - total_data;
        total_data = TrafficStats.getTotalRxBytes();
        return (int) traffic_data / count;
    }

    /**
     * 启动服务时就开始启动线程获取网速
     */
    @Override
    public void onStart(Intent intent, int startId) {
        Log.i("NetService", "开始了服务");
        ExampleApplication.getHandle().postDelayed(mRunnable, 0);
    };

    /**
     * 在服务结束时删除消息队列
     */
    @Override
    public void onDestroy() {
        Log.i("NetService", "停止了服务");
        ExampleApplication.getHandle().removeCallbacks(mRunnable);
        super.onDestroy();
    };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

}

ExampleApplication中代码:

public static Handler getHandle(){
        return mHandler;
    }


    public static void setmHandler(Handler mHandler) {
        ExampleApplication.mHandler = mHandler;
    }

在到Activity写一个Handler更新UI就好了,把那个Handler set到ExampleApplication的handler,拿到msg.arg1。

AsyncTask的基本介绍

不过为了更加方便我们在子线程中对 UI进行操作, Android还提供了另外一些好用的工
具,AsyncTask就是其中之一。借助 AsyncTask,即使你对异步消息处理机制完全不了解,
也可以十分简单地从子线程切换到主线程。当然,AsyncTask背后的实现原理也是基于异步
消息处理机制的,只是 Android帮我们做了很好的封装而已。

public class YiBuAncyTask extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.asyncactivity);
        new Test().execute();//启动这个异步任务
    }

    /**
     * 创建一个子类去继承AsyncTask,重写AsyncTask里面的方法 ,并指定三个泛型参数
     * (第一个参数表示)Params在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
     * (第二个参数)Progress在执行后台任务时,如果需要在界面显示当前进度, 则使用这里指定的泛型作为进度单位,指定进度单位的类型
     * (第三个参数)Result 指定返回结果的类型
     */
    class Test extends AsyncTask<Void, Integer, Boolean> {
        // Void表示在执行的时候不需要传入参数给后台任务
        // Integer表示用整型数据用来做进度显示单位
        // Boolean表示用布尔来反馈执行结果

        // 最常用的方法有以下方法

        /**
         * 这个方法是在子线程当中运行的,所有的耗时操作应当在这个方法当中运行 如果指定AsyncTask第三个泛型参数指定的是Void,
         * 就可以不返回
         * 这个方法当中不能进行UI操作如果需要更新 UI元素,比如说反馈当前任务的执行进度,可以调 用
         * publishProgress(Progress...)方法来完成。
         */
        @Override
        protected Boolean doInBackground(Void... params) {
            return true;
        }
        /**
         * 在执行后台任务之前进行一些初始化操作,比如显示一个进度条对话框等等
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        /**
         * 在调用了publishProgress方法之后 ,这个方法马上会被调用
         * 携带的这个参数就是后台任务中传递过来的,在这个方法中可以对UI进行操作
         * 利用参数中的数值就可以对界面元素进行相应的更新
         */
        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
        }

        /**
         * 当后台执行任务完毕,并返回结果时,这个方法会被调用,返回的数据会将结果传递到该参数当中,
         * 可以利用该参数来完成一些UI操作
         */
        @Override
        protected void onPostExecute(Boolean result) {
            super.onPostExecute(result);
        }
    }
}

下面我们来看个AsyncTask异步获取一个本地数据库题库的例子:

// 先读取数据库中的缓存数据量较多比较耗时使用AsyncTask
    private class QueryTask extends AsyncTask<Void, Void, ArrayList<CauseInfo>> {

        @Override
        protected ArrayList<CauseInfo> doInBackground(Void... params) {
            String CATEID = getIntent().getStringExtra("cateId");
            String chapter = getIntent().getStringExtra("chapter");
            list = DBManager.getInstance(OrderActivity.this).querys(AnswerColumns.TABLE_NAME, CATEID, chapter);
            Log.e("chapter", ""+chapter+"id"+CATEID);
            return list;
        }

        @Override
        protected void onPostExecute(ArrayList<CauseInfo> list) {
            if (list.size() == 0){
                subjectTop.setText("0/0");
                selectOne.setText("");
                selectTwo.setText("");
                selectThree.setText("");
                selectFour.setText("");
                selectFive.setText("");
                selectSix.setText("");
                return;
            }
            int lastSelect;
            if (chapter.equals("")) {
                lastSelect = ConfigPreferences.getInstance(OrderActivity.this).isLastSelectOrder(CATEID);
            } else {
                lastSelect = ConfigPreferences.getInstance(OrderActivity.this).isLastSelectOrder(chapters);
            }
            i = lastSelect - 1;
            CauseInfo myData = list.get(lastSelect - 1);
            title.setText(lastSelect + "." + myData.title);
            subjectTop.setText(lastSelect + "/" + list.size());
            if (myData.q_type == 1) {
                type.setText("题型:单选题");
                submit.setVisibility(View.GONE);
                selectOne.setText(myData.optionA);
                selectTwo.setText(myData.optionB);
                selectThree.setText(myData.optionC);
                selectFour.setText(myData.optionD);
                selectFive.setText(myData.optionE);
                selectSix.setText(myData.optionF);
                if (myData.optionE.equals("")) {
                    relFive.setVisibility(View.GONE);
                } else {
                    relFive.setVisibility(View.VISIBLE);
                }
                if (myData.optionF.equals("")) {
                    relSix.setVisibility(View.GONE);
                } else {
                    relSix.setVisibility(View.VISIBLE);
                }
            } else if (myData.q_type == 2) {
                submit.setVisibility(View.VISIBLE);
                selectOne.setText(myData.optionA);
                selectTwo.setText(myData.optionB);
                selectThree.setText(myData.optionC);
                selectFour.setText(myData.optionD);
                selectFive.setText(myData.optionE);
                selectSix.setText(myData.optionF);
                type.setText("题型:多选题");
                if (myData.optionE.equals("")) {
                    relFive.setVisibility(View.GONE);
                } else {
                    relFive.setVisibility(View.VISIBLE);
                }
                if (myData.optionF.equals("")) {
                    relSix.setVisibility(View.GONE);
                } else {
                    relSix.setVisibility(View.VISIBLE);
                }
            } else if (myData.q_type == 3) {
                submit.setVisibility(View.GONE);
                selectThree.setVisibility(View.GONE);
                selectFour.setVisibility(View.GONE);
                imageThree.setVisibility(View.GONE);
                imageFour.setVisibility(View.GONE);
                relFive.setVisibility(View.GONE);
                relSix.setVisibility(View.GONE);
                selectOne.setText("正确");
                selectTwo.setText("错误");
                type.setText("题型:判断题");
                if (myData.optionE.equals("")) {
                    relFive.setVisibility(View.GONE);
                } else {
                    relFive.setVisibility(View.VISIBLE);
                }
                if (myData.optionF.equals("")) {
                    relSix.setVisibility(View.GONE);
                } else {
                    relSix.setVisibility(View.VISIBLE);
                }
            }
            // 判断是否有图片
            Log.i("Imageurl", "" + myData.getImage());
            if (myData.getImage().equals("")) {
                iv_picture.setVisibility(View.GONE);
            } else {
                iv_picture.setVisibility(View.VISIBLE);
                String imageurl = myData.getImage();
                Bitmap bitmap = asyncbitmap.loadBitmap(iv_picture, imageurl, new ImageCallBack() {
                    @Override
                    public void imageLoad(ImageView imageView,Bitmap bitmap){
                        imageView.setImageBitmap(bitmap);
                    }
                });
                iv_picture.setImageBitmap(bitmap);
            }
        }
    }

以上是关于多线程异步机制Handler以及AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章

Android多线程的四种方式:Handler、AsyncTask、ThreadPoolExector、IntentService

Android线程管理之AsyncTask异步任务

201709013工作日记--Android异步通信AsyncTask

Android开发学习之路--异步消息Handler,Message,Looper和AsyncTask之初体验

Android开发学习之路--异步消息Handler,Message,Looper和AsyncTask之初体验

Android AsyncTask解析