- 浏览: 385924 次
最新评论
-
qq_19471875:
为了感谢楼主 我特意注册了一个账号!,谢谢!很实用!真棒
ViewPager刷新单个页面的方法 -
asdf658:
...
Eclipse安装server插件 -
JasonMichael:
多谢。搞定~
Eclipse安装server插件 -
passerby_whu:
应该是官网写错了。应该是144x144.
Android不太能够分辨率launcher icon的适配 -
zhengyong7232:
Create or replace function test ...
postgresql产生随机数和随机日期的存储过程
前言
项目要做短信验证码自动填充的功能,基本上两种方法:ContentObserver监听短信数据库、广播。我个人比较喜欢监听数据库的方式,原因有以下几点:
1.广播方式只能读短信,不能写短信(比如更新短信状态为已读);而监听数据库则可以对短信做增删改查;
2.系统的短信广播是有序广播,如果有其他应用的优先级高于我们的应用,广播会先被其他应用截获,如果其他应用截获短信广播后将其消费掉并且不再继续发送,那我们的应用就收不到广播了;
3.广播方式需要注册receiver,动态注册还好,如果是静态注册,对于sdk类的产品来说,使用者有可能忘记在Manifest中注册;
基于以上原因,我的项目中索性就选择了监听数据库。监听数据库相对于广播稍微复杂点,但是更加灵活,功能更强大,所以还是建议使用监听数据库的方法,不过这里会将两种都说一下。
方法一:监听短信数据库
遇到的问题是大部分手机收到一条短信时会触发两次onChange()方法,很多人提出的解决方法是记录smsId,判断如果是旧的就不做处理,是新的就做该做的事。起初这么做了,后来发现有时候自动填充会先填旧的,立马又填新的,会闪一下,虽然只是偶尔出现,但还是觉得不爽,于是抽空又找别的方法,总算找出来了,也是受到一些网友的启发,下面就分享出来,原理我不多说了,代码注释里都讲得很清楚。
用法很简单,注册和注销一下就行:
方法二:广播
用法也很简单,比如你要在一个Service中使用,只要注册和注销即可:
上面的例子实现的功能是接收短信广播,收到后将短信设置为已读,实现过程是当触发onReceive后,通过ContentResolver读取短信数据库,并更新相应短信的状态为已读。需要注意里面两个地方:
1.为什么要做3秒的延迟,再去读取短信数据库:
是为了等待手机系统将短信写入短信数据库中,否则你读到的就是旧的数据库信息,不包含刚收到的短信。关于这一点,我的理解是:广播机制它是系统一旦收到短信,就发出一个广播,并将短信内容以pdus的形式放在Intent中,同时系统又去将短信往数据库中写入,发广播和写数据库是并行进行的,他们之间没有什么先后关系,所以当我们收到onReceive时其实很可能短信还没被写入数据库,如果此时去读取数据库,就读不到最新的这条短信。当然我没有去证实这个观点,但应该是八九不离十。
2.更新短信状态的处理为什么要放在子线程:
这个就只是为了适配老设备,起初没有放在子线程中,我在红米(Android4.4.4)上测试时没有问题,只要延时3秒就能读到最新的短消息,但是在一台很老的联想机(Android4.3)上面就总是读到的之前一条短信内容,即便我将延时设置为30秒也没用,我不确定是设备太老还是系统版本太旧,但我偶然发现将这个处理放在子线程后3秒就可以读到最新的短信。如果有大神能解释一下,请多多指教。
其实,方法二这样的功能,最好还是使用第一种方式监听短信,因为只有第一种方式能够写短信内容,而广播只能读短信,如果硬要将两种方式结合起来用,那在读取短信数据库前就不得不做一定的延时处理了。
项目要做短信验证码自动填充的功能,基本上两种方法:ContentObserver监听短信数据库、广播。我个人比较喜欢监听数据库的方式,原因有以下几点:
1.广播方式只能读短信,不能写短信(比如更新短信状态为已读);而监听数据库则可以对短信做增删改查;
2.系统的短信广播是有序广播,如果有其他应用的优先级高于我们的应用,广播会先被其他应用截获,如果其他应用截获短信广播后将其消费掉并且不再继续发送,那我们的应用就收不到广播了;
3.广播方式需要注册receiver,动态注册还好,如果是静态注册,对于sdk类的产品来说,使用者有可能忘记在Manifest中注册;
基于以上原因,我的项目中索性就选择了监听数据库。监听数据库相对于广播稍微复杂点,但是更加灵活,功能更强大,所以还是建议使用监听数据库的方法,不过这里会将两种都说一下。
方法一:监听短信数据库
遇到的问题是大部分手机收到一条短信时会触发两次onChange()方法,很多人提出的解决方法是记录smsId,判断如果是旧的就不做处理,是新的就做该做的事。起初这么做了,后来发现有时候自动填充会先填旧的,立马又填新的,会闪一下,虽然只是偶尔出现,但还是觉得不爽,于是抽空又找别的方法,总算找出来了,也是受到一些网友的启发,下面就分享出来,原理我不多说了,代码注释里都讲得很清楚。
package com.jackie.bindmobile; import android.app.Activity; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Handler; import android.text.TextUtils; import android.util.Log; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Monitor sms database * * @author Jackie * */ public class SmsContent extends ContentObserver { private static final String TAG = SmsContent.class.getSimpleName(); private static final String MARKER = "YOUR_KEYWORD"; private Cursor cursor = null; private Activity mActivity; public SmsContent(Handler handler, Activity activity) { super(handler); this.mActivity = activity; } /** * This method is called when a content change occurs. * <p> * Subclasses should override this method to handle content changes. * </p> * * @param selfChange True if this is a self-change notification. */ @Override public void onChange(boolean selfChange) { super.onChange(selfChange); Log.d(TAG, "onChange(boolean selfChange). selfChange=" + selfChange); onChange(selfChange, null); } /** * Notice: onChange will be triggered twice on some devices when a sms received, * eg: samsung s7 edge(API.23) - twice * samsung note3(API.18) - once * 06-15 11:45:48.706 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/raw * 06-15 11:45:49.466 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/387 * * Generally onChange will be triggered twice, first time is triggered by uri "content://sms/raw"(sms received, * but have not written into inbox), second time is triggered by uri "content://sms/387"(number is sms id) * * Android official comments: * This method is called when a content change occurs. * Includes the changed content Uri when available. * <p> * Subclasses should override this method to handle content changes. * To ensure correct operation on older versions of the framework that * did not provide a Uri argument, applications should also implement * the {@link #onChange(boolean)} overload of this method whenever they * implement the {@link #onChange(boolean, Uri)} overload. * </p><p> * Example implementation: * <pre><code> * // Implement the onChange(boolean) method to delegate the change notification to * // the onChange(boolean, Uri) method to ensure correct operation on older versions * // of the framework that did not have the onChange(boolean, Uri) method. * {@literal @Override} * public void onChange(boolean selfChange) { * onChange(selfChange, null); * } * * // Implement the onChange(boolean, Uri) method to take advantage of the new Uri argument. * {@literal @Override} * public void onChange(boolean selfChange, Uri uri) { * // Handle change. * } * </code></pre> * </p> * * @param selfChange True if this is a self-change notification. * @param uri The Uri of the changed content, or null if unknown. */ @Override public void onChange(boolean selfChange, Uri uri) { Log.d(TAG, "onChange(boolean selfChange, Uri uri). selfChange=" + selfChange + ", uri=" + uri); /** * 适配某些较旧的设备,可能只会触发onChange(boolean selfChange)方法,没有传回uri参数, * 此时只能通过"content://sms/inbox"来查询短信 */ if (uri == null) { uri = Uri.parse("content://sms/inbox"); } /** * 06-15 11:45:48.706 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/raw * 06-15 11:45:49.466 D/SmsContent: onChange(boolean selfChange, Uri uri). selfChange=false, uri=content://sms/387 * * Generally onChange will be triggered twice, first time is triggered by uri "content://sms/raw"(sms received, * but have not written into inbox), second time is triggered by uri "content://sms/387"(number is sms id) */ if (uri.toString().equals("content://sms/raw")) { return; } cursor = this.mActivity.getContentResolver().query(uri, null, null, null, null); if (cursor != null) { if (cursor.moveToFirst()) { int id = cursor.getInt(cursor.getColumnIndex("_id")); String body = cursor.getString(cursor.getColumnIndex("body")); Log.d(TAG, "sms id: " + id + "\nsms body: " + body); cursor.close(); // Already got sms body, do anything you want, for example: filter the verify code getVerifyCode(body); } } else { Log.e(TAG, "error: cursor == null"); } } /** * Register a monitor of changing of sms */ public void register() { Log.d(TAG, "Register sms monitor"); this.mActivity.getContentResolver().registerContentObserver( Uri.parse("content://sms/"), true, this); } /** * Unregister the monitor of changing of sms */ public void unRegister() { Log.d(TAG, "Unregister sms monitor"); this.mActivity.getContentResolver().unregisterContentObserver(this); } /** * Get verify code from sms body * @param str * @return */ public String getVerifyCode(String str) { String verifyCode = null; if (smsContentFilter(str)) { Log.d(TAG, "sms content matched, auto-fill verify code."); verifyCode = getDynamicPassword(str); } else { // Do nothing Log.d(TAG, "sms content did not match, do nothing."); } return verifyCode; } /** * Check if str is verification-code-formatted * * @param str * @return */ private boolean smsContentFilter(String str) { Log.d(TAG, "smsContentFilter. smsBody = " + str); boolean isMatched = false; if (!TextUtils.isEmpty(str)) { // Check if str contains keyword if (str.contains(MARKER)) { Log.d(TAG, "This sms contains \"" + MARKER + "\""); // Check if str contains continuous 6 numbers Pattern continuousNumberPattern = Pattern.compile("[0-9\\.]+"); Matcher m = continuousNumberPattern.matcher(str); while(m.find()){ if(m.group().length() == 6) { Log.d(TAG, "This sms contains continuous 6 numbers : " + m.group()); isMatched = true; } } } } return isMatched; } /** * Cut the continuous 6 numbers from str * * @param str sms content * @return verification code */ private String getDynamicPassword(String str) { Log.d(TAG, "getDynamicPassword. smsBody = " + str); Pattern continuousNumberPattern = Pattern.compile("[0-9\\.]+"); Matcher m = continuousNumberPattern.matcher(str); String dynamicPassword = ""; while(m.find()){ if(m.group().length() == 6) { Log.d(TAG, m.group()); dynamicPassword = m.group(); } } Log.d(TAG, "Verification code: " + dynamicPassword); return dynamicPassword; } }
用法很简单,注册和注销一下就行:
package com.jackie.bindmobile; import android.app.Activity; import android.os.Bundle; import android.os.Handler; public class BindMobileActivity extends Activity { private static final String TAG = BindMobileActivity.class.getSimpleName(); private SmsContent smsContent; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Register sms monitor smsContent = new SmsContent(new Handler(), mActivity); smsContent.register(); } @Override protected void onDestroy() { super.onDestroy(); // Unregister sms monitor smsContent.unRegister(); } }
方法二:广播
public class SMSBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "SMSBroadcastReceiver"; private SMSBroadcastReceiver() { } public static SMSBroadcastReceiver buildSMSReceiver(Context context) { SMSBroadcastReceiver smsReceiver = new SMSBroadcastReceiver(); IntentFilter iff = new IntentFilter("android.provider.Telephony.SMS_RECEIVED"); iff.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); context.registerReceiver(smsReceiver, iff); return smsReceiver; } public static void unregisterSMSReceiver(Context context, SMSBroadcastReceiver receiver) { context.unregisterReceiver(receiver); } @SuppressLint("SimpleDateFormat") @Override public void onReceive(Context context, Intent intent) { Object[] pdus = (Object[]) intent.getExtras().get("pdus"); for (Object pdu : pdus) { SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu); String sender = smsMessage.getDisplayOriginatingAddress(); String content = smsMessage.getMessageBody(); long date = smsMessage.getTimestampMillis(); Date timeDate = new Date(date); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String time = simpleDateFormat.format(timeDate); Log.i(TAG, " ------------- Incoming message details start ------------- "); Log.i(TAG, "SMS from: " + sender); Log.i(TAG, "SMS body: " + content); Log.i(TAG, "SMS timestamp: " + time); LogCat.i(TAG, " ------------- Incoming message details end ------------- "); // Mark SMS as read markAsRead(context, sender); } } private void markAsRead(final Context context, final String sender) { // Can not get the latest sms from ContentResolver on Android4.3 device if not run in sub thread new Thread(new Runnable() { @Override public void run() { // Wait for mobile system to write sms into database try { Thread.sleep(3000); } catch (InterruptedException e) { Log.e(TAG, e.getMessage()); } // Query sms from database Uri uri = Uri.parse("content://sms/inbox"); String where = " address = " + sender; ContentResolver resolver = context.getContentResolver(); Cursor cursor = resolver.query(uri, null, where, null, "_id desc"); if (null == cursor) return; if (cursor.moveToNext()) { // Query status String smsId = cursor.getString(cursor.getColumnIndex("_id")); int read = cursor.getInt(cursor.getColumnIndex("read")); String smsBody = cursor.getString(cursor.getColumnIndex("body")); Log.d(TAG, "Before update. smsId=" + smsId + ", read=" + read + ", smsBody=" + smsBody); // Mark as read ContentValues values = new ContentValues(); values.put("read", 1); resolver.update(uri, values, "_id=" + smsId, null); Log.d(TAG, "Mark as read DONE"); // Confirm status cursor = resolver.query(uri, null, "_id=" + smsId, null, null); cursor.moveToNext(); smsId = cursor.getString(cursor.getColumnIndex("_id")); read = cursor.getInt(cursor.getColumnIndex("read")); smsBody = cursor.getString(cursor.getColumnIndex("body")); Log.d(TAG, "After update. smsId=" + smsId + ", read=" + read + ", smsBody=" + smsBody); } cursor.close(); } }).start(); } }
用法也很简单,比如你要在一个Service中使用,只要注册和注销即可:
class SMSService extends Service{ private SMSBroadcastReceiver smsBroadcastReceiver; @Override private void onCreate() { smsBroadcastReceiver = SMSBroadcastReceiver.buildSMSReceiver(this); } @Override public void onDestroy() { SMSBroadcastReceiver.unregisterSMSReceiver(this, smsBroadcastReceiver); super.onDestroy(); } }
上面的例子实现的功能是接收短信广播,收到后将短信设置为已读,实现过程是当触发onReceive后,通过ContentResolver读取短信数据库,并更新相应短信的状态为已读。需要注意里面两个地方:
1.为什么要做3秒的延迟,再去读取短信数据库:
是为了等待手机系统将短信写入短信数据库中,否则你读到的就是旧的数据库信息,不包含刚收到的短信。关于这一点,我的理解是:广播机制它是系统一旦收到短信,就发出一个广播,并将短信内容以pdus的形式放在Intent中,同时系统又去将短信往数据库中写入,发广播和写数据库是并行进行的,他们之间没有什么先后关系,所以当我们收到onReceive时其实很可能短信还没被写入数据库,如果此时去读取数据库,就读不到最新的这条短信。当然我没有去证实这个观点,但应该是八九不离十。
2.更新短信状态的处理为什么要放在子线程:
这个就只是为了适配老设备,起初没有放在子线程中,我在红米(Android4.4.4)上测试时没有问题,只要延时3秒就能读到最新的短消息,但是在一台很老的联想机(Android4.3)上面就总是读到的之前一条短信内容,即便我将延时设置为30秒也没用,我不确定是设备太老还是系统版本太旧,但我偶然发现将这个处理放在子线程后3秒就可以读到最新的短信。如果有大神能解释一下,请多多指教。
其实,方法二这样的功能,最好还是使用第一种方式监听短信,因为只有第一种方式能够写短信内容,而广播只能读短信,如果硬要将两种方式结合起来用,那在读取短信数据库前就不得不做一定的延时处理了。
发表评论
-
Cocos2dx开发解决undefined reference to 'atof'和x86平台下报internal compiler error的错误
2016-11-21 17:10 2883最近在为游戏做java sdk的cocos2dx引擎层代码时遇 ... -
Android项目集成Jenkins(JUnit test & Coverage)
2016-08-26 14:12 4437为了实现持续集成,提 ... -
Android实现可自动关闭的定时器
2015-12-03 18:54 1462之前一篇文章里有用到过一个封装好的定时器工具类,现在又做了一些 ... -
Android不太能够分辨率launcher icon的适配
2015-04-24 11:01 1945网上讲android适配不同分辨率的文章很多,但是很少有说明不 ... -
Android 根据屏幕尺寸适配控件尺寸(按比例缩放)
2015-04-03 18:28 4685在做facebook登录时,正好看到其SDK中一段代码,可以根 ... -
Android获取状态栏高度
2015-01-28 12:04 1231获取状态栏高度有两种方法: 1.如果是在Activity中: ... -
Apktool打包和解包
2014-12-02 17:49 0本文的学习内容参考自[Android实例] 【eoeAndro ... -
Android 控件自动“移入、暂停、移出”效果的实现
2014-09-05 09:54 2438一个常见的效果:控件自动移入屏幕,停留几秒,再移出屏幕。项目中 ... -
Google Map 如何捕获onTouchEvent
2014-09-02 17:42 1759当我的项目中需要捕获google map的touch事件时,才 ... -
Android日期时间选择器实现以及自定义大小
2014-08-27 20:01 62616本文主要讲两个内容:1.如何将DatePicker和TimeP ... -
ViewPager刷新单个页面的方法
2014-08-22 11:09 39179使用ViewPager做滑动切换图片的效果时,如果图片是从 ... -
Android使用MediaPlayer开发时抛IllegalStateException
2014-08-18 16:45 60055在我开发的语音播放程序中,首次播放语音没问题,第二次播放时 ... -
Android 含有图片和文字的Button的实现
2014-07-17 20:15 8934要实现一个同时包含图片和文字的按钮,粗糙一点的做法当然是直 ... -
LinearLayout半透明效果
2014-07-16 18:12 17536透明效果有很多中实现方式,可以代码实现,也可以直接在布局文件中 ... -
Android图片压缩(质量压缩和尺寸压缩)
2014-07-04 18:16 4275在网上调查了图片压缩的方法并实装后,大致上可以认为有两类压缩: ... -
Google Map无法显示:Error contacting Google servers. XXX authentication issue
2014-06-30 20:32 2123在开发google map时遇到的问题: 06-26 14 ... -
【转】Eclipse错误识别javascript文件报错的解决方法
2014-02-13 11:52 1919Eclipse自动解析时经常会对导入的jQuery库文件或其他 ... -
让Aptana支持JavaScript的代码提示
2013-08-28 14:08 2101先下载Aptana Studio 3安装包,它是基于Eclip ... -
【转载】Android异步处理
2013-06-20 12:12 931关于Android异步处理的一整个系列的博文,共有4篇,博主写 ... -
android 写log到文件
2013-06-14 17:31 14312网上找的一个很强大的实现方法,原网页的链接找不到了,没法转载, ...
相关推荐
本文将探讨如何有效地监听短信验证码并解决onChange被多次调用的问题。 首先,监听短信通常通过ContentObserver类来实现。ContentObserver允许我们监控ContentResolver中数据的变化。在Android中,短信存储在...
在Android开发中,监听短信数据库的变化是一项常见的需求,尤其是在实现如短信自动回复、短信通知等功能时。通过监听短信数据库,开发者可以实时获取到用户手机上接收到的新短信或已有的短信变化,从而进行相应的...
Android 数据库内容变化的监听 Android 数据库内容变化的监听是 Android 系统中一种重要的机制,用于监听数据库中的内容变化。这种机制基于 Uri 的内容监测,通过 ContentResolver 类提供了三个方法来实现监听功能...
在Android系统中,监听短信通常有两种方法:通过注册BroadcastReceiver监听SMS_RECEIVED_ACTION广播和使用ContentObserver监控短信数据库。这两种方法各有优缺点,下面将详细解释它们的工作原理和实现方式。 1. ...
在Easy-UI中,`onchange`事件是用于监听用户在组件(如Combobox)中做出改变时触发的事件,这对于实现动态交互和数据处理至关重要。 首先,我们来看如何为Easy-UI的Combobox添加`onchange`事件。在HTML结构中,我们...
那么,如何在js代码模式通过代码触发textbox控件的onchange事件呢,经过查阅资料,发现js提供了一个方法,可以触发控件的应该是所有事件。object.fireEvent()方法,使用方法如 object.fireEvent(‘onchange’),即可...
web前端小问题,select 选择事件,当选择select同一值触发onchange事件
在这个活动中,我们使用了`DatabaseOpenHelper`来管理数据库,记录广播的信息,例如广播名称、最近一次触发时间以及触发次数。 `BroadcastActionRecordAct`中还包含了一个`ContentObserver`,用于监听数据库的变化...
在Android开发中,数据库是应用数据...正确地创建、注册和注销`ContentObserver`,以及在`onChange()`方法中处理数据变化,是实现这一功能的关键步骤。在实际开发中,我们还需要考虑性能优化,避免不必要的资源消耗。
总结来说,“Android短信例子大全”涵盖了Android应用处理短信的核心技术,包括使用`SmsManager`发送短信,注册BroadcastReceiver接收短信并展示通知,以及利用ContentObserver监听短信库的实时变化。这些知识对于...
通过上述步骤,可以实现一个Android应用的自动更新功能,并且能够实时监听下载进度,向用户反馈当前的下载状态。这个过程对于开发者来说既高效又方便,使得更新应用这一功能变得可靠而用户友好。
根据给定文件的信息,本文将重点探讨Android后台监听的实现机制以及如何利用这些机制来加强Android平台的安全性。首先,我们需要了解Android系统的基本结构和特点,随后深入剖析后台监听的关键技术,包括`...
为了实现短信实时删除,我们需要监听短信内容提供者的变更。Android提供了ContentObserver类,它可以监控特定ContentProvider中的数据变化。我们可以创建一个ContentObserver子类,并在Activity或Service中注册它,...
为了实现实时删除,你需要监听短信表的变化。Android提供ContentObserver类来实现这一功能。创建一个ContentObserver并注册到ContentResolver: ```java final ContentObserver observer = new ContentObserver(new...
`onchange` 事件是 JavaScript 中的一个重要事件,它会在元素的值发生改变并失去焦点后触发。在 EasyUI 中,`onchange` 事件同样适用于 `easyui-textbox` 和 `easyui-combobox`,可以用来监听用户在这些组件上的操作...
最后,使用JavaScript代码自动设置select元素的值并触发onchange事件时,要注意兼容性问题。在不支持fireEvent方法的浏览器中,可以考虑使用W3C标准的dispatchEvent方法或者使用一个通用的触发事件的方法,比如通过...
标题"onchange_1.8.zip"暗示这是一个针对CKEditor的补丁或插件,它的主要目标是解决在IE浏览器下`change`事件不触发的问题。CKEditor是一款流行的开源富文本编辑器,它允许用户在网页上创建和编辑复杂的内容,类似于...
"onchange" 事件插件是 CKEditor 的一个扩展功能,它允许开发者监听编辑器中的内容变化,并在内容发生改变时触发特定的回调函数。这个功能对于实时保存、自动校验或者实时预览等场景非常有用。 CKEditor 的 ...