您的位置:首页 > 产品设计 > UI/UE

Android入门:深入学习理解 Handler HandlerThread AsyncQueryHandler 三者的关系 收藏

2016-04-22 17:12 561 查看



Android入门:深入学习理解 Handler HandlerThread AsyncQueryHandler 三者的关系 收藏 

首先创建工程 ThreadDemo 创建Activity
一、Handler
Handler在android里负责发送和处理消息。它的主要用途有:
  1)按计划发送消息或执行某个Runnanble(使用POST方法);

  2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程)

   默认情况下,Handler接受的是当前线程下的消息循环实例(使用Handler(Looper looper)、Handler(Looper  looper, Handler.Callback callback) 可以指定线程),同时一个消息队列可以被当前线程中的多个对象进行分发、处理(在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个 Handler来处理)。在实例化Handler的时候,Looper可以是任意线程的,只要有Handler的指针,任何线程也都可以 sendMessage。Handler对于Message的处理不是并发的。一个Looper
只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。

view plaincopy to clipboardprint?

01.package com.debby.threaddemo;   

02.import android.app.Activity;   

03.import android.content.AsyncQueryHandler;   

04.import android.os.Bundle;   

05.import android.os.Handler;   

06.import android.os.HandlerThread;   

07.import android.util.Log;   

08.public class ThreadDemo extends Activity {   

09.    private static final String TAG = "bb";     

10.    private int count = 0;     

11.    private Handler mHandler ;     

12.         

13.    private Runnable mRunnable = new Runnable() {     

14.             

15.        public void run() {     

16.            //为了方便 查看,我们用Log打印出来     

17.            Log.e(TAG, Thread.currentThread().getId() + " " +count);     

18.            count++;     

19.            setTitle("" +count);     

20.            //每2秒执行一次     

21.            mHandler.postDelayed(mRunnable, 2000);     

22.        }     

23.             

24.    };     

25.    @Override     

26.    public void onCreate(Bundle savedInstanceState) {     

27.        Log.e(TAG, "Main id    "+Thread.currentThread().getId() + " " +count);     

28.        super.onCreate(savedInstanceState);     

29.        setContentView(R.layout.main);      

30.        //通过Handler启动线程     

31.        mHandler =  new Handler();   

32.        mHandler.post(mRunnable);     

33.    }     

34.    @Override     

35.    protected void onDestroy() {     

36.        //将线程与当前handler解除绑定   

37.        //mHandler.removeCallbacks(mRunnable);     

38.        super.onDestroy();     

39.    }     

40.}  

package com.debby.threaddemo;

import android.app.Activity;

import android.content.AsyncQueryHandler;

import android.os.Bundle;

import android.os.Handler;

import android.os.HandlerThread;

import android.util.Log;

public class ThreadDemo extends Activity {

 private static final String TAG = "bb";  

    private int count = 0;  

    private Handler mHandler ;  

      

    private Runnable mRunnable = new Runnable() {  

          

        public void run() {  

            //为了方便 查看,我们用Log打印出来  

            Log.e(TAG, Thread.currentThread().getId() + " " +count);  

            count++;  

            setTitle("" +count);  

            //每2秒执行一次  

            mHandler.postDelayed(mRunnable, 2000);  

        }  

          

    };  

    @Override  

    public void onCreate(Bundle savedInstanceState) {  

     Log.e(TAG, "Main id    "+Thread.currentThread().getId() + " " +count);  

        super.onCreate(savedInstanceState);  

        setContentView(R.layout.main);   

        //通过Handler启动线程  

        mHandler =  new Handler();

        mHandler.post(mRunnable);  

    }  

    @Override  

    protected void onDestroy() {  

        //将线程与当前handler解除绑定

        //mHandler.removeCallbacks(mRunnable);  

        super.onDestroy();  

    }  

}
 

这里直接通过Handler启动一个线程
执行测试后可以发现 setTitle("" +count);   该行代码可以执行 并可以不断改变UI

由于android是单线程模型 所以可见个线程就是运行在UI主线程当中的 通过两次打印的Log也可以看出是同一个线程

