前一篇文章,Android短信拦截机制适配的坑(上)--4.4以下系统

介绍了广播接收的顺序,但是我明确说明在4.4以下系统,那么4.4及以上系统会遇到说明问题呢?

首先我们要来了解4.4系统短信的机制的改变,主要是这篇文章

http://android-developers.blogspot.com/2013/10/getting-your-sms-apps-ready-for-kitkat.html

还有两篇篇中文翻译大家也可以看看

http://blog.csdn.net/maybe_windleave/article/details/17740345

让你的短信应用迎接Android 4.4(KitKat)


下面我再来说明一下google对短信机制的修改,首先一个原则是,

4.4及其以后系统,只能设置一个默认的SMS短信app,但短信到达,首先会通知这个app,并且只有这个app有对短信数据库的修改权限短信的发送权限

并且短信广播,不再是有序广播,也就是App没有办法拦截这个广播,所有app都快接收到短信到达的广播通知,但是只有默认SMS短信app可以修改短信记录

但是!不排除有些操作系统,例如小米会修改这个机制!


那么我再次拿我上一篇文章的需求来说,

需求是:新短信到达以后,项目app希望可以提示用户未读短信的数据,并且可以将短信置为已读。

问题是:和微信电话本冲突的情况,由于微信电话本也要实现上述功能,可是它有一个坑就是,它收到短信以后,就将短信置为已读

解决方法是:比微信电话本更早接收到新短信到达的通知,但是可惜的是,由于我的app不是短信app,所以不能这样做以致使用户收不到新短信


但是我们这里只是来说我们要怎么做,才能比微信电话本早。

在前一篇文章中,已经说过了设置最高优先权的办法,这里面临的一个新问题就是,默认Sms短信app还是会比我们先,所以我最好让用户将我们的App设置为默认sms短信app

根据上面的参考文章,我做了一个简单的demo,这个demo让我们可以在系统选项中,将我们的app设置为默认sms短信app

在我们的原生6.0系统的手机上,是这样的,设置-》应用-》默认应用-》短信应用-》选择AndroidTest



怎么才能做到呢?如果大家看了我提到的文章,就应该知道怎么做,如果嫌麻烦,可以看一下我的设置,首先是AndroidManifest的设置

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.example.androidtest"    android:versionCode="1"    android:versionName="1.0" >    <uses-sdk        android:minSdkVersion="8"        android:targetSdkVersion="21" />    <uses-permission android:name="android.permission.WRITE_SMS" />    <uses-permission android:name="android.permission.READ_SMS" />    <uses-permission android:name="android.permission.RECEIVE_SMS" />    <uses-permission android:name="android.permission.RECEIVE_MMS" />    <application        android:allowBackup="true"        android:icon="@drawable/ic_launcher"        android:label="@string/app_name">        <activity            android:name=".MainActivity"            android:label="@string/app_name" >            <intent-filter>                <action android:name="android.intent.action.MAIN" />                <category android:name="android.intent.category.LAUNCHER" />            </intent-filter>        </activity>                <receiver android:name="com.example.androidtest.SmsReceiver" android:permission="android.permission.BROADCAST_SMS">            <intent-filter android:priority="2147483647">            <action android:name="android.provider.Telephony.SMS_DELIVER" />            <action android:name="android.provider.Telephony.SMS_RECEIVED" />            </intent-filter>        </receiver>                <!-- BroadcastReceiver that listens for incoming MMS messages -->                 <receiver android:name=".MmsReceiver"            android:permission="android.permission.BROADCAST_WAP_PUSH">            <intent-filter>                <action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />                <data android:mimeType="application/vnd.wap.mms-message" />            </intent-filter>        </receiver>        <!-- Activity that allows the user to send new SMS/MMS messages -->                  <activity android:name=".ComposeSmsActivity" >            <intent-filter>                <action android:name="android.intent.action.SEND" />                                <action android:name="android.intent.action.SENDTO" />                <category android:name="android.intent.category.DEFAULT" />                <category android:name="android.intent.category.BROWSABLE" />                <data android:scheme="sms" />                <data android:scheme="smsto" />                <data android:scheme="mms" />                <data android:scheme="mmsto" />            </intent-filter>        </activity>         <!-- Service that delivers messages from the phone "quick response" -->              <service android:name=".HeadlessSmsSendService"                 android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"                 android:exported="true" >            <intent-filter>                <action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />                <category android:name="android.intent.category.DEFAULT" />                <data android:scheme="sms" />                <data android:scheme="smsto" />                <data android:scheme="mms" />                <data android:scheme="mmsto" />            </intent-filter>        </service>             </application></manifest>

其实

1、是设置短信获取的权限

2、是设置SmsReceiver,MmsReceiver,ComposeSmsActivity,HeadlessSmsSendService,并且这四个一个都不能少,而且对于的action之类的都要设置正确(简单来说,你copy我的,根据自己的实际改改就行了)


接下来为了简单起见,我设置了四个最简单的类

MmsReceiver:

package com.example.androidtest;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;public class MmsReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {Log.i("cky","MmsReceiver: "+intent);}}

ComposeSmsActivity:

package com.example.androidtest;import android.app.Activity;import android.os.Bundle;import android.util.Log;public class ComposeSmsActivity extends Activity{@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);Log.i("cky","ComposeSmsActivity");}}

HeadlessSmsSendService:

package com.example.androidtest;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.util.Log;public class HeadlessSmsSendService extends Service{@Overridepublic IBinder onBind(Intent intent) {Log.i("cky","HeadlessSmsSendService: "+intent);return null;}}

在SmsReceiver里面,我打印了新短信的信息,并且查询了短信数据库(查询短信权限是有的,但是没有修改权限)

package com.example.androidtest;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.telephony.SmsMessage;import android.transition.Slide;import android.util.Log;import android.widget.Toast;public class SmsReceiver extends BroadcastReceiver{public static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";  public SmsReceiver() {Log.i("cky", "new SmsReceiver");}@Overridepublic void onReceive(Context context, Intent intent) {  // TODO Auto-generated method stubLog.i("cky", "jie shou dao");Cursor cursor = null;        try {                        if (SMS_RECEIVED.equals(intent.getAction())) {              Log.d("cky", "sms received!");              Bundle bundle = intent.getExtras();              if (bundle != null) {                  Object[] pdus = (Object[]) bundle.get("pdus");                  final SmsMessage[] messages = new SmsMessage[pdus.length];                  for (int i = 0; i < pdus.length; i++) {                      messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);                  }                  if (messages.length > 0) {                      String msgBody = messages[0].getMessageBody();                      String msgAddress = messages[0].getOriginatingAddress();                      long msgDate = messages[0].getTimestampMillis();                      String smsToast = "New SMS received from : "                              + msgAddress + "\n'"                              + msgBody + "'";                      Toast.makeText(context, smsToast, Toast.LENGTH_LONG)                              .show();                      Log.d("cky", "message from: " + msgAddress + ", message body: " + msgBody                              + ", message date: " + msgDate);                    }              }              cursor = context.getContentResolver().query(Uri.parse("content://sms"), new String[] { "_id", "address", "read", "body", "date" }, "read = ? ", new String[] { "0" }, "date desc");            if (null == cursor){                return;            }                                   Log.i("cky","m cursor count is "+cursor.getCount());            Log.i("cky","m first is "+cursor.moveToFirst());                                        }        } catch (Exception e) {            e.printStackTrace();            Log.e("cky", "Exception : " + e);        } finally {            if (cursor != null) {                cursor.close();                cursor = null;            }        }}}

最后是MainActivity,里面有一个按钮,按一下就将新短信设置为已读

package com.example.androidtest;import android.annotation.SuppressLint;import android.app.Activity;import android.content.ContentValues;import android.content.Intent;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.provider.Telephony;import android.provider.Telephony.Sms;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;@SuppressLint("NewApi")public class MainActivity extends Activity {SmsReceiver myReceiver;Button btn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);btn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {test();}});}//将新短信设置为已读public void test(){Cursor cursor = null;        try {            cursor = getContentResolver().query(Uri.parse("content://sms/inbox"), new String[] { "_id", "address", "read" }, "read = ? ", new String[] {"0" }, "date desc");            if (cursor != null) {                ContentValues values = new ContentValues();                values.put("read", "1");                                     for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {                    Log.v("cky", "" + cursor.getInt(cursor.getColumnIndex("_id")) + "  ,  " + cursor.getString(cursor.getColumnIndex("address")));                                        int res = getContentResolver().update(Uri.parse("content://sms/inbox"), values, "_id=?", new String[] { "" + cursor.getInt(cursor.getColumnIndex("_id")) });                    Log.i("cky","geng xin = "+res);                }                            }                    } catch (Exception e) {            e.printStackTrace();        } finally {            if (cursor != null) {                cursor.close();                cursor = null;            }        }}}


接下来就是测试

1,当我们没有设置AndroidTest为默认sms短信app的时候,给手机发短信,通过logcat可以看到有新短信,但是数据库查询不到(这个原因见上一篇文章);

点击按钮,没有办法将短信设置为已读(geng xin=0)


2,我将AndriodTest设置为默认Sms短信app,结果如下,我们可以将短信设置为已读了(geng xin-1),并且数据库查询,也有新短信信息



综上所述,就可以使我们的app最先接收到短信了


可是问题是,我们的App既想修改短信数据库,又不想作为短信app,怎么办呢?

答案是凉拌。。虽然google为我们提供了一种机智的方法,就是在我们要操作短信数据库的时候,将我们的app设置为默认sms短信app

数据库操作结束以后,再还原回来

步骤如下:

1查询当前默认的短信应用包名并把包名保存起来。

String defaultSmsApp = Telephony.Sms.getDefaultSmsPackage(context);


2请求用户把你的应用设置成默认短信应用以便进行短信还原(你必须作为默认短信应用才可以写入数据到SMS Provider中)。

Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT);intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, context.getPackageName());startActivity(intent);

3当你还原完所有短信之后,请求用户把步骤一保存的应用设置回默认短信应用。

Intent intent = new Intent(context, Sms.Intents.ACTION_CHANGE_DEFAULT);intent.putExtra(Sms.Intents.EXTRA_PACKAGE_NAME, defaultSmsApp);startActivity(intent);


上面的做法,看起来不错,但是在实际应用中,大部分操作系统都会弹出一个确认窗口,只有用户点击了以后,才能修改成功

就是这个弹出窗口,影响用户体验(意味着每次app修改短信数据库,都会有弹出窗口);

并且这个窗口弹出以后,和你的数据库操作是异步的,也就是可能用户还没有确认关闭这个窗口,你就进行了数据库操作,结果导致操作失败

至于怎么监听这个系统级的弹出窗口,我还没有办法,有知道的朋友可以给我说说谢谢!


文章到此结束,不得不说,Android适配的坑实在是太多了!


更多相关文章

  1. Android(安卓):Kernel Uevent发送(热插拔)事件到用户空间
  2. adroid之Sqlite篇
  3. Android(安卓)使用ContentObserver监听短信的变化,并发送信息给特
  4. 短信的收发及在android模拟器之间实践(3)
  5. Android整合网上资源以及个人对GreenDao数据库框架的理解与使用(
  6. android startActivityForResult的用法
  7. 第3.1.4节 理解任务与返回堆栈
  8. Android项目开发中如何处理Home键
  9. Android(安卓)7.0新特性总结

随机推荐

  1. android:layout_gravity和android:gravit
  2. android小说阅读源码、bilibili源码、MVP
  3. Android(安卓)SDK 2.0安装(配置图文教程)
  4. Android开发人员必须收藏的国外网站
  5. Android知识图谱:我们到底需要学习哪些And
  6. 如何学习android
  7. 跟着做 Android(安卓)NDK学习入门如此简
  8. Android分割线divider(内含Android虚线分
  9. android布局文件中各属性所代表的意义
  10. Unity与Android(安卓)Studio✨之间那些不