Android Handler与数据库的实现方法 (android handler 数据库)
在Android开发中,数据库操作和UI更新是非常常见的需求,如何在不阻塞UI线程的情况下进行数据库操作,然后将结果更新到UI上,成为了一项重要的技术挑战。Android Handler便是解决这一问题的重要工具。本文将从概念介绍、实现方法和实际应用三个角度来详细说明。
一、概念介绍
Handler是Android中的一个重要类,它是Android消息机制的基础。通俗地讲,Handler就是一个消息循环,专门用来处理Message对象的。在实践中,我们通过Handler可以实现线程之间消息的发送与处理,从而完成异步处理任务和UI更新。
数据库操作是Android开发中非常重要的一环。它可以持久保存App数据并支持数据的增删改查等基本操作,SQLite是Android中默认的数据库。但在进行数据库操作时,我们需要注意,所有的I/O操作都是在UI线程中进行的,如果数据操作任务比较复杂,程序就会阻塞UI线程,造成程序响应缓慢,严重的话,程序会崩溃。
最早的解决方法是采用线程的方式,将数据库操作放在子线程中进行,然后通过Handler来将处理结果推回UI线程中。这一方法的好处是可以防止ANR(应用未响应)情况的出现,但也存在一些问题。比如,多个操作需要在同一个子线程中被执行,此时就需要一个另外的机制来处理消息循环。
二、实现方法
通过Handler来进行数据库操作和UI更新,需要我们掌握以下的实现方法。
1.在Activity中定义Handler
“`
class UIHandler extends Handler {
public static final int
MSG_QUERY_SUCCESS=0x001,
MSG_QUERY_ERROR=0x002,
MSG_INSERT_SUCCESS=0x101,
MSG_INSERT_ERROR=0x102;
private WeakReference mActivity;
UIHandler(MnActivity activity){
mActivity=new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
MnActivity activity=mActivity.get();
switch (msg.what) {
case MSG_QUERY_SUCCESS:{
List students=(List)msg.obj;
activity.updateUI(students);
} break;
case MSG_QUERY_ERROR:{
activity.showToast(“查询失败”);
} break;
case MSG_INSERT_SUCCESS:{
int id=msg.arg1;
activity.editId=id;
activity.showToast(“添加成功”);
} break;
case MSG_INSERT_ERROR:{
activity.showToast(“添加失败”);
} break;
}
}
}
“`
此处我们定义了一个UIHandler类,它继承自Handler,采用了内部类的形式被MnActivity引用。它实现了handleMessage方法,用于处理不同的消息类型,并将消息的结果传递到MnActivity中更新UI。
在上述例子中,我们定了了4种不同的消息类型(分别表示查询成功、查询失败、添加成功和添加失败),每个消息都有其自己的处理方式。通过Message的传递,我们可以实现在子线程中进行消息的传递,从而达到异步处理任务的目的。
在UIHandler的构造函数中,我们通过WeakReference对MnActivity进行了引用,这是为了避免对Activity对象的持有,使Activity可以自动被回收从而降低内存泄露的风险。
2.在子线程中执行数据库操作
在子线程中执行数据库操作时,我们通常采用Runnable或者Thread来实现,此处以Runnable为例。
“`
class DatabaseTask implements Runnable {
private Handler mHandler;
private String mName;
public DatabaseTask(Handler handler, String name){
mHandler=handler;
mName=name;
}
@Override
public void run(){
List students=new ArrayList();
//开启数据库操作
SQLiteDatabase db=…;
try{
Cursor cursor=db.rawQuery(“select * from student”, null);
while(cursor.moveToNext()){
int id=cursor.getInt(cursor.getColumnIndex(“id”));
String name=cursor.getString(cursor.getColumnIndex(“name”));
int age=cursor.getInt(cursor.getColumnIndex(“age”));
students.add(new Student(id, name, age));
}
//数据操作成功,发送消息
mHandler.obtnMessage(UIHandler.MSG_QUERY_SUCCESS, students).sendToTarget();
} catch(Exception e){
//数据操作失败,发送消息
mHandler.obtnMessage(UIHandler.MSG_QUERY_ERROR).sendToTarget();
}
}
}
“`
在该线程中,我们封装了一段获取Student数据的操作,它通过Cursor对象遍历数据库,然后将查询到的所有Student封装在一个列表中,并通过Handler发送到UI线程中。如果操作成功,则发送UIHandler中的MSG_QUERY_SUCCESS消息,否则发送MSG_QUERY_ERROR消息。sendMessage不能直接在子线程中被调用,需要使用Handler的obtnMessage方法来将消息打包推送到消息循环中。
3.在Activity中绑定子线程
通过上述第1步和第2步,我们已经分别定义了Handler和子线程,但这两个是密不可分的。我们需要将Handler传到子线程中,并将子线程绑定在Activity中,实现子线程与UI线程之间的通信。
“`
class MnActivity extends AppCompatActivity {
private UIHandler mHandler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mn);
mHandler=new UIHandler(this);
}
public void onClickQuery(View view){
//查询–开启子线程
DatabaseTask task=new DatabaseTask(mHandler, “query”);
new Thread(task).start();
}
…
}
“`
在上述例子中,我们通过new操作符创建了一个DatabaseTask对象,并将Handler作为其参数传入。然后,我们通过Thread的启动方式来启动子线程执行查询操作。
4.更新UI
将查询结果更新到UI中需要手动实现,此处以ListView为例。假设我们已经获取到查询结果的students,那么更新UI的代码如下:
“`
public void updateUI(List students) {
mListAdapter.clear();
mListAdapter.addAll(students);
mListAdapter.notifyDataSetChanged();
}
“`
此处的mListAdapter为一个ArrayAdapter类型,用于ListView的数据绑定。
三、实际应用
在实际应用中,Android Handler与数据库的结合特别常见。以下是一个完整的例子,通过Handler实现异步地向数据库中添加一条学生信息,并将结果显示在UI上。
1.定义Activity中的UIHandler
“`
class UIHandler extends Handler {
public static final int
MSG_INSERT_SUCCESS=0x1,
MSG_INSERT_ERROR=0x2;
private WeakReference mActivity;
UIHandler(MnActivity activity){
mActivity=new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
MnActivity activity=mActivity.get();
switch (msg.what) {
case MSG_INSERT_SUCCESS:{
int id=msg.arg1;
activity.showToast(“添加成功”);
activity.editId=id;
} break;
case MSG_INSERT_ERROR:{
activity.showToast(“添加失败”);
} break;
}
}
}
“`
2. 定义添加操作的子线程
“`
class InsertTask implements Runnable {
private Handler mHandler;
private String mName;
private int mAge;
public InsertTask(Handler handler, String name, int age){
mHandler=handler;
mName=name;
mAge=age;
}
@Override
public void run(){
int result=-1;
//开启数据库操作
SQLiteDatabase db=…;
try{
ContentValues values=new ContentValues();
values.put(“name”, mName);
values.put(“age”, mAge);
result=(int)db.insert(“student”, null, values);
//数据操作成功,发送消息
mHandler.obtnMessage(UIHandler.MSG_INSERT_SUCCESS, result, -1).sendToTarget();
} catch(Exception e){
//数据操作失败,发送消息
mHandler.obtnMessage(UIHandler.MSG_INSERT_ERROR).sendToTarget();
}
}
}
“`
3. 完整的Activity代码
“`
public class MnActivity extends AppCompatActivity {
private UIHandler mHandler;
private EditText mNameEditText;
private EditText mAgeEditText;
private ListView mListView;
private ArrayAdapter mListAdapter;
private int editId=-1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mn);
mHandler=new UIHandler(this);
mNameEditText=findViewById(R.id.edit_text_name);
mAgeEditText=findViewById(R.id.edit_text_age);
mListView=findViewById(R.id.list_view_students);
mListAdapter=new ArrayAdapter(this, android.R.layout.simple_list_item_1);
mListView.setAdapter(mListAdapter);
}
public void onClickInsert(View view){
String name=mNameEditText.getText().toString().trim();
int age=Integer.parseInt(mAgeEditText.getText().toString().trim());
if(name.length()==0||age==0) return;
if(editId==-1){
InsertTask task=new InsertTask(mHandler, name, age);
new Thread(task).start();
} else {
// TODO: Update student record.
}
}
public void showToast(String message){
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void updateUI(List students) {
mListAdapter.clear();
mListAdapter.addAll(students);
mListAdapter.notifyDataSetChanged();
}
}
“`
在 onClickInsert 方法中,首先获取到名字和年龄的值,然后判断是否为空。如果为空,则直接返回;如果不为空,则启动InsertTask来添加一条Student记录。需要注意的是,如果editId变量的值不是-1,则说明需要更新学生记录的信息,由于篇幅限制,这里不再赘述。
四、