也就是说mHandler.post(mRunnable);  执行了run()方法 并没有执行Thread的start()方法开启一个新的线程
所以这种方式不适合比较耗时的操作 会堵塞主线程 UI message队列
另外 mHandler.removeCallbacks(mRunnable);   该行代码如果注释掉会发现即使退出该Acitivity也会继续执行线程的run()
方法 所以这里需要注意

二、 HandlerThread
HandlerThread继承于Thread,所以它本质就是个Thread。与普通Thread的差别就在于,它有个Looper成员变量。这个Looper其实就是对消息队列以及队列处理逻辑的封装,简单说就是 消息队列+消息循环。
当我们需要一个工作者线程,而不是把它当作一次性消耗品,用过即废弃的话,就可以使用它。
view plaincopy to clipboardprint?

01.public class ThreadDemo extends Activity {   

02.    private static final String TAG = "bb";     

03.    private int count = 0;     

04.    private Handler mHandler ;     

05.         

06.    private Runnable mRunnable = new Runnable() {     

07.             

08.        public void run() {     

09.            //为了方便 查看,我们用Log打印出来     

10.            Log.e(TAG, Thread.currentThread().getId() + " " +count);     

11.            count++;     

12.//            setTitle("" +count);     

13.            //每2秒执行一次     

14.            mHandler.postDelayed(mRunnable, 2000);     

15.        }     

16.             

17.    };     

18.    @Override     

19.    public void onCreate(Bundle savedInstanceState) {     

20.        Log.e(TAG, "Main id    "+Thread.currentThread().getId() + " " +count);     

21.        super.onCreate(savedInstanceState);     

22.        setContentView(R.layout.main);      

23.        //通过Handler启动线程     

24.        HandlerThread handlerThread = new HandlerThread("threadone");   

25.        handlerThread.start();   

26.        mHandler =  new Handler(handlerThread.getLooper());   

27.        mHandler.post(mRunnable);     

28.           

29.           

30.    }     

31.    @Override     

32.    protected void onDestroy() {     

33.        //将线程与当前handler解除   

34.        mHandler.removeCallbacks(mRunnable);     

35.        super.onDestroy();     

36.    }     

37.}  

public class ThreadDemo extends Activity {

 private static final String TAG = "bb";  

    private int count = 0;  

    private Handler mHandler ;  

      

    private Runnable mRunnable = new Runnable() {  

          

        public void run() {  

            //为了方便 查看,我们用Log打印出来  

            Log.e(TAG, Thread.currentThread().getId() + " " +count);  

            count++;  

//            setTitle("" +count);  

            //每2秒执行一次  

            mHandler.postDelayed(mRunnable, 2000);  

        }  

          

    };  

    @Override  

    public void onCreate(Bundle savedInstanceState) {  

     Log.e(TAG, "Main id    "+Thread.currentThread().getId() + " " +count);  

        super.onCreate(savedInstanceState);  

        setContentView(R.layout.main);   

        //通过Handler启动线程  

        HandlerThread handlerThread = new HandlerThread("threadone");

        handlerThread.start();

        mHandler =  new Handler(handlerThread.getLooper());

        mHandler.post(mRunnable);  

        

        

    }  

    @Override  

    protected void onDestroy() {  

        //将线程与当前handler解除

        mHandler.removeCallbacks(mRunnable);  

        super.onDestroy();  

    }  

}
这里通过HandlerThread启动一个新线程
注这里需要handlerThread.start();先启动线程 才能 handlerThread.getLooper() 获取当前线程的Looper
通过HandlerThread的run方法可以发现

view plaincopy to clipboardprint?

01.public void run() {   

02.        mTid = Process.myTid();   

03.        Looper.prepare();   

04.        synchronized (this) {   

05.            mLooper = Looper.myLooper();   

06.            Process.setThreadPriority(mPriority);   

07.            notifyAll();   

08.        }   

09.        onLooperPrepared();   

10.        Looper.loop();   

11.        mTid = -1;   

12.    }  

public void run() {

        mTid = Process.myTid();

        Looper.prepare();

        synchronized (this) {

            mLooper = Looper.myLooper();

            Process.setThreadPriority(mPriority);

            notifyAll();

        }

        onLooperPrepared();

        Looper.loop();

        mTid = -1;

    }
这里调用了Looper.prepare(); 初始化了Looper
通过执行可以发现 setTitle("" +count);  在这里调用会出现异常
还有通过打印的日志
 
