可以在Android上发定时短信

  定时短信的界面比较简单,只是几个EditText和Button,功能包括添加联系人,编写短信,设定时间,保存发送等。Android号称拥有四大组件:Activity、Intent、Broadcast和Service。只要熟练掌握了这四大组件,Android开发就变得很简单了。在这个项目中,我用到了前三个,Service没有用到。Activity可以理解为一个窗口或者容器,它是可视化的,里面可以承载各种控件。对于Intent和Broadcast会在后面介绍。

  时间的设定是通过android.app.TimePickerDialog类来实现,这个类提供了一个可视化的窗口,对于用户来说界面十分友好。

  Android里的数据存储有三种方式。Android中的数据全部都是私有的,但是不同程序之间还是可以互相传递数据,那么Android是怎么做到的呢?原来Android中有一个ContentProvide,ContentProvide是来封装数据的,外界的程序可以通过 ContentProvide 的接口访问数据。Android中使用的是SQLite这个轻量级的嵌入式数据库。这个数据库对于硬件的要求很低,而且占用内存很小,但是速度很快,在嵌入式设备中被广泛应用。不过由于定时短信中需要存储的仅仅是联系人电话和编写的短信,所以我选择了另一种存储方式,那就是SharedPreferences。SharedPreferences提供了一种简单的,键值对的存储方式。一般用来存储一些简单的,少量的数据,存储一条短信再合适不过了。    

  完成这个小软件,必须解决的问题就是当设定好时间软件关闭后,时间到达时软件能够重新启动发送短信。实现这个功能的是Android里的闹钟唤醒。在android.app.AlarmManager类中提供了一种机制,使程序可以访问到系统的闹铃服务,这样应用程序就可以设定在未来的某个时间点执行。当到达那个时间点时,系统将Broadcast一个Intent来启动注册了闹铃服务的程序。 Intent在官方文档中是这样定义的:An intent is an abstract description of an operation to be performed.可以理解成Intent是对即将要执行的动作的抽象描述。在这里,我主要是把Intent给Broadcast 出去,然后在新建一个BroadcastReceiver 来接收,时间到达时开启一个新的Activity来发送短信。

  发送短信需要新建一个Activity,在本Activity里面只需使用 SmsManager这个类就可以了,这个类提供了发送短信的方法。在短信发出之后,一定要记着关掉这个发送短信的Activity,因为这个Activity的生命周期是0,发送完短信之后就没有必要存在了,使用finish()方法就可以了。

  这就是这个软件实现的主体思想,但是这样粗略地完成之后还有很多问题,比如在时间设定上,如果设定的时间已经过去,那么点击保存发送之后信息会立即发送;编写好的短信在发送之前仍然不能够查看和编辑;发送号码一栏里只能手动输入号码,不能从通讯录导入... 

    于是,我把主要的精力用在了软件的完善上。时间设定的问题比较容易解决,只要在点击短信保存按钮时对时间的合理性进行判断即可。但是在点击短信保存按钮的方法中已经不能获取设定的时间了,因此必须在点击设置时间的按钮里进行时间的判断,然后通过一个变量根据时间的合理性进行变化,在点击保存短信的方法中对这个变量进行判断,看看时间的设定是否满足要求。需要注意的是,这里系统时间的获取仍然是采用Java的语法,即final Calendar ca = Calendar.getInstance();int hours = ca.get(Calendar.HOUR);int minutes=ca.get(Calendar.MINUTE);这样就出现了一个小问题。众所周知,Java语言获取的系统时间是12小时制的,也就是说如果系统时间为14:00,那么用户设定8:00显然是不允许的,因为定时短信只能在一天之内发送,8:00代表早上八点,已经过去了。但是系统获取到的时间小时数却是2,因为获取系统时间为12小时制,通过比较8>2,时间设置看似没问题,但实际上时间设置是不正确的。所以还应当通过判断ca.get(Calendar.AM_PM)的值来得知是上午还是下午,如果是下午的话,小时数还应当再加上12。

  相对于时间问题,短信的保存就显得很简单了,只要在主Activity里再SharedPreferences一次就可以了,然后在短信发送成功之后把值再变为空。

  在整个软件的制作过程中,我遇到的最大问题就是联系人的导入。我前面说过,Android sdk更新的速度很快而且变化很大,这一点在通讯录方面尤为明显!由于我使用的是最新的Android sdk 2.2,可是有关读取通讯录联系人的资料都是使用Android sdk 1.5的API写的,当我在Android sdk 2.2下使用Android sdk 1.5的API时,Eclipse就会提出警告说不建议。没办法,我只能重新学习Android sdk 2.2里面相关的API。二者的API发生了变化,在之后与ListView进行适配时也出现了很多问题。对于联系人的读取都是获取光标实例,然后通过query()方法根据Uri读取,即Cursor c= getContentResolver().query(Phones.CONTENT_URI, null,null, null, null);不同的是,在Android sdk 2.2中Phones.CONTENT_URI变成了ContactsContract.Contacts.CONTENT_URI,另外读取联系人的姓名和电话号码也不同。怪不得有人说做Android开发很痛苦,因为要考虑不同版本之间的兼容性问题。这也没办法,Android系统毕竟是一个新的系统,在成熟性方面还不能与Symbian和WM相比。不过在现在看来,Android更新的速度已经降下来了,而且以后应该也不会出现API大幅的变化了。开发人员应该放心了!之后就是与ListView进行适配,把联系人信息显示在ListView中,在ListView的点击方法中把存储的联系人电话号码传到主Activity中。数据的传递又要使用到Intent,只不过与之前简单的跳转不同,这次的跳转需要获取数据,startActivity()变成了startActivityForResult()。

  这样,整个程序就相对完善了。当然这个小软件还存在很多问题,比如不能跨天发短信等,界面也不是很友好。这些问题的解决还需要我不断地去钻研和尝试。通过这一个小程序,我认识到自己的Android开发之旅才刚刚起步。不过我也发现Android手机不仅能够吸引用户,因为大家都说Android手机是玩机的最佳选择,而且在这上面做开发也是一种很棒的感觉,因为它提供了一套很优秀很强大的开发工具。借着开源世界强大的Eclipse和Java语言之风,我们有理由相信这个小绿人会飞得越来越高,越来越远...

 

  主程序除了在onCreat()中创建两个EditText控件与一个Button控件外,分别设置onClickLinstener()让用户单击EditText控件时,同时清除内容,在单击Button时送出短信,并通过isPhoneNumberValid()与iswithin70()这两个自定义的方法来检查收件人电话号码的正则表达式,以及短信正文的字数是否超过70个字符。

  在两项检查同时通过的前提下,通过PendingIntent.getBroadcast()的方法自定义PendingIntent并进行Broadcasting,而后使用SmsManager.getDefault()(当处理SMS短信相关的活动,例如发送数据、文字与pdu SMS信息,都需要调用这种静态的方法)所预先构建的SmsManager使用sendTextMessage()方法,将相关数据以参数带入,即可完成发送短信的任务。

 

  1. /*检查字符串是否为电话号码的方法,并返回true or false的判断值*/  
  2.   
  3.   public static boolean isPhoneNumberValid(String phoneNumber)  
  4.   
  5.   {  
  6.   
  7.     boolean isValid = false;  
  8.   
  9.     /* 可接受的电话格式有: 
  10.  
  11.      * ^//(? : 可以使用 "(" 作为开头 
  12.  
  13.      * (//d{3}): 紧接着三个数字 
  14.  
  15.      * //)? : 可以使用")"继续 
  16.  
  17.      * [- ]? : 在上述格式后可以使用具有选择性的 "-". 
  18.  
  19.      * (//d{3}) : 再紧接着三个数字 
  20.  
  21.      * [- ]? : 可以使用具有选择性的 "-" 继续. 
  22.  
  23.      * (//d{5})$: 以五个数字结束. 
  24.  
  25.      * 可以比较下列数字格式: 
  26.  
  27.      * (123)456-7890, 123-456-7890, 1234567890, (123)-456-7890   
  28.  
  29.     */  
  30.   
  31.     String expression =   
  32.   
  33.     "^//(?(//d{3})//)?[- ]?(//d{3})[- ]?(//d{5})$";  
  34.   
  35.     CharSequence inputStr = phoneNumber;  
  36.   
  37.     /*创建Pattern*/  
  38.   
  39.     Pattern pattern = Pattern.compile(expression);  
  40.   
  41.     /*将Pattern 以参数传入Matcher作Regular expression*/   
  42.   
  43.     Matcher matcher = pattern.matcher(inputStr);  
  44.   
  45.     /*创建Pattern2*/  
  46.   
  47.     Pattern pattern2 =Pattern.compile(expression2);  
  48.   
  49.     /*将Pattern2 以参数传入Matcher2作Regular expression*/   
  50.   
  51.     Matcher matcher2= pattern2.matcher(inputStr);  
  52.   
  53.     if(matcher.matches()||matcher2.matches())  
  54.   
  55.     {  
  56.   
  57.       isValid = true;  
  58.   
  59.     }  
  60.   
  61.     return isValid;   
  62.   
  63.   }  
  64.   
  65.     
  66.   
  67.   public static boolean iswithin70(String text)  
  68.   
  69.   {  
  70.   
  71.     if (text.length()<= 70)  
  72.   
  73.     {  
  74.   
  75.       return true;  
  76.   
  77.     }  
  78.   
  79.     else  
  80.   
  81.     {  
  82.   
  83.       return false;  
  84.   
  85.     }  
  86.   
  87.   }  
  88.   
  89. }  
