文章目錄
定時簡訊的介面比較簡單,只是幾個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()這兩個自訂的方法來檢查收件者電話號碼的Regex,以及簡訊本文的字數是否超過70個字元。
在兩項檢查同時通過的前提下,通過PendingIntent.getBroadcast()的方法自訂PendingIntent並進行Broadcasting,而後使用SmsManager.getDefault()(當處理SMS簡訊相關的活動,例如發送資料、文字與pdu SMS資訊,都需要調用這種靜態方法)所預先構建的SmsManager使用sendTextMessage()方法,將相關資料以參數帶入,即可完成傳送簡訊的任務。
- /*檢查字串是否為電話號碼的方法,並返回true or false的判斷值*/
-
- public static boolean isPhoneNumberValid(String phoneNumber)
-
- {
-
- boolean isValid = false;
-
- /* 可接受的電話格式有:
-
- * ^//(? : 可以使用 "(" 作為開頭
-
- * (//d{3}): 緊接著三個數字
-
- * //)? : 可以使用")"繼續
-
- * [- ]? : 在上述格式後可以使用具有選擇性的 "-".
-
- * (//d{3}) : 再緊接著三個數字
-
- * [- ]? : 可以使用具有選擇性的 "-" 繼續.
-
- * (//d{5})$: 以五個數字結束.
-
- * 可以比較下列數字格式:
-
- * (123)456-7890, 123-456-7890, 1234567890, (123)-456-7890
-
- */
-
- String expression =
-
- "^//(?(//d{3})//)?[- ]?(//d{3})[- ]?(//d{5})$";
-
- CharSequence inputStr = phoneNumber;
-
- /*建立Pattern*/
-
- Pattern pattern = Pattern.compile(expression);
-
- /*將Pattern 以參數傳入Matcher作Regular expression*/
-
- Matcher matcher = pattern.matcher(inputStr);
-
- /*建立Pattern2*/
-
- Pattern pattern2 =Pattern.compile(expression2);
-
- /*將Pattern2 以參數傳入Matcher2作Regular expression*/
-
- Matcher matcher2= pattern2.matcher(inputStr);
-
- if(matcher.matches()||matcher2.matches())
-
- {
-
- isValid = true;
-
- }
-
- return isValid;
-
- }
-
-
-
- public static boolean iswithin70(String text)
-
- {
-
- if (text.length()<= 70)
-
- {
-
- return true;
-
- }
-
- else
-
- {
-
- return false;
-
- }
-
- }
-
- }
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)