都可以发现 这里启动的是一个新线程 虽然不能直接操作UI 但可以通过Message发送消息来进行操作
这样可以处理一些比较耗时操作
三、AsyncQueryHandler
这个类继承了Handler 实现了 ContentProvider处理相关的一些操作的异步方式
与其说这个类提供给我们一个处理ContentProvider的方法 我觉得这更给我们提供了一个处理异步的方案
若我们不用AsyncQueryHandler,直接在UI 线程调用ContentResolve去操作数据库比如查询,若你的数据库的数据很少还好,若很多,就会出现ANR了。一般解决ANR,就是开 thread去解决。让UI线程知道何时查询完毕,可以更新UI将查询的结果表现出来
首先分析一下 AsyncQueryHandler 这个类
他的基本策略如下:

  1. 当你实例化一个AsyncQueryHandler类时(包括其子类...),它会单件构造一个线程WorkerHandler,这个线程里面会构建一个消息循环。

  2. 获得该消息循环的指针,用它做参数实例化另一个Handler类,该类为内部类。至此,就有了两个线程,各自有一个Handler来处理消息。

  3. 当调用onXXX的时候,在XXX函数内部会将请求封装成一个内部的参数类,将其作为消息的参数,将此消息发送至另一个线程。

  4. 在该线程的Handler中,接受该消息,并分析传入的参数,用初始化时传入的ContentResolver进行XXX操作,并返回Cursor或其他返回值。

  5. 构造一个消息,将上述返回值以及其他相关内容绑定在该消息上,发送回主线程。

  6. 主线程默认的AsyncQueryHandler类的handleMessage方法(可自定义,但由于都是内部类,基本没有意义...)会分析该消息,并转发给对应的onXXXComplete方法。

  7. 用户重写的onXXXComplete方法开始工作。
通过上面的HandlerThread的用法可以看到我们启动新线程进行操作的代码是很冗余很繁琐的 把更多对Handler的操作暴露出来了
这样是很不利于维护和复用的(虽然有时候没有必要 这不显得比较NB嘛)
那通过这个类我们只需要实例化的时候传入ContentResolver 并实现自己的回调方法onXXXComplete 最后调用你需要的操作就可以
确实代码简洁了 想知道怎么回事去反编译android的代码去吧
那我觉得如果有什么非ContentProvider操作,却需要异步多线程执行的话,模拟一套,是个不错的选择
这里我做了个demo

AsyncQueryHandler

  官方解释是一个异步帮助类(A helper class to help make handling asynchronous 
ContentResolver
 queries
easier.
) 。这个类的主要作用就是异步对DB数据库进行操作,加快其数据处理的速度(这个非常重要,特别是大容量的数据处理时,例如几千联系人的数据读取,按正常的处理速度会非常的慢,使用AsyncQueryHandler,这就会大大的加快速度,增加用户的良好体验)。

[b]AsyncQueryHandler重要的方法:[/b]

final voidstartDelete(int token, Object cookie, Uri uri, String selection, String[] selectionArgs)This method begins an asynchronous delete.
final voidstartInsert(int token, Object cookie, Uri uri, ContentValues initialValues)This method begins an asynchronous insert.
voidstartQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy)This method begins an asynchronous query.
final voidstartUpdate(int token, Object cookie, Uri uri, ContentValues values, String selection, String[] selectionArgs)This method begins an asynchronous update.
显然,从方法的名字,我们可以看出,这是使用异步方式对DB数据库进行基本的增,删,改,查。

voidonDeleteComplete(int token, Object cookie, int result)Called when an asynchronous delete is completed.
voidonInsertComplete(int token, Object cookie, Uri uri)Called when an asynchronous insert is completed.
voidonQueryComplete(int token, Object cookie, Cursor cursor)Called when an asynchronous query is completed.
voidonUpdateComplete(int token, Object cookie, int result)Called when an asynchronous update is completed.
这几个方法,分别对应于上面四个异步操作时的回调方法。

AsyncQueryHandler的一个应用:读取电话联系人的数据信息

[java] view
plain copy

public class TestAsyncQueryHandler extends Activity {  

      

    private static final String NAME = "name", NUMBER = "number", SORT_KEY = "sort_key";  

      