AndroidManifest.xml

请注意,需要添加发送短信的权限android.permission.SEND_SMS。

扩展学习

//取得android系统中默认的短信管理器
SmsManager manager=SmsManager.getDefault();
//如果短信内容过长时,则对短信内容进行拆分
ArrayList<String> texts=manager.divideMessage(content);
for(String text:texts){
//第一个参数:对方手机号码
//第二个参数:短信中心号码,一般设置为空
//第三个参数:短信内容
//第四个参数:sentIntent判断短信是否发送成功,如果你没有SIM卡,或者网络中断,则可以通过这个intent来判断。
//注意强调的是“发送”的动作是否成功。那么至于对于对方是否收到,另当别论
//第五个参数:当短信发送到收件人时,会收到这个deliveryIntent。即强调了“发送”后的结果
//就是说是在"短信发送成功"和"对方收到此短信"才会激活sentIntent和deliveryIntent这两个Intent。这也相当于是延迟执行了Intent
manager.sendTextMessage(mobile,null, text,null,null);
}
//Toast.makeText(getApplicationContext(), "发送成功", Toast.LENGTH_LONG).show();
Toast.makeText(MainActivity.this,"发送成功",Toast.LENGTH_LONG).show();
}

本范例使用到的PendingIntent对象,具有下列的特性:当接收到PendingIntent对象时,会进行broadcast的动作,就如同使用Context.sendBroadcast()方法一样,这也就是为什么在SmsManager.sendTextMessage()方法中需要传入PendingIntent作为传送服务的参数之一。

