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,则说明需要更新学生记录的信息,由于篇幅限制,这里不再赘述。
四、
相关问题拓展阅读:
- Android 为什么使用Handler
- Android Handler那些事儿,消息屏障?IdelHandler?ANR?
- Android Handler消息机制?
Android 为什么使用Handler
在Android的UI开发中,我们经常会使用Handler来控制主UI程序的界面变化。有关Handler的作用,我们总结为:与其他线程协同工作,接收其他线程的消息并通过接收到的消息更或穗拆新主UI线程的内容。
我们假设在一个UI界面上面,有一个按钮,当点击这个按钮的时候,会进行网络连接,并把网络上的一个字符串拿下来显示到界面上的一个 TextView上面,这时就出现了一个问题,如果这个网络连接的延迟过大,可能是10秒钟甚至更长,那我们的界面将处于一直假死状态,而如果这段时间超 过5秒钟的话,程序会出现异常。
这时我们会想到使用线程来完成以上工作,即当按钮被按下的时候新开启一个线程来完成网络连接工作,并把得到的结果更新到UI上面。但是,这时候又会 出现另一个问题,在Android中,主线程是非线程安全的,也就是说UI的族让更新只能在本线程中完成,其他线程无法直接对主线程进行操作。
为了解决以上问题,Android设计了Handler机制,由Handler来负责与子线程进行通讯,从而让子线程与主线程之间建立起协作的桥梁,使Android的UI更新的问题得到完美的解决。接下来举例来诠释Handler的基本使用方法。
Handler的工作原理
一般情况下,在主线程中我们绑定了Handler,并在事件触发上面创建新的衫枣线程用于完成某些耗时的操作,当子线程中的工作完成之后,会对Handler发送一个完成的信号,而Handler接收到信号后,就进行主UI界面的更新操作。
Handler与子线程协作实例
1、创建Handler实现类,在主UI所在类中的内部类
class MyHandler extends Handler {
public MyHandler() { }
public MyHandler(Looper L) {
super(L);
}
// 重写handleMessage方法,接受数据并更新UI
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//此处根据msg内容进行UI操作
}
}
2、子线程的实现
class MyThread implements Runnable {
public void run() {
Message msg = new Message();
Bundle b = new Bundle();
b.putString(“cmd”, “update”);
msg.setData(b);
MainActivity.this.myHandler.sendMessage(msg);
//通知Handler更新UI
}
}
通过以上的两个实现,我们只需要在MainActivity中声明MyHandler实例对象就可以完成线程之间的通讯和界面的更新操作。
MyHandler myHandler = newMyHandler();
Android Handler那些事儿,消息屏障?IdelHandler?ANR?
Handler 是Android SDK中用来处理异步消息的核心类,子线程可以通过handler来通知主线程进行ui更新。
备注:本文源码截图 基于Android sdk 28
Handler机制 消息发送主要流程如图
应用程序启动后,zygote fork一个应用进程后,和普通java程序一样,程序会首先执行ActivityThread中的main函数。在main函数中,程序首先会创建Looper对象并绑定到主线程中,然后开启loop循环。(ps:主线程loop循环不能退出)
在prepareMainLooper方法中,最终会创建Looper,MessageQueue对象 以及创建native层MessageQueue对象。
使用Handler.sendMessageXXX或这 postDedayXXX发送消息后,最终会调用到SendMessageAtTime方法中。
然后调用MessageQueue.enqueueMessage将消息存到消息队列中。
存入消息后,然后通过调用native方法 唤醒主线程进行消息处理。
当应用程序启者滚乱动,做完一些必要工作之后,便会开启Loop循环,除非系统异常,否则该循环不会停止。loop循环中,主要做两件事,之一,从消息队列中取消息。第二,进行消息分发处理。
MessageQueue.next() 方法 通过调用 native方法 nativePollOnce(ptr, nextPollTimeoutMillis)实现无消息处理时,进入阻塞的功能。
当nextPollTimeoutMillis 值为0时,该方法会立刻返回;
当nextPollTimeoutMillis 值为-1时,该方法会无限阻塞,直到被唤醒;
当nextPollTimeoutMillis 值大于0时,该方法会将该值设置为超时时间,阻塞到达一定时间后,返回;
在loop循环中 ,通过调用 msg.target.dispatchMessage(msg) 进行消息的分发处理
使用当前线程的MessageQueue.addIdleHandler方法可以在消息队列中添加一个IdelHandler。
当MessageQueue 阻塞时,即当前线程空闲时,会回调IdleHandler中的方法;
当IdelHandler接口返回false时,表示该IdelHandler只执行一次,
a,延迟执行
例如,当启动Activity时,需要延时执行一些操作,以免启动过慢,我们常常使用以下方备键式延迟执行任务,但是在延迟时间上却不好控制。
其实,这时候使用IdelHandler 会更优雅
b,批量任务,任务密集,且只关注最终结果
例如,在开发一个IM类型的界面时,通常情况下,每次收到一个IM消息时,都会刷新一次界面,但是当短时间内, 收到多条消息时,就会刷新多次界面,容易造成卡顿,影响性能,此时就可以使用一个工作线程监听IM消息,在通过添加IdelHandler的方式通知界面刷新,避免短时间内多次刷新界面情况的发生。
在Android的消息机制中,其实有三种消息: 普通消息、异步消息及消息屏障。
消息屏障
也是一种消息,但是它的target为 null。可以通过MessageQueue中的postSyncBarrier方法发送一个消首档息屏障(该方法为私有,需要反射调用)。
在消息循环中,如果之一条消息就是屏障消息,就往后遍历,看看有没有异步消息:
如果没有,则无限休眠,等待被唤醒
如果有,就看离这个消息被触发时间还有多久,设置一个超时时间,继续休眠
异步消息
和普通消息一样,只不过它被设置setAsynchronous 为true。有了这个标志位,消息机制会对它有些特别的处理,我们稍后说。
所以
消息屏障和异步消息的作用
很明显,在设置消息屏障后,异步消息具有优先处理的权利。
这时候我们回顾将消息添加到消息队列中时,可以发现,其实并不是每一次添加消息时,都会唤醒线程。
当该消息插入到队列头时,会唤醒该线程;
当该消息没有插入到队列头,但队列头是屏障,且该消息是队列中 靠前的一个异步消息,则会唤醒线程,执行该消息;
调用MessageQueue.removeSyncBarrier 方法可以移除指定的消息屏障
ANR 即 Application Not Response, 是系统进程对应用行为的一种监控,如果应用程序没有在规定时间内完成任务的话,就会引起ANR。
ANR类型
Service Timeout
: 前台服务20s, 后台服务200s
BroadcastQueue Timeout
: 前台广播 10s,后台广播60s
ContentPrivider Timeout
: 10s
InputDispatching Timeout
: 5s
比如,在启动一个服务时, AMS端通过应用进程的Binder对象创建Service, 在scheduleCreateService()方法中 会调用到当前service的onCreate()生命周期函数;
bumpServiceExecutingLocked()方法内部实际上会调用到scheduleServiceTimeoutLocked()方法,发送一个ActivityManagerService.SERVICE_TIMEOUT_MSG类型消息到AMS工作线程中。
消息的延时时间,如果是前台服务,延时20s, 如果是后台服务,延时200s;
如果Service的创建 工作在 上诉消息的延时时间内完成,则会移除该消息,
否则,在Handler正常收到这个消息后,就会进行服务超时处理,即弹出ANR对话框。
复杂情况下,可能会频繁调用sendMessage 往消息队列中,添加消息,导致消息积压,造成卡顿,
1,重复消息过滤
频繁发送同类型消息时,有可能队列中之前的消息还没有处理,又发了一条相同类型的消息,更新之前的数据,这时候,可以采用移除前一个消息的方法,优化消息队列。
2,互斥消息取消
在发送消息时,优先将消息队列中还未处理的信息已经过时的消息 移除,优化队列
3,队列优化-复用消息
创建消息时,优先采用之前回收的消息,避免重复创建对象,引起GC
完~
(如果错误或不足,望指出, 大家共同进步)
Android Handler消息机制?
跨线程通信。当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。
四要素:
Message(消息):需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
MessageQueue(消息队列):用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。
Handler(处理者):负责Message的发送及处理。通过 Handler.sendMessage() 向乎凳消息池发送各种消息事件;通过 Handler.handleMessage() 处理相应的消纯清息事件。
Looper(消息泵):通过Looper.loop()不断地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。
Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息;
通过Looper.loop()开启循环后,不断轮询调用MessageQueue.next();
调用目标岁裤旅Handler.dispatchMessage()去传递消息,目标Handler收到消息后调用Handler.handlerMessage()处理消息。
关于android handler 数据库的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。
香港服务器首选后浪云,2H2G首月10元开通。
后浪云(www.IDC.Net)提供简单好用,价格厚道的香港/美国云服务器和独立服务器。IDC+ISP+ICP资质。ARIN和APNIC会员。成熟技术团队15年行业经验。