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

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


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

其中有如下代码,。

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

		if (thread == null) {

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

有如下代码:

	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();

				}
			}

......这只是个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;

	@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();
	// }

}

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

这里是Log.关于使用HandlerThread获取数据,并实现sqlite分页。


一段时间后导致OOM.

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


因此,我换了种方式写。

代码如下:

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);
	}
}


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

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


程序中我新建了一个类继承自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);
	}
}



异常截图截图如下:


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

原因:因为我在创建的时候启动了线程(他会执行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);

会调用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只能有一个。
具体为什么会被重复执行,以及如何解决,下次来把。

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

关于使用HandlerThread获取数据,并实现sqlite分页。,布布扣,bubuko.com

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

上一篇:ORACLE字符型函数(二)


下一篇:大龙在win7下安装SQL Server 2008 r2的悲惨经历