在主程序中使用发送短信的方式,只展示了SmsManager类中,可使用的3种传送短信的方法之一,而完整的3种可用方法,整理如表5-1所示。

表5-1                                     SmsManager类中可使用的3种方法

方 法 名 称

传 入 参 数

使 用 时 机

sendDataMessage

String destinationAddress, String scAddress, short destin-
ationPort, byte[] data, PendingIntent sentIntent, Pending
Intent deliveryIntent

发送Data格式的SMS传送到特定程序的Port

sendMultipartTextMessage

String destinationAddress, String scAddress, ArrayList 
<String> parts, ArrayList<PendingIntent> sentIntents, 
ArrayList<PendingIntent> deliveryIntents

发送多条文字短信

sendTextMessage

String destinationAddress, String scAddress, String text, 
PendingIntent sentIntent, PendingIntent deliveryIntent

发送文字短信

 

另外,本范例并没有实现接收sms的部分,仅发出短信,由于单纯通过运行程序的模拟器,将无法了解短信是否真的有送出,而收件人是否真的有收到。因此,在程序开发的过程中,读者可以通过下面的小技巧来打开两个模拟器,一个进行传送,另一个进行收件的模拟测试。

步骤一:先进入Eclipse,compile运行程序,并顺利开始第一个模拟器实例(Instance)。

步骤二:打开DOS窗口(cmd),并输入命令,进入文件夹:

D:/>cd D:/SDK/android/tools/

步骤三:输入shell command,其中foo为AVD的名称。

D:/SDK/android/tools>emulator -data foo

此时,窗口会跳出另一个模拟器,通过输入左上方的InstanceID(例:5546)作为收件人的电话号码,即可测试短信送达的状态。

最后提到了拆分短信,此范例中虽然自制了简单的判断字符串字符数,却只能接受单则的短信,事实上,在SmsManager里尚有一个公有方法:

public ArrayList<String> divideMessage (String text)

 

 

posted @ 2012-09-24 11:01  任智康  阅读(2485)  评论(1编辑  收藏  举报