#1 楼
使用内容解析器(“ content:// sms / inbox”)读取收件箱中的SMS。// public static final String INBOX = "content://sms/inbox";
// public static final String SENT = "content://sms/sent";
// public static final String DRAFT = "content://sms/draft";
Cursor cursor = getContentResolver().query(Uri.parse("content://sms/inbox"), null, null, null, null);
if (cursor.moveToFirst()) { // must check the result to prevent exception
do {
String msgData = "";
for(int idx=0;idx<cursor.getColumnCount();idx++)
{
msgData += " " + cursor.getColumnName(idx) + ":" + cursor.getString(idx);
}
// use msgData
} while (cursor.moveToNext());
} else {
// empty box, no SMS
}
请添加READ_SMS权限。
希望对您有所帮助:)
评论
谢谢!您拼写了“ getColumnName”,除此之外,它像一个超级按钮一样工作。哦,如果有人会使用它,请不要忘记添加权限android.permission.READ_SMS。
– qwerty
2012年3月28日在18:40
谢谢。我修改了它:)
– Suryavel TR
2012年3月30日在6:37
这是否还会使用@CommonsWare在其对已接受答案的注释中指定的未记录的api?
–克里希那巴德拉
2012年8月20日在5:07
注意!不要像我一样错过moveToFirst。
–Alexandr Priymak
13年4月23日在14:38
@Krishnabhadra是的。它使用未记录的“ content:// sms / inbox”内容提供程序。
–pm_labs
13年5月2日在11:07
#2 楼
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final String myPackageName = getPackageName();
if (!Telephony.Sms.getDefaultSmsPackage(this).equals(myPackageName)) {
Intent intent = new Intent(Telephony.Sms.Intents.ACTION_CHANGE_DEFAULT);
intent.putExtra(Telephony.Sms.Intents.EXTRA_PACKAGE_NAME, myPackageName);
startActivityForResult(intent, 1);
}else {
List<Sms> lst = getAllSms();
}
}else {
List<Sms> lst = getAllSms();
}
将应用设置为默认的短信应用
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if (resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
final String myPackageName = getPackageName();
if (Telephony.Sms.getDefaultSmsPackage(mActivity).equals(myPackageName)) {
List<Sms> lst = getAllSms();
}
}
}
}
}
获取短信的功能
public List<Sms> getAllSms() {
List<Sms> lstSms = new ArrayList<Sms>();
Sms objSms = new Sms();
Uri message = Uri.parse("content://sms/");
ContentResolver cr = mActivity.getContentResolver();
Cursor c = cr.query(message, null, null, null, null);
mActivity.startManagingCursor(c);
int totalSMS = c.getCount();
if (c.moveToFirst()) {
for (int i = 0; i < totalSMS; i++) {
objSms = new Sms();
objSms.setId(c.getString(c.getColumnIndexOrThrow("_id")));
objSms.setAddress(c.getString(c
.getColumnIndexOrThrow("address")));
objSms.setMsg(c.getString(c.getColumnIndexOrThrow("body")));
objSms.setReadState(c.getString(c.getColumnIndex("read")));
objSms.setTime(c.getString(c.getColumnIndexOrThrow("date")));
if (c.getString(c.getColumnIndexOrThrow("type")).contains("1")) {
objSms.setFolderName("inbox");
} else {
objSms.setFolderName("sent");
}
lstSms.add(objSms);
c.moveToNext();
}
}
// else {
// throw new RuntimeException("You have no SMS");
// }
c.close();
return lstSms;
}
Sms类如下:
public class Sms{
private String _id;
private String _address;
private String _msg;
private String _readState; //"0" for have not read sms and "1" for have read sms
private String _time;
private String _folderName;
public String getId(){
return _id;
}
public String getAddress(){
return _address;
}
public String getMsg(){
return _msg;
}
public String getReadState(){
return _readState;
}
public String getTime(){
return _time;
}
public String getFolderName(){
return _folderName;
}
public void setId(String id){
_id = id;
}
public void setAddress(String address){
_address = address;
}
public void setMsg(String msg){
_msg = msg;
}
public void setReadState(String readState){
_readState = readState;
}
public void setTime(String time){
_time = time;
}
public void setFolderName(String folderName){
_folderName = folderName;
}
}
别忘了在AndroidManifest.xml中定义权限
<uses-permission android:name="android.permission.READ_SMS" />
评论
那是一段不错的代码。仅一件事,时间以毫秒为单位。我认为最好将其设置为易于理解的格式,例如String receiveDayTime = Functions.dateFromMilisec(Long.valueOf(c.getColumnIndexOrThrow(“ date”)),“ hh:mm a MMM dd,yyyy”);
– Bibaswann Bandyopadhyay
2015年9月13日19:42
用getter和setter制作所有东西的目的是什么,我真的不明白为什么不只使用直接访问元素的assoc数组或类
–michnovka
15年10月25日在18:54
@TomasNavara:检查此代码以了解getter和setter的用法。 pastebin.com/Nh8YXtyJ
–错误发生
16年8月24日在11:18
@BibaswannBandyopadhyay如果您不想使用任何东西,除了android库和Java库。 new SimpleDateFormat(“ hh:mm”,Locale.US).format(new Date(Long.parseLong(_time)));这将给您24小时的时间。
–克里斯-小
17年2月11日在14:13
未定义mActivity。这是什么?
–三
17年11月15日在6:39
#3 楼
这是一个微不足道的过程。您可以在源代码SMSPopup中看到一个很好的示例检查以下方法:
SmsMmsMessage getSmsDetails(Context context, long ignoreThreadId, boolean unreadOnly)
long findMessageId(Context context, long threadId, long _timestamp, int messageType
void setMessageRead(Context context, long messageId, int messageType)
void deleteMessage(Context context, long messageId, long threadId, int messageType)
这是读取方法:
SmsMmsMessage getSmsDetails(Context context,
long ignoreThreadId, boolean unreadOnly)
{
String SMS_READ_COLUMN = "read";
String WHERE_CONDITION = unreadOnly ? SMS_READ_COLUMN + " = 0" : null;
String SORT_ORDER = "date DESC";
int count = 0;
// Log.v(WHERE_CONDITION);
if (ignoreThreadId > 0) {
// Log.v("Ignoring sms threadId = " + ignoreThreadId);
WHERE_CONDITION += " AND thread_id != " + ignoreThreadId;
}
Cursor cursor = context.getContentResolver().query(
SMS_INBOX_CONTENT_URI,
new String[] { "_id", "thread_id", "address", "person", "date", "body" },
WHERE_CONDITION,
null,
SORT_ORDER);
if (cursor != null) {
try {
count = cursor.getCount();
if (count > 0) {
cursor.moveToFirst();
// String[] columns = cursor.getColumnNames();
// for (int i=0; i<columns.length; i++) {
// Log.v("columns " + i + ": " + columns[i] + ": " + cursor.getString(i));
// }
long messageId = cursor.getLong(0);
long threadId = cursor.getLong(1);
String address = cursor.getString(2);
long contactId = cursor.getLong(3);
String contactId_string = String.valueOf(contactId);
long timestamp = cursor.getLong(4);
String body = cursor.getString(5);
if (!unreadOnly) {
count = 0;
}
SmsMmsMessage smsMessage = new SmsMmsMessage(context, address,
contactId_string, body, timestamp,
threadId, count, messageId, SmsMmsMessage.MESSAGE_TYPE_SMS);
return smsMessage;
}
} finally {
cursor.close();
}
}
return null;
}
评论
这不是Android SDK的一部分。此代码错误地假设所有设备都支持该未记录且不受支持的内容提供程序。 Google明确表示,依靠它不是一个好主意:android-developers.blogspot.com/2010/05/…
– CommonsWare
2010-12-15在16:08
@Janusz:没有记录和受支持的方法可在所有设备上的所有SMS客户端上使用。
– CommonsWare
2010-12-27 15:07
@CommonsWare是伤心地听到。然后可能必须使用此API。
– Janusz
2010-12-28 10:09
@Omer是否知道如何计算每个联系人的SMS消息数量?
–user868935
2012年5月21日晚上10:36
代码已移动。搜索SmsPopupUtils.java在谷歌代码中给了我一个新的链接。万一他们再次移动它或完全中止它,这是一个备用链接-pastebin.com/iPt7MLyM
– KalEl
2012年6月3日12:06
#4 楼
从API 19开始,您可以使用Telephony类。由于内容提供商Uri会因设备和制造商而异,因此硬核值不会在每个设备中检索消息。public void getAllSms(Context context) {
ContentResolver cr = context.getContentResolver();
Cursor c = cr.query(Telephony.Sms.CONTENT_URI, null, null, null, null);
int totalSMS = 0;
if (c != null) {
totalSMS = c.getCount();
if (c.moveToFirst()) {
for (int j = 0; j < totalSMS; j++) {
String smsDate = c.getString(c.getColumnIndexOrThrow(Telephony.Sms.DATE));
String number = c.getString(c.getColumnIndexOrThrow(Telephony.Sms.ADDRESS));
String body = c.getString(c.getColumnIndexOrThrow(Telephony.Sms.BODY));
Date dateFormat= new Date(Long.valueOf(smsDate));
String type;
switch (Integer.parseInt(c.getString(c.getColumnIndexOrThrow(Telephony.Sms.TYPE)))) {
case Telephony.Sms.MESSAGE_TYPE_INBOX:
type = "inbox";
break;
case Telephony.Sms.MESSAGE_TYPE_SENT:
type = "sent";
break;
case Telephony.Sms.MESSAGE_TYPE_OUTBOX:
type = "outbox";
break;
default:
break;
}
c.moveToNext();
}
}
c.close();
} else {
Toast.makeText(this, "No message to show!", Toast.LENGTH_SHORT).show();
}
}
评论
似乎是不使用未记录的API且未引用第三方库的唯一答案。
–伊莎玛尔
17年1月19日在18:43
我试图使用此代码从环聊(这是我的默认短信应用)中获取短信。取而代之的是,它检索了我通过Messenger发送的最新外发邮件...您知道是什么原因造成的吗?
– Miki P
17年1月20日在1:48
@MikiP使用我的猜测能力,我会说Messenger App询问您要用Messenger取代SMS管理。其他一些消息传递应用程序也会发生这种情况。我没有其他解释。
– m3nda
17年8月17日在22:12
不要忘记调用c.close();。
–CíceroMoura
18年4月20日在17:07
@SardarAgabejli如果我们使用诸如“ contenturi:sms”之类的硬核值,则并不是每个设备都一样,但是如果使用Telephony类,我们将直接访问该竞争uri或该设备的sms db路径,指向短信数据库的助手类
–Manoj Perumarath
19年11月6日在4:32
#5 楼
这篇文章有些陈旧,但这是用于获取与Android中的SMS
内容提供者相关的数据的另一个简单解决方案:使用此lib:https://github.com/EverythingMe/easy-content -providers
获取所有
SMS
:TelephonyProvider telephonyProvider = new TelephonyProvider(context);
List<Sms> smses = telephonyProvider.getSms(Filter.ALL).getList();
每个Sms都有所有字段,因此您可以获取任何字段您需要的信息:地址,正文,receivedDate,类型(INBOX,SENT,DRAFT,..),threadId,...
全部全部
MMS
:List<Mms> mmses = telephonyProvider.getMms(Filter.ALL).getList();
所有凝胶
Thread
:List<Thread> threads = telephonyProvider.getThreads().getList();
所有凝胶
Conversation
:List<Conversation> conversations = telephonyProvider.getConversations().getList();
它可与
List
或Cursor
配合使用,并且有一个示例应用程序可查看其外观和工作方式。 实际上,所有Android内容提供商均提供支持,例如:联系人,通话记录,日历,...
具有所有选项的完整文档:https://github.com/ EverythingMe / easy-content-providers / wiki / Android-providers
希望它也有帮助:)
评论
github上的源代码和示例非常有用。对于大多数常见的提供者来说,这是一个很好的包装/外观。谢谢。
– m3nda
17年8月17日在22:18
#6 楼
步骤1:首先,我们必须在清单文件中添加权限。like
<uses-permission android:name="android.permission.RECEIVE_SMS" android:protectionLevel="signature" />
<uses-permission android:name="android.permission.READ_SMS" />
步骤2:然后添加服务短信用于接收短信的接收器类
<receiver android:name="com.aquadeals.seller.services.SmsReceiver">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
步骤3:添加运行时权限
private boolean checkAndRequestPermissions()
{
int sms = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_SMS);
if (sms != PackageManager.PERMISSION_GRANTED)
{
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_SMS}, REQUEST_ID_MULTIPLE_PERMISSIONS);
return false;
}
return true;
}
步骤4:在您的应用中添加此类并测试
接口类
public interface SmsListener {
public void messageReceived(String messageText);
}
SmsReceiver.java
public class SmsReceiver extends BroadcastReceiver {
private static SmsListener mListener;
public Pattern p = Pattern.compile("(|^)\d{6}");
@Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
Object[] pdus = (Object[]) data.get("pdus");
for(int i=0;i<pdus.length;i++)
{
SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdus[i]);
String sender = smsMessage.getDisplayOriginatingAddress();
String phoneNumber = smsMessage.getDisplayOriginatingAddress();
String senderNum = phoneNumber ;
String messageBody = smsMessage.getMessageBody();
try{
if(messageBody!=null){
Matcher m = p.matcher(messageBody);
if(m.find()) {
mListener.messageReceived(m.group(0));
}
}
}
catch(Exception e){}
}
}
public static void bindListener(SmsListener listener) {
mListener = listener;
}
}
/>
评论
模式有什么作用?
– Mark Buikema
16-10-19在9:14
嗯...(com.aquadeals.seller.services.SmsReceiver)是通用服务名称吗?
– m3nda
17年8月17日在22:21
Ya不是服务名称,即我的应用程序中的SmsReceiver类路径
–Venkatesh
17年8月18日在3:09
为什么需要LOCATION的许可?
–詹·沉
18年4月5日在11:18
我正在尝试制作一个向用户弹出短信内容的应用程序,即使该应用程序已被杀死
–安贾尼·米塔尔(Anjani Mittal)
18年7月17日在6:41
#7 楼
已经有很多答案,但是我认为所有答案都缺少这个问题的重要部分。在从内部数据库或其表中读取数据之前,我们必须了解如何在其中存储数据,然后我们可以找到上述问题的解决方案:
如何在Android中以编程方式从设备读取SMS消息?
所以,在Android中,SMS表就像这
知道,我们可以从数据库中选择所需的内容。在本例中,我们只需要
id ,地址和正文
阅读短信时:
1.询问权限
int REQUEST_PHONE_CALL = 1;
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_SMS}, REQUEST_PHONE_CALL);
}
或
<uses-permission android:name="android.permission.READ_SMS" />
2.现在您的代码是这样的
// Create Inbox box URI
Uri inboxURI = Uri.parse("content://sms/inbox");
// List required columns
String[] reqCols = new String[]{"_id", "address", "body"};
// Get Content Resolver object, which will deal with Content Provider
ContentResolver cr = getContentResolver();
// Fetch Inbox SMS Message from Built-in Content Provider
Cursor c = cr.query(inboxURI, reqCols, null, null, null);
// Attached Cursor with adapter and display in listview
adapter = new SimpleCursorAdapter(this, R.layout.a1_row, c,
new String[]{"body", "address"}, new int[]{
R.id.A1_txt_Msg, R.id.A1_txt_Number});
lst.setAdapter(adapter);
我希望这个会会有所帮助。
谢谢。
#8 楼
Google Play服务具有两个API,您可以使用它们简化基于SMS的验证过程SMS检索API
提供全自动的用户体验,而无需用户手动输入验证码,不需要任何额外的应用程序权限,应尽可能使用。但是,它确实需要您在消息正文中放置自定义哈希代码,因此您还必须对服务器端具有控制权。
消息要求-11-唯一标识您的应用的数字哈希码
发件人要求-无
用户交互-无
在Android应用中请求SMS验证
在服务器上执行SMS验证
SMS用户同意API
不需要自定义哈希码,但是需要用户批准您的应用程序访问请求包含验证码的消息。为了最大程度地减少向用户显示错误消息的机会,
SMS User Consent
将从用户的联系人列表中的发件人中过滤掉。消息要求-4-10包含至少一个数字的数字字母数字代码
发件人要求-发件人不能出现在用户的联系人列表中
用户交互-一键批准
The SMS User Consent API
是Google Play服务的一部分。要使用它,您至少需要这些库的17.0.0
版本:implementation "com.google.android.gms:play-services-auth:17.0.0"
implementation "com.google.android.gms:play-services-auth-api-phone:17.1.0"
步骤1:开始侦听SMS消息
SMS用户同意将侦听包含一次性代码的传入SMS消息,最长持续5分钟。在启动之前,它不会查看发送的任何消息。如果您知道将发送一次性代码的电话号码,则可以指定
senderPhoneNumber
,或者您不指定null
可以匹配任何号码。 smsRetriever.startSmsUserConsent(senderPhoneNumber /* or null */)
步骤2:请求同意阅读邮件
您的应用收到一条包含一次性代码的消息后,就会通过广播方式通知该消息。此时,您无需同意阅读该消息,而是会收到一个
Intent
,您可以开始提示用户同意。在BroadcastReceiver
内部,使用Intent
中的extras
显示提示。启动该意图时,它将提示用户阅读单个消息的权限。将向他们显示将与您的应用共享的所有文本。
val consentIntent = extras.getParcelable<Intent>(SmsRetriever.EXTRA_CONSENT_INTENT)
startActivityForResult(consentIntent, SMS_CONSENT_REQUEST)
步骤3:一次性解析代码并完成SMS验证
当用户单击
“Allow”
时,该是实际阅读该消息的时候了!在onActivityResult
内部,您可以从数据中获取SMS消息的全文:val message = data.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
然后您可以解析SMS消息并将一次性代码传递给您后端!
评论
包含至少一个数字的4-10位字母数字代码,您能解释一下这是什么意思吗?这是否意味着整个消息的长度应仅为短信代码的4-10个字符?
–Zeeshan Shabbir
19-10-14在13:29
也谢谢你
– Levon Petrosyan
19-10-14在15:46
这仅适用于OTP验证吗?读取手机内的所有其他消息,所有SMS等如何?有没有新的API,请告诉我。祝您编码愉快! :)
–Manoj Perumarath
19-10-28在6:30
我们总是收到超时错误。请帮我
– Manikandan K
6月18日下午5:14
#9 楼
最简单的函数要阅读短信,我编写了一个返回会话对象的函数:
class Conversation(val number: String, val message: List<Message>)
class Message(val number: String, val body: String, val date: Date)
fun getSmsConversation(context: Context, number: String? = null, completion: (conversations: List<Conversation>?) -> Unit) {
val cursor = context.contentResolver.query(Telephony.Sms.CONTENT_URI, null, null, null, null)
val numbers = ArrayList<String>()
val messages = ArrayList<Message>()
var results = ArrayList<Conversation>()
while (cursor != null && cursor.moveToNext()) {
val smsDate = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE))
val number = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS))
val body = cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.BODY))
numbers.add(number)
messages.add(Message(number, body, Date(smsDate.toLong())))
}
cursor?.close()
numbers.forEach { number ->
if (results.find { it.number == number } == null) {
val msg = messages.filter { it.number == number }
results.add(Conversation(number = number, message = msg))
}
}
if (number != null) {
results = results.filter { it.number == number } as ArrayList<Conversation>
}
completion(results)
}
使用:
getSmsConversation(this){ conversations ->
conversations.forEach { conversation ->
println("Number: ${conversation.number}")
println("Message One: ${conversation.message[0].body}")
println("Message Two: ${conversation.message[1].body}")
}
}
或仅获得特定号码的通话:
getSmsConversation(this, "+33666494128"){ conversations ->
conversations.forEach { conversation ->
println("Number: ${conversation.number}")
println("Message One: ${conversation.message[0].body}")
println("Message Two: ${conversation.message[1].body}")
}
}
#10 楼
String WHERE_CONDITION = unreadOnly ? SMS_READ_COLUMN + " = 0" : null;
更改为:
String WHERE_CONDITION = unreadOnly ? SMS_READ_COLUMN + " = 0 " : SMS_READ_COLUMN + " = 1 ";
#11 楼
读取SMS的Kotlin代码:1-将此权限添加到AndroidManifest.xml中:
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
2-创建BroadCastreceiver类:
package utils.broadcastreceivers
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.telephony.SmsMessage
import android.util.Log
class MySMSBroadCastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
var body = ""
val bundle = intent?.extras
val pdusArr = bundle!!.get("pdus") as Array<Any>
var messages: Array<SmsMessage?> = arrayOfNulls(pdusArr.size)
// if SMSis Long and contain more than 1 Message we'll read all of them
for (i in pdusArr.indices) {
messages[i] = SmsMessage.createFromPdu(pdusArr[i] as ByteArray)
}
var MobileNumber: String? = messages[0]?.originatingAddress
Log.i(TAG, "MobileNumber =$MobileNumber")
val bodyText = StringBuilder()
for (i in messages.indices) {
bodyText.append(messages[i]?.messageBody)
}
body = bodyText.toString()
if (body.isNotEmpty()){
// Do something, save SMS in DB or variable , static object or ....
Log.i("Inside Receiver :" , "body =$body")
}
}
}
3-如果Android 6及更高版本,则获取SMS权限:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
ActivityCompat.checkSelfPermission(context!!,
Manifest.permission.RECEIVE_SMS
) != PackageManager.PERMISSION_GRANTED
) { // Needs permission
requestPermissions(arrayOf(Manifest.permission.RECEIVE_SMS),
PERMISSIONS_REQUEST_READ_SMS
)
} else { // Permission has already been granted
}
4-将请求代码添加到活动或片段:
companion object {
const val PERMISSIONS_REQUEST_READ_SMS = 100
}
5-覆盖检查权限请求结果有趣:
override fun onRequestPermissionsResult(
requestCode: Int, permissions: Array<out String>,
grantResults: IntArray
) {
when (requestCode) {
PERMISSIONS_REQUEST_READ_SMS -> {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i("BroadCastReceiver", "PERMISSIONS_REQUEST_READ_SMS Granted")
} else {
// toast("Permission must be granted ")
}
}
}
}
评论
@David Freitas可信链接+1@DavidFreitas此链接无效,您能否分享最新链接?
@Khobaib,像往常一样,互联网上的事物瞬息万变。我在archive.org stackoverflow.com/a/19966227/40961上找到了一个副本,感谢他们(我最近捐赠了使它们继续运行)。但是,在此问题的答案中,我们应该考虑将页面的内容从web.archive.org/web/20121022021217/http://mobdev.olin.edu/…转换为markdown语法。大概一个小时的工作。