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的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 数据库的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。


数据运维技术 » Android Handler与数据库的实现方法 (android handler 数据库)