多线程异步机制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);
            }
        }
    }
时间: 2024-10-10 22:00:10

多线程异步机制Handler以及AsyncTask的相关文章

Android多线程编程之Handler篇(消息机制)

Android多线程编程之Handler篇(消息机制) Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑. MessageQueue 消息队列,以队列的形式(实为单链表结构)对外提供插入和删除的工作, Looper 以无限循环的形式不断获取MessageQueue中的消息,有则处理,无则等待. ThreadLocal ThreadLocal可以在不同的线程互不干扰的存储并提供数据,通过ThreadLocal可以很

Android多线程分析之五:使用AsyncTask异步下载图像

Android多线程分析之五:使用AsyncTask异步下载图像 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在本系列文章的第一篇<Android多线程分析之中的一个:使用Thread异步下载图像>中.曾演示了怎样使用 Thread 来完毕异步任务. Android 为了简化在 UI 线程中完毕异步任务(毕竟 UI 线程是 app 最重要的线程).实现了一个名为 AysncTask 的模板类.使用 AysncTask 能够在异步任务进行的同

深入探讨Android异步精髓Handler

深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–onMeasure源码详尽分析 自定义View系列教程03–onLayout源码详尽分析 自定义View系列教程04–Dra

JavaScript单线程和异步机制

随着对JavaScript学习的深入和实践经验的积累,一些原理和底层的东西也开始逐渐了解.早先也看过一些关于js单线程和事件循环的文章,不过当时看的似懂非懂,只留了一个大概的印象:浏览器中的js程序时是单线程的.嗯,就这么点印象.当时也有些疑问:既然是单线程的,那异步调用是怎么实现的?计时器是靠谁来计时的,这单线程总不能一边执行程序一边计时吧?那些耗时的I/O操作为啥没把线程阻塞,不是说好的单线程么?相信很多不了解JavaScript单线程的同学也有过类似的疑问. 今天看了不少相关的资料,就详细

Android消息处理机制(Handler 与Message)---01

一.handler的使用场景为么会有handler?(部分内容图片摘自http://www.runoob.com/w3cnote/android-tutorial-handler-message.html) 如有侵犯,请告知. 二.handler的消息处理机制 在Android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长时间的任务后做出相应的通知. UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的Message

异步机制学习

在Android中异步主要有Thread和Handler. Thread是创建线程来实现异步,Handler则是在looper中实现异步. 1.线程异步: 创建线程new Thread():调用Thread.start()启动线程.在子线程中执行Thread.run()方法.也可以通过new Thread(Runnable runnable)来实例化后在子线程中执行Runnable.run()方法,此时不能重写Thread中的run()方法,否则还是会执行Thread.run()方法. 2.ha

Cocos2d-x 3.0多线程异步资源加载

Cocos2d-x从2.x版本到上周刚刚才发布的Cocos2d-x 3.0 Final版,其引擎驱动核心依旧是一个单线程的"死循环",一旦某一帧遇到了"大活儿",比如Size很大的纹理资源加载或网络IO或大量计算,画面将 不可避免出现卡顿以及响应迟缓的现象.从古老的Win32 GUI编程那时起,Guru们就告诉我们:别阻塞主线程(UI线程),让Worker线程去做那些"大活儿"吧. 手机游戏,即便是休闲类的小游戏,往往也涉及大量纹理资源.音视频资

解析Android的 消息传递机制Handler

1. 什么是Handler: Handler 网络释义"操纵者,管理者的"意思,在Android里面用于管理多线程对UI的操作: 2. 为什么会出现Handler: 在Android的设计机制里面,只允许主线程(一个程序第一次启动时所移动的线程,因为此线程主要是完成对UI相关事件的处理,所以也称UI线程) 对UI进行修改等操作,这是一种规则的简化,之所以这样简化是因为Android的UI操作时线程不安全的,为了避免多个线程同时操作UI造成线程安全 问题,才出现了这个简化的规则. 由此以

Android多线程之图解Handler Looper MessageQueue Message

Android中的多线程可以有多种实现方式,前面我们已经讲过了封装程度较高异步任务(AnsyncTask),这一节我们来看看较为灵活的方式:Handler Looper MessageQueue Message. Message:用于线程之间传递信息,发送的消息放入目标线程的MessageQueue中. MessageQueue:用于简化线程之间的消息传递,MessageQueue接受发送端的Message,并作为消息处理端的输入源.每个线程只有一个实例. Handler:用于处理Message