在用多线程获取数据的时候总是用Thread,要去启动一个子线程就去new 一个thread,久而久之,要做的事情多了,代码看起来就零散而臃肿,效率估计也不好,而且维护和扩展起来也不一定容易,估计大多数人也会这么写。
比如我之前一篇文章讲的sqlite分页查询。
其中有如下代码,。
这是在刚进入该画面的时候,启动线程,去数据库那数据,然后发送消息给mHandler刷新UI。
if (thread == null) {
thread = new MyThread();
thread.start();
} 有如下代码:
cityListView.setOnScrollListener(new OnScrollListener() {
@Override
public 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();
}
}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;
@Override
protected 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() {
@Override
public 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);
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
});
handlerThread = new HandlerThread("myThread");
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper()) {
@Override
public 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);
}
@Override
public 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() {
@Override
public void run() {
while (isThreadRunning) {
int num[] = new int[2];
num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50
num[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>
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 过滤按键动作
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onResume() {
isThreadRunning = true;
super.onResume();
}
@Override
protected void onPause() {
isThreadRunning = false;
super.onPause();
}
@Override
protected void onStop() {
isThreadRunning = false;
super.onStop();
}
@Override
protected void onDestroy() {
isThreadRunning = false;
mHandler.removeCallbacks(runable);
super.onDestroy();
}
// @Override
// public void onBackPressed() {
//
// moveTaskToBack(true);
// super.onBackPressed();
// }
}
这里是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;
@Override
protected 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() {
@Override
public 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);
}
}
@Override
public 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;
@Override
public boolean handleMessage(Message msg) {
Log.e("----------------->", "执行到handleMessage方法");
int num[] = new int[2];
num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50
num[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;
}
}
@Override
public 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>
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 过滤按键动作
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
}
return super.onKeyDown(keyCode, event);
}
}
程序中我新建了一个类继承自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;
@Override
protected 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() {
@Override
public 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);
}
}
@Override
public 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() + "");
}
@Override
public void run() {
super.run();
int num[] = new int[2];
num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50
num[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);
}
@Override
public 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;
}
}
@Override
public 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>
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// 过滤按键动作
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
moveTaskToBack(true);
}
return super.onKeyDown(keyCode, event);
}
}异常截图截图如下:
原因:因为我在创建的时候启动了线程(他会执行run方法),而之前的代码是这样的,看上面
@Override
public void run() {
Log.e("--------run ------>", Thread.currentThread().getId() + "");
super.run();
int num[] = new int[2];
num[0] = currentPage * perPageItemNum;// 0*50 1*50 2*50
num[1] = perPageItemNum;
List<cls_city> dataList = cls_city.getCityList(Common.dbh, num);
查看源码 :
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只能有一个。至于这个思路如何做好,有时间再继续研究。代码就在上面,有兴趣可以拿下来看下。欢迎各位批评指正。
关于使用HandlerThread获取数据,并实现sqlite分页。,布布扣,bubuko.com
关于使用HandlerThread获取数据,并实现sqlite分页。
原文:http://blog.csdn.net/xxm282828/article/details/21813883