关于使用HandlerThread获取数据,并实现sqlite分页。

来源:互联网 发布:淘宝优惠券群怎么加入 编辑:程序博客网 时间:2024/06/14 10:15

在用多线程获取数据的时候总是用Thread,要去启动一个子线程就去new 一个thread,久而久之,要做的事情多了,代码看起来就零散而臃肿,效率估计也不好,而且维护和扩展起来也不一定容易,估计大多数人也会这么写。


比如我之前一篇文章讲的sqlite分页查询。

其中有如下代码,。

这是在刚进入该画面的时候,启动线程,去数据库那数据,然后发送消息给mHandler刷新UI。

if (thread == null) {thread = new MyThread();thread.start();}
 
当用户不断的滑动ListView以获取更多数据时,会重新开启一个线程,再去拿数据,然后发送消息刷新UI。

有如下代码:

cityListView.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (view.getLastVisiblePosition() == view.getCount() - 1&& scrollState == OnScrollListener.SCROLL_STATE_IDLE) {if (thread != null && !thread.isInterrupted()) {thread.interrupt();thread = null;}currentPage++;cityListView.setSelection(view.getLastVisiblePosition());// 设置显示位置,这句只是让Listview停留在最后末尾的显示而已,加不加影响不大thread = new MyThread();thread.start();}}

......这只是个DEMO,所以不是很在意这些小问题。但是实际项目中,这样写并不是一个好的方式。

android提供了,HandlerThread,IntentService,AsyncTask等这些使用起来在某些情况下或许比较好一点。


接下来,我使用HandlerThread来修改之前的demo;

地址:http://blog.csdn.net/xxm282828/article/details/21437727


贴个代码先、

package com.example.sqlitepagetest;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Handler.Callback;import android.os.HandlerThread;import android.os.Message;import android.view.KeyEvent;import android.view.Menu;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ListView;/** * <p> * 使用HandlerThread 刷新数据 * </p> * 下午12:30:18 *  * @auther dalvikCoder */public class Activity4 extends Activity {private ListView cityListView;private List<cls_city> cityList;private CityAdapter cityAdapter;private HandlerThread handlerThread = null;/** 为线程设立标志位 **/private boolean isThreadRunning = false;/** * 每页有数据条数 这个数量可以根据需要更改,而不需在程序中更改具体数值 * **/private int perPageItemNum = 200;/** 当前是第几页 0表示第一页 **/private int currentPage = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity4);setUpView();}private void setUpView() {cityList = new ArrayList<cls_city>();try {Common.loadCityDatabase(this);} catch (Exception e) {e.printStackTrace();}Common.dbh = new DatabaseHelper(this, "city");cityListView = (ListView) findViewById(R.id.citylistview);cityAdapter = new CityAdapter(this, cityList);cityListView.setAdapter(cityAdapter);cityListView.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (view.getLastVisiblePosition() == view.getCount() - 1&& scrollState == OnScrollListener.SCROLL_STATE_IDLE) {currentPage++;cityListView.setSelection(view.getLastVisiblePosition());// 设置显示位置,这句只是让Listview停留在最后末尾的显示而已,加不加影响不大isThreadRunning = true;mHandler.post(runable);}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {}});handlerThread = new HandlerThread("myThread");handlerThread.start();mHandler = new Handler(handlerThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {@SuppressWarnings("unchecked")List<cls_city> dataList = (List<cls_city>) msg.obj;if (!dataList.isEmpty()) {cityAdapter.refresh(dataList);}isThreadRunning = false;super.handleMessage(msg);}};isThreadRunning = true;mHandler.post(runable);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}/** 分页获取数据 **/Runnable runable = new Runnable() {@Overridepublic void run() {while (isThreadRunning) {int num[] = new int[2];num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50num[1] = perPageItemNum;List<cls_city> dataList = cls_city.getCityList(Common.dbh, num);Message msg = new Message();msg.what = 1;msg.obj = dataList;mHandler.sendMessage(msg);}}};/** to refresh UI **/private Handler mHandler = null;/** * <p> * 这里我需要的效果是类似于QQ当我点击返回键后他不会退出,再次进来后他还是原来的状态 查阅资料google,sdk,这里涉及一个方法。 * </p> */@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// 过滤按键动作if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {moveTaskToBack(true);}return super.onKeyDown(keyCode, event);}@Overrideprotected void onResume() {isThreadRunning = true;super.onResume();}@Overrideprotected void onPause() {isThreadRunning = false;super.onPause();}@Overrideprotected void onStop() {isThreadRunning = false;super.onStop();}@Overrideprotected void onDestroy() {isThreadRunning = false;mHandler.removeCallbacks(runable);super.onDestroy();}// @Override// public void onBackPressed() {//// moveTaskToBack(true);// super.onBackPressed();// }}

问题:使用上面代码测试的时候发现。线程会不断的跑,而且消息发送出去后并没有执行到mHandler的handleMessage(Message msg)方法中区。

这里是Log.


一段时间后导致OOM.


因此,我换了种方式写。

代码如下:

package com.example.sqlitepagetest;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Handler.Callback;import android.os.HandlerThread;import android.os.Message;import android.util.Log;import android.view.KeyEvent;import android.view.Menu;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ListView;/** * <p> * 使用HandlerThread 刷新数据 * </p> * 下午12:30:18 *  * @auther dalvikCoder */public class Activity5 extends Activity {private ListView cityListView;private List<cls_city> cityList;private CityAdapter cityAdapter;private mHandlerThread handlerThread = null;/** * 每页有数据条数 这个数量可以根据需要更改,而不需在程序中更改具体数值 * **/private int perPageItemNum = 50;/** 当前是第几页 0表示第一页 **/private int currentPage = 0;/** to refresh UI **/private Handler mHandler = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity4);setUpView();}private void setUpView() {cityList = new ArrayList<cls_city>();try {Common.loadCityDatabase(this);} catch (Exception e) {e.printStackTrace();}Common.dbh = new DatabaseHelper(this, "city");cityListView = (ListView) findViewById(R.id.citylistview);cityAdapter = new CityAdapter(this, cityList);cityListView.setAdapter(cityAdapter);cityListView.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (view.getLastVisiblePosition() == view.getCount() - 1&& scrollState == OnScrollListener.SCROLL_STATE_IDLE) {Log.e("------------>", "翻页");currentPage++;cityListView.setSelection(view.getLastVisiblePosition());// 设置显示位置,这句只是让Listview停留在最后末尾的显示而已,加不加影响不大mHandler.sendEmptyMessage(2);}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {}});handlerThread = new mHandlerThread("mytest");handlerThread.start();mHandler = new Handler(handlerThread.getLooper(), handlerThread);mHandler.sendEmptyMessage(2);Log.e("--------oncreate------>", Thread.currentThread().getId() + "");}class mHandlerThread extends HandlerThread implements Callback {public mHandlerThread(String name) {super(name);Log.e("--------mHandlerThread------>", Thread.currentThread().getId() + "");}List<cls_city> dataList = null;@Overridepublic boolean handleMessage(Message msg) {Log.e("----------------->", "执行到handleMessage方法");int num[] = new int[2];num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50num[1] = perPageItemNum;dataList = cls_city.getCityList(Common.dbh, num);Log.e("------handleMessage threadid----->", Thread.currentThread().getId() + "");// 更新UI必须在主线程中。runOnUiThread(new Runnable() {public void run() {Log.e("--------runOnUiThread------>", Thread.currentThread().getId() + "");if (!dataList.isEmpty()) {cityAdapter.refresh(dataList);}}});return false;}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}/** * <p> * 这里我需要的效果是类似于QQ当我点击返回键后他不会退出,再次进来后他还是原来的状态 查阅资料google,sdk,这里涉及一个方法。 * </p> */@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// 过滤按键动作if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {moveTaskToBack(true);}return super.onKeyDown(keyCode, event);}}


打印出来的Log截图如下,程序一切正常:



程序中我新建了一个类继承自HandlerThread类,实现Handler类中的回调接口

Open Declaration android.os.Handler.Callback
实现其handlerMessage方法。然后设计到更新UI部分调用Activity的runOnUiThread方法,即代码部分“”:

// 更新UI必须在主线程中。runOnUiThread(new Runnable() {public void run() {Log.e("--------runOnUiThread------>", Thread.currentThread().getId() + "");if (!dataList.isEmpty()) {cityAdapter.refresh(dataList);}}});

从打印出来的Log看的出,handlerThread新开了一个线程、


问题描述:

另外一个思路,猜测可以这样实现:

public class HandlerThread extends Thread {
HandlerThread是一个线程类,因此他有run方法。我可以同时在我的类mHandlerThread中同时实现run和handleMessage方法。将从数据库拿数据的代码放到run中然后在handleMessage中处理消息更新UI。

但是结果会抛出异常。

代码如下:

package com.example.sqlitepagetest;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Handler.Callback;import android.os.HandlerThread;import android.os.Message;import android.util.Log;import android.view.KeyEvent;import android.view.Menu;import android.widget.AbsListView;import android.widget.AbsListView.OnScrollListener;import android.widget.ListView;/** * <p> * 使用HandlerThread 刷新数据 * </p> * 下午12:30:18 *  * @auther dalvikCoder */public class Activity6 extends Activity {private ListView cityListView;private List<cls_city> cityList;private CityAdapter cityAdapter;private mHandlerThread handlerThread = null;/** * 每页有数据条数 这个数量可以根据需要更改,而不需在程序中更改具体数值 * **/private int perPageItemNum = 50;/** 当前是第几页 0表示第一页 **/private int currentPage = 0;/** to refresh UI **/private Handler mHandler = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity4);setUpView();}private void setUpView() {cityList = new ArrayList<cls_city>();try {Common.loadCityDatabase(this);} catch (Exception e) {e.printStackTrace();}Common.dbh = new DatabaseHelper(this, "city");cityListView = (ListView) findViewById(R.id.citylistview);cityAdapter = new CityAdapter(this, cityList);cityListView.setAdapter(cityAdapter);cityListView.setOnScrollListener(new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {if (view.getLastVisiblePosition() == view.getCount() - 1&& scrollState == OnScrollListener.SCROLL_STATE_IDLE) {Log.e("------------>", "翻页");currentPage++;cityListView.setSelection(view.getLastVisiblePosition());// 设置显示位置,这句只是让Listview停留在最后末尾的显示而已,加不加影响不大mHandler.post(handlerThread);}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {}});handlerThread = new mHandlerThread("mytest");handlerThread.start();mHandler = new Handler(handlerThread.getLooper(), handlerThread);mHandler.post(handlerThread);Log.e("--------oncreate------>", Thread.currentThread().getId() + "");}class mHandlerThread extends HandlerThread implements Callback {public mHandlerThread(String name) {super(name);Log.e("--------mHandlerThread------>", Thread.currentThread().getId() + "");}@Overridepublic void run() {super.run();int num[] = new int[2];num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50num[1] = perPageItemNum;List<cls_city> dataList = cls_city.getCityList(Common.dbh, num);Message msg = new Message();msg.what = 2;msg.obj = dataList;mHandler.obtainMessage();mHandler.sendMessage(msg);}@Overridepublic boolean handleMessage(Message msg) {Log.e("----------------->", "执行到handleMessage方法");Log.e("------handleMessage threadid----->", Thread.currentThread().getId() + "");final List<cls_city> obj = (List<cls_city>) msg.obj;// 更新UI必须在主线程中。runOnUiThread(new Runnable() {public void run() {Log.e("--------runOnUiThread------>", Thread.currentThread().getId() + "");if (!obj.isEmpty()) {cityAdapter.refresh(obj);}}});return false;}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}/** * <p> * 这里我需要的效果是类似于QQ当我点击返回键后他不会退出,再次进来后他还是原来的状态 查阅资料google,sdk,这里涉及一个方法。 * </p> */@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// 过滤按键动作if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {moveTaskToBack(true);}return super.onKeyDown(keyCode, event);}}



异常截图截图如下:



原因:因为我在创建的时候启动了线程(他会执行run方法),而之前的代码是这样的,看上面

@Overridepublic void run() {Log.e("--------run ------>", Thread.currentThread().getId() + "");super.run();int num[] = new int[2];num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50num[1] = perPageItemNum;List<cls_city> dataList = cls_city.getCityList(Common.dbh, num);

会调用super.run() ;

查看源码 :

    public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }
可以看出,HandlerThread有自己的Looper,super.run()被重复执行了,因此异常说每个线程的Looper只能有一个。
具体为什么会被重复执行,以及如何解决,下次来把。

至于这个思路如何做好,有时间再继续研究。代码就在上面,有兴趣可以拿下来看下。欢迎各位批评指正。

1 0