2012年3月31日 星期六

[TQC+ Android] 3-2 接收SMS Use BroadcastReceiver, SmsMessage, onNewIntent







設計接收SMS的程式, 程式一執行時顯示 "Waiting SMS", 在接收到由DDMS傳送的簡訊之後, 即會將儲存於本機內的圖片顯示於畫面上。現在來說明一下整個完成這項功能的處理流程, 首先進入Main Activity等待SMS 的訊息, 而我們已經在AndroidManifest.xml 靜態的註冊了一個BroacastReceiver 名稱為 SMSreceiver 它的功用是在處理System接收SMS的Action 發生
  
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>


而SMSreceiver收到了receive SMS action之後就會啟動Image activity, 並把簡訊內容和電話號碼的資訊傳給它, 而根據題目的要求, 當Image被啟動的時候使用的Image圖片是交替顯示的, 並且請使用者預先在res/drawable 資料夾底下分別載入sdk.png 和 android_118.png , 以下是程式碼的部分。


package COM.TQC.GDD03;

import android.app.Activity;
import android.os.Bundle;


public class GDD03 extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);        
    }
}


這個Activity很簡單, 就是一個mian Activity, 他的layout檔只放了一個TextView所以就不特別附上 ,下面要貼的是Image Activity的layout , 也相當的簡單。







接著是SMSreceiver class 它是一個BroadcastReceiver, 功能就是當有SMS傳進來時, 從簡訊中取得來電號碼和簡訊內容將它們放入 intent並啟動Image.class, 先來看看程式碼。
package COM.TQC.GDD03;


import java.util.Set;
import android.content.BroadcastReceiver; 
import android.content.Context; 
import android.content.Intent; 
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;


public class SMSreceiver extends BroadcastReceiver
{
 @Override 
   public void onReceive(Context context, Intent intent) 
   {       
    SmsMessage[] smsMessage = null;
      
      //先判斷這個intent是不是簡訊傳入的action
   if(intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED"))
      {       
    Bundle bundle = intent.getExtras();
    
    //檢查一下返回的Bundle裡面有哪些的資訊, 檢查Key
    Set KeySet = bundle.keySet();
       
       for(String s : KeySet)
       {
          Log.d("We have key elements", "Key:"+s);
       }
              
    Object messages[] = (Object[]) bundle.get("pdus");
    smsMessage = new SmsMessage[messages.length];
    for (int n = 0; n < messages.length; n++)
    {
     smsMessage[n] = SmsMessage.createFromPdu((byte[]) messages[n]);
        //將原始的PDU格式的資料轉為smsMessage的格式, 由於傳入的簡訊由於長度的限制可能不止一封
    }
      }      
   
   Intent NewIntent = new Intent();
   NewIntent.setClass(context, Image.class);
   //指定目的地Activity
   
   NewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   //如果是由非Activity來啟動另一個Activity, 需要加入這個flag
   
   NewIntent.putExtra("Phone", smsMessage[0].getOriginatingAddress());
   //取出來電號碼, 因為輸入的訊息長度不會拆成多封簡訊所以這邊我只取第一封, 或許可以再做更嚴謹的判斷
   
   NewIntent.putExtra("Message", smsMessage[0].getDisplayMessageBody());
   //取出簡訊內容
   
   context.startActivity(NewIntent);
     
   } 
}





再來是被SMSreceiver 呼叫的 Image.java
package COM.TQC.GDD03;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;


public class Image extends Activity 
{    
 Intent intent;
 int count;
 TextView tv;
 ImageView iv;
 
 /** Called when the activity is first created. */
 
 @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image);
        
        intent = this.getIntent();
        handleIntent(intent);
        //將intent 送去handleIntent將newIntent中的資訊取出顯示
    }
 
 @Override
 protected void onNewIntent(Intent newIntent)
 {
  // TODO Auto-generated method stub
    
  super.onNewIntent(newIntent);    
  Log.d("Test","onNewIntent()");
  //如果Activity已經存在的話, 系統會將intent發送給 onNewIntent(), 而不是再去 call onCreate()
  
  setIntent(newIntent);
  //告知將換成newIntent
  
     handleIntent(newIntent);
     //將newIntent 送去handleIntent將newIntent中的資訊取出顯示
 }
 
 private void handleIntent(Intent intent)
 {
  tv = (TextView) this.findViewById(R.id.TextView1);
  iv = (ImageView) this.findViewById(R.id.imageView1);
  
  Bundle bundle = intent.getExtras();  
  SharedPreferences sPreferences = this.getSharedPreferences("SMSCount", MODE_PRIVATE);
  
  tv.setText("電話號碼:"+ bundle.getString("Phone") +", SMS內容:"+bundle.getString("Message"));
  count = sPreferences.getInt("Count", 0);
  Log.d("Test","Count:"+count);  
  
  if(count%2==0) iv.setImageResource(R.drawable.android_118);
  else iv.setImageResource(R.drawable.sdk);
  count++;
  //處理圖片交替顯示的部分
  
  sPreferences.edit().putInt("Count", count).commit();
  
 }
 
/*  另一個不要重複產生Instance的方法是在切換到另一個Activity之前
           在onStop()就call finish() destroy Instance
      
 @Override
 protected void onStop()
 {
  // TODO Auto-generated method stub    
    
  super.onStop();
  this.finish();
 } 
*/
}


當BroadcastReceiver每收到一個SMS的訊息, 它就會startActivity一次(也就是create Instance), 而這樣一來當我們沒有按下Back鍵回到main Activity, 而又收到SMS的話, 在Activity stack中就會產生多個 Image Activity, 這種情況顯然不是我們想見到的, 為了處理這種情況, 有簡單的兩種方法, 


1. 在onStop()的時候finish() 當前的Activity, 因為onStop() method 不管在我們按下Back鍵, 或是被強行跳出到新的Image Activity之前都會被執行的method。


2. 在AndoridManifest.xml中宣告Image Activity的lauchMode為singleTop, 因為singleTop的屬性規則是如果此Activity已經產生在Top of Activity stack的話, system就不會再替他create instance, 實際上只是不去call onCreate() method, 取而代之的是call onNewIntent() method, 所以我們要在程式中override onNewIntent() method, 把設定圖片來源的code 在這邊處理。




最後是AndroidManifest.xml的部分

    
    
        
            
                
                
            
        
                    
        
        
            
                
            
        
    
    
 


這邊要注意的地方就是先前提到的記得幫 Image Activity宣告lauchMode:singleTop, 另外還要幫SMSreceiver 在intent-filter中宣告 <action android:name="android.provider.Telephony.SMS_RECEIVED"/> , 最後別忘記因為我們會讀取到簡訊的內容, 所以記得別忘了user-permission: <uses-permission android:name="android.permission.RECEIVE_SMS"/>









P.S. 題目中所要求的Variable和Method皆會保留 , 也會根據題目所要求的流程去實作 , 縱使題目要求繞遠路....



1 則留言:

  1. 為什麼在執行SMSreceiver.java時
    第26行的會出現錯誤

    回覆刪除

Google Analytics