    private List<ContentValues> listData;  

    private AsyncQueryHandler asyncQuery;    

      

    private ListView personList;  

    private BaseAdapter adapter;    

    

    @Override    

    public void onCreate(Bundle savedInstanceState) {    

        super.onCreate(savedInstanceState);    

          

        setContentView(R.layout.main);    

          

        personList = (ListView) findViewById(R.id.list_view);        

        asyncQuery = new MyAsyncQueryHandler(getContentResolver());  

        //异步读取联系人的信息  

        asyncQueryContact();         

    }    

    

  

    private void asyncQueryContact() {  

        // TODO Auto-generated method stub  

        Uri uri = Uri.parse("content://com.android.contacts/data/phones");    

        String[] projection = { "_id", "display_name", "data1", "sort_key" };    

        asyncQuery.startQuery(0, null, uri, projection, null, null,"sort_key COLLATE LOCALIZED asc");  

    }  

  

  

    private class MyAsyncQueryHandler extends AsyncQueryHandler {    

    

        public MyAsyncQueryHandler(ContentResolver cr) {    

            super(cr);    

    

        }    

    

        @Override    

        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {    

            if (cursor != null && cursor.getCount() > 0) {    

                listData = new ArrayList<ContentValues>();    

                //cursor.moveToFirst();    

                for (int i = 0; i < cursor.getCount(); i++) {  

                    ContentValues cv = new ContentValues();    

                    cursor.moveToPosition(i);    

                    String name = cursor.getString(1);    

                    String number = cursor.getString(2);    

                    String sortKey = cursor.getString(3);  

                    if (number.startsWith("+86")) {    

                        cv.put(NAME, name);    

                        //process (+86)  

                        cv.put(NUMBER, number.substring(3));    

                        cv.put(SORT_KEY, sortKey);    

                    } else {    

                        cv.put(NAME, name);    

                        cv.put(NUMBER, number);    

                        cv.put(SORT_KEY, sortKey);    

                    }    

                    listData.add(cv);    

                }    

                if (listData.size() > 0) {    

                    setAdapter(listData);    

                }    

                cursor.close();  

            }    

        }    

    

    }    

    

    private void setAdapter(List<ContentValues> listData) {  

        adapter = new ListAdapter(this, listData);  

        personList.setAdapter(adapter);    

    

    }  

      

      

    private class ListAdapter extends BaseAdapter {  

          

         private LayoutInflater inflater;    

         private List<ContentValues> list;  

          

        public ListAdapter(Context context, List<ContentValues> list) {  

            this.inflater = LayoutInflater.from(context);  

            this.list = list;             

        }  

          

        @Override  

        public int getCount() {  

            return list.size();  

        }  

  

        @Override  

        public Object getItem(int position) {  

            return list.get(position);  

        }  

  

        @Override  

        public long getItemId(int position) {  

            return position;  

        }  

          

        @Override  

        public View getView(int position, View convertView, ViewGroup parent) {  

            ViewHolder holder;  

            if (convertView == null) {    

                convertView = inflater.inflate(R.layout.list_item, null);  

                holder = new ViewHolder();    

                holder.name = (TextView) convertView.findViewById(R.id.name);    

                holder.number = (TextView) convertView.findViewById(R.id.number);    

                convertView.setTag(holder);    

            } else {    

                holder = (ViewHolder) convertView.getTag();    

            }    

              

            ContentValues cv = list.get(position);    

            holder.name.setText(cv.getAsString(NAME));  

            holder.number.setText(cv.getAsString(NUMBER));  

  

            return convertView;    

        }  

          

        private class ViewHolder {   

            TextView name;    

            TextView number;  

        }  

          

    }  

       

}    

  

  这个例子,只是使用了AsyncQueryHandler类中的异步查询方法,其它的方法,也是类似,大家可以自己尝试。

源代码下载地址:

           http://download.csdn.net/detail/hfreeman2011/5040647

参考资料:

1.Android异步的几种方式

http://ericchan2012.iteye.com/blog/1673929

2.官方文档

http://developer.android.com/reference/android/content/AsyncQueryHandler.html

3.android快速滑动列表 首字母提示

http://download.csdn.net/detail/j1582830/4010012
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: