前一篇文章,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反射方式访问内部类成员
  2. Delphi XE5 android 捕获几个事件
  3. Android源码编译全过程
  4. android之渐变色背景
  5. android hardware 简述(Android系统源码情
  6. android 页面切换动画效果
  7. Android 跳转+两种ListView+ListheaderVi
  8. 自定义Android标题栏
  9. Android build失败 原因总结:
  10. Android studio常见错误分析解决