Android中的Intent是一個非常重要的類,如果對Intent不是特別瞭解,可以參見《詳解Android中Intent的使用方法》。如果對Intent Filter不是特別瞭解,可以參見《詳解Android中Intent對象與Intent Filter過濾匹配過程》。
本文著重講一下Android中一些常見的Intent的習慣用法,比如如何通過Intent傳送簡訊、發送郵件、啟動攝像機拍照錄視頻、設定鬧鈴、開啟WIFI設定介面等等。
限於篇幅,本文分為上下兩篇,這是上篇。
傳送簡訊
傳送簡訊的時候,我們要使用的action是Intent.ACTION_SENDTO,並且要指定其URI是smsto:協議,這樣能保證是簡訊應用接收並處理我們的intent對象,而不是其他應用接收,從而準確實現傳送簡訊的目的。如果我們的action不是Intent.ACTION_SENDTO,而是Intent.ACTION_SEND,且沒有指定smsto:協議的URI的話,那麼Android在接收到intent對象之後不會直接啟動簡訊應用,而是彈出了App Chooser,讓我們選擇要啟動哪個應用,比如電子郵件、QQ等等,所以為了確保直接啟動簡訊應用,我們應該使用Intent.ACTION_SENDTO並且指定smsto:協議的URI。
範例程式碼如下:
//使用ACTION_SENDTO而不是ACTION_SENDIntent intent = new Intent(Intent.ACTION_SENDTO);//指定URI使用smsto:協議,協議後面是接收簡訊的對象Uri uri = Uri.parse("smsto:10086");intent.setData(uri);//設定訊息體intent.putExtra("sms_body", "手頭有點緊,借點錢吧~~");ComponentName componentName = intent.resolveActivity(getPackageManager());if(componentName != null){ startActivity(intent);}
在構造傳送簡訊的URI時,前面是smsto:協議,後面跟的是接收簡訊的對方的手機號。如果在構建URI時,唯寫了smsto:,而沒有寫後面的手機的號的話,那麼該intent也可以成功啟動簡訊應用,不過這種情形下,在啟動了簡訊應用之後,還需要我們自己再手動輸入接收資訊的手機號。我們通過key為sms_body的extra設定簡訊的內容。
需要注意的是,在執行了startActivity(intent)之後,雖然簡訊應用啟動了,但是簡訊沒有直接發出去,需要我們再點擊一下發送訊息才可以。
發送郵件
發送郵件的時候,我們要使用的action也是Intent.ACTION_SENDTO,並且要指定其URI是mailto:協議,這樣能保證是郵件應用接收並處理我們的intent對象,而不是其他應用接收,從而準確實現發送郵件的目的。如果我們的action不是Intent.ACTION_SENDTO,而是Intent.ACTION_SEND,且沒有指定mailto:協議的URI的話,那麼Android在接收到intent對象之後不會直接郵件應用,而是彈出了App Chooser,讓我們選擇要啟動哪個應用,比如簡訊、QQ等等,所以為了確保直接啟動郵件應用,我們應該使用Intent.ACTION_SENDTO並且指定mailto:協議的URI。
範例程式碼如下:
//使用ACTION_SENDTO而不是ACTION_SENDIntent intent = new Intent(Intent.ACTION_SENDTO);//指定URI使用mailto:協議,確保只有郵件應用能接收到此intent對象Uri uri = Uri.parse("mailto:");intent.setData(uri);String[] addresses = {"zhangsan@126.com", "lisi@126.com"};String[] cc = {"boss@126.com"};String[] bcc = {"girlfriend@126.com"};String subject = "加班";String content = "國慶正常上班~~";//設定郵件的接收方intent.putExtra(Intent.EXTRA_EMAIL, addresses);//設定郵件的抄送方intent.putExtra(Intent.EXTRA_CC, cc);//設定郵件的密送方intent.putExtra(Intent.EXTRA_BCC, bcc);//設定郵件標題intent.putExtra(Intent.EXTRA_SUBJECT, subject);//設定郵件內容intent.putExtra(Intent.EXTRA_TEXT, content);//設定郵件附件//intent.putExtra(Intent.EXTRA_STREAM, Uri.parse(...));ComponentName componentName = intent.resolveActivity(getPackageManager());if(componentName != null){ startActivity(intent);}
啟動郵件應用後的截圖如下所示:
我們分別通過key為Intent.EXTRA_EMAIL、Intent.EXTRA_CC和Intent.EXTRA_BCC的extra依次設定郵件的接收方、抄送方、密送方,其值均為String數組。我們通過key為Intent.EXTRA_SUBJECT的extra設定郵件標題,通過key為Intent.EXTRA_TEXT的extra設定郵件內容。如果想發送附件,那麼可以將附件封裝成Uri的形式,然後通過key為Intent.EXTRA_STREAM的extra設定郵件附件。
需要注意的是,在執行了startActivity(intent)之後,雖然郵件應用啟動開啟了,但是郵件沒有直接發出去,需要我們再點擊一下右上方的發送按鈕才能將郵件發出去。
打電話
要想通過Intent打電話,我們有兩個可以使用的action:Intent.ACTION_DIAL和Intent.ACTION_CALL,二者有一定的區別。
如果使用Intent.ACTION_DIAL作為intent對象的action,那麼當執行startActivity(intent)之後,會啟動打電話應用,並且會自動輸入指定的手機號,但是不會自動撥打,需要我們手動按下撥打按鈕才能真正給對方打電話。
如果使用Intent.ACTION_CALL作為intent對象的action,那麼當執行startActivity(intent)之後,會啟動打電話應用,並且直接撥打我們指定的手機號,無需我們再手動按下撥打按鈕。但是需要注意的是,該action需要許可權android.permission.CALL_PHONE,如果在應用的AndroidManifest.xml檔案中沒有添加該許可權,那麼當指定到startActivity(intent)這句代碼的時候,就會拋出異常,應用崩潰退出。
以下是範例程式碼:
//Intent.ACTION_DIAL只撥號,不打電話//Intent intent = new Intent(Intent.ACTION_DIAL);//Intent.ACTION_CALL直接撥打指定電話,需要android.permission.CALL_PHONE許可權Intent intent = new Intent(Intent.ACTION_CALL);Uri uri = Uri.parse("tel:10086");intent.setData(uri);ComponentName componentName = intent.resolveActivity(getPackageManager());if(componentName != null){ startActivity(intent);}
在該範例程式碼中,我們使用了Intent.ACTION_CALL作為intent對象的action,並且在AndroidManifest.xml中添加了如下許可權:
複製代碼 代碼如下:
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
我們使用tel:協議的URI,在協議後面的是要撥打的號碼,將該Uri作為intent對象的data。
拍照
要想通過Intent啟動攝像機進行拍照,我們需要設定intent對象的action值為MediaStore.ACTION_IMAGE_CAPTURE的action。然後我們用key為MediaStore.EXTRA_OUTPUT的extra設定圖片的輸出路徑,最後調用startActivityForResult()方法以啟動攝像機應用,並重寫我們的onActivityResult()以便在該方法中得知拍照完成。
範例程式碼如下:
//表示用於拍照的requestCode private final int REQUEST_CODE_IMAGE_CAPTURE = 1; //我們儲存照片的輸出路徑,以便後續使用 private Uri imageOutputUri = null; //拍照 private void captureImage(){ PackageManager pm = getPackageManager(); //先判斷本機是否在硬體上有攝像能力 if(pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)){ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); ComponentName componentName = intent.resolveActivity(pm); //判斷手機上有無攝像機應用 if(componentName != null){ //建立圖片檔案,以便於通過Uri.fromFile()產生對應的Uri File imageFile = createImageFile(); if(imageFile != null){ //根據imageFile產生對應的Uri imageOutputUri = Uri.fromFile(imageFile); //利用該Uri作為拍照完成後照片的儲存路徑,注意,一旦設定了儲存路徑,我們就不能擷取縮圖了 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageOutputUri); //調用startActivityForResult()方法,以便在onActivityResult()方法中進行相應處理 startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE); }else{ Toast.makeText(this, "無法建立影像檔!", Toast.LENGTH_LONG).show(); } }else{ Toast.makeText(this, "未在本機找到Camera應用,無法拍照!", Toast.LENGTH_LONG).show(); } }else{ Toast.makeText(this, "本機沒有網路攝影機,無法拍照!", Toast.LENGTH_LONG).show(); } } //建立圖片檔案,以便於通過Uri.fromFile()產生對應的Uri private File createImageFile(){ File image = null; //用時間戳記拼接檔案名稱,防止檔案重名 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "JPEG_" + timeStamp + "_"; File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES); try{ image = File.createTempFile( imageFileName, //首碼 ".jpg", //尾碼 storageDir //檔案夾 ); }catch (IOException e){ image = null; e.printStackTrace(); Log.e("DemoLog", e.getMessage()); } return image; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { //首先判斷是否正確完成 if(resultCode == RESULT_OK){ switch (requestCode){ case REQUEST_CODE_IMAGE_CAPTURE: //此處,我們可以通過imageOutputUri擷取到我們想要的圖片 String imagePath = imageOutputUri.toString(); Log.i("DemoLog", "照片路徑是: " + imagePath); Toast.makeText(this, "照片路徑是: " + imagePath, Toast.LENGTH_LONG).show(); //以下代碼嘗試擷取縮圖 //如果設定MediaStore.EXTRA_OUTPUT作為extra的時候,那麼此處的intent為null,需要判斷 if(intent != null){ Bitmap thumbnail = intent.getParcelableExtra("data"); //有的手機並不會給拍照的圖片產生縮圖,所以此處也要判斷 if(thumbnail != null){ Log.i("DemoLog", "得到縮圖"); } } default: break; } } }
我們分析一下上面的程式碼片段:
不是所有的Android裝置都能拍照的,所以首先我們調用了PackageManager的hasSystemFeature(PackageManager.FEATURE_CAMERA)方法,判斷當前裝置在硬體層級是否具有拍照的能力。
然後我們建立了一個action為MediaStore.ACTION_IMAGE_CAPTURE的intent對象。
然後我們通過調用intent.resolveActivity(pm)方法判斷當前裝置有無攝像機應用以便我們啟動。如果沒有攝像機應用但是我們卻把intent對象傳遞給startActivity()或startActivityForResult()的話,就會拋出異常,應用崩潰退出。
我們自己寫了一個createImageFile方法,通過該方法我們在自己的應用所對應的外設儲存卡上建立了一個圖片檔案。需要注意的是,此步驟需要WRITE_EXTERNAL_STORAGE許可權,在AndroidManifest.xml中註冊如下:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18"></uses-permission>
我們利用上面產生的圖片檔案產生了對應的Uri,將其儲存在Activity中類型為Uri的欄位imageOutputUri中,之後我們執行了intent.putExtra(MediaStore.EXTRA_OUTPUT, imageOutputUri),利用該Uri作為拍照完成後照片的儲存路徑。
此處需要特別注意的是,一旦設定了儲存路徑,我們就不能在onActivityResult()中擷取縮圖了。
最後我們需要調用方法startActivityForResult(intent, REQUEST_CODE_IMAGE_CAPTURE)以啟動攝像機應用進行拍照,其中REQUEST_CODE_IMAGE_CAPTURE是我們自訂指定的用於拍照的requestCode。
我們覆寫了onActivityResult方法,拍照完成後觸發該方法的執行。首先我們要判斷resultCode是否與RESULT_OK相等,只有相等才表明拍照成功,然後我們判斷如果requestCode是否等於REQUEST_CODE_IMAGE_CAPTURE,若相等表明是拍照返回的結果。那麼此時,我們就可以通過我們之前儲存的imageOutputUri擷取剛剛拍完的照片了,其URI字串如:
file:///storage/sdcard0/Android/data/com.ispring.commonintents/files/Pictures/JPEG_20150919_112704_533002075.jpg
需要注意的是,如果我們在第5步之中設定MediaStore.EXTRA_OUTPUT作為照片輸出路徑的話,那麼在onActivityResult中無法擷取從攝像機應用換回的Intent,即為null,這樣也就無法擷取縮圖。反之,如果在第5步沒有設定MediaStore.EXTRA_OUTPUT作為照片輸出路徑的話,intent不為空白,可以嘗試執行Bitmap thumbnail = intent.getParcelableExtra("data")擷取縮圖,如果thumbnail不為空白,表示能成功擷取縮圖。但是有的手機並不會給拍照的圖片產生縮圖,所以此處的thumbnail也有可能是null,所以在使用之前要先判斷。
攝像
通過Intent啟動攝像機進行攝像的步驟與上面剛提到的通過Intent啟動攝像機進行拍照的步驟非常相似,稍有區別。要啟動Camera進行攝像,我們需要給intent設定值為MediaStore.ACTION_VIDEO_CAPTURE的action,然後我們用key為MediaStore.EXTRA_OUTPUT的extra設定圖片的輸出路徑,最後調用startActivityForResult()方法以啟動攝像機應用,並重寫我們的onActivityResult()以便在該方法中得知攝像完成。
以下是範例程式碼:
//表示用於錄視頻的requestCode private final int REQUEST_CODE_VIDEO_CAPTURE = 2; //我們儲存視頻的輸出路徑,以便後續使用 private Uri videoOutputUri = null; //攝像 private void captureVideo(){ PackageManager pm = getPackageManager(); //先判斷本機是否在硬體上有攝像能力 if(pm.hasSystemFeature(PackageManager.FEATURE_CAMERA)){ //將intent的action設定為MediaStore.ACTION_VIDEO_CAPTURE Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE); ComponentName componentName = intent.resolveActivity(pm); //判斷手機上有無攝像機應用 if(componentName != null){ //建立視頻檔案,以便於通過Uri.fromFile()產生對應的Uri File videoFile = createVideoFile(); if(videoFile != null){ //根據videoFile產生對應的Uri videoOutputUri = Uri.fromFile(videoFile); //利用該Uri作為攝像完成後視頻的儲存路徑 intent.putExtra(MediaStore.EXTRA_OUTPUT, videoOutputUri); //調用startActivityForResult()方法,以便在onActivityResult()方法中進行相應處理 startActivityForResult(intent, REQUEST_CODE_VIDEO_CAPTURE); }else{ Toast.makeText(this, "無法建立視頻檔案!", Toast.LENGTH_LONG).show(); } }else{ Toast.makeText(this, "未在本機找到Camera應用,無法攝像!", Toast.LENGTH_LONG).show(); } }else{ Toast.makeText(this, "本機沒有網路攝影機,無法攝像!", Toast.LENGTH_LONG).show(); } } //建立視頻檔案,以便於通過Uri.fromFile()產生對應的Uri private File createVideoFile(){ File videoFile = null; //用時間戳記拼接檔案名稱,防止檔案重名 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "MP4" + timeStamp + "_"; File storageDir = getExternalFilesDir(Environment.DIRECTORY_MOVIES); try{ videoFile = File.createTempFile( imageFileName, //首碼 ".mp4", //尾碼 storageDir //檔案夾 ); }catch (IOException e){ videoFile = null; e.printStackTrace(); Log.e("DemoLog", e.getMessage()); } return videoFile; } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { //首先判斷是否正確完成 if(resultCode == RESULT_OK){ switch (requestCode){ case REQUEST_CODE_VIDEO_CAPTURE: //如果設定MediaStore.EXTRA_OUTPUT作為extra的時候, //在有的手機上,此處的intent為不為null,但是在有的手機上卻為null, //所以不建議從intent.getData()中擷取視頻路徑 //我們應該自己記錄videoOutputUri來得知視頻路徑,下面注釋的代碼不建議使用 /*if(intent != null){ Uri videoUri = intent.getData(); if(videoUri != null){ //路徑格式如content://media/external/video/media/130025 Log.i("DemoLog", "視頻路徑是: " + videoUri.toString()); } }*/ String videoPath = videoOutputUri.toString(); //1.如果沒有設定MediaStore.EXTRA_OUTPUT作為視頻檔案儲存體路徑,那麼路徑格式如下所示: // 路徑格式如content://media/external/video/media/130025 //2.如果設定了MediaStore.EXTRA_OUTPUT作為視頻檔案儲存體路徑,那麼路徑格式如下所示: // 路徑格式如file:///storage/sdcard0/Android/data/com.ispring.commonintents/files/Movies/MP420150919_184132_533002075.mp4 Log.i("DemoLog", "視頻路徑是: " + videoPath); Toast.makeText(this, "視頻路徑是: " + videoPath, Toast.LENGTH_LONG).show(); break; default: break; } } }
可以看到上面啟動Camera攝像的代碼與拍照的代碼幾乎完全一樣,具體解釋參見對拍照代碼的描述。在該範例程式碼中,我們通過MediaStore.EXTRA_OUTPUT設定了視頻的存放路徑,拍照的時候我們也通過它設定了照片的輸出路徑,但是二者稍有區別:
- 1. 對於拍照,設定了MediaStore.EXTRA_OUTPUT之後,onActivityResult中的Intent參數是null,不能從Intent中得知照片的儲存路徑。
- 2. 對於攝像,設定了MediaStore.EXTRA_OUTPUT之後,onActivityResult中的Intent參數在有的手機上是null,但是在有的手機上不是null,My Phone小米1s得到的intent對象就不是null,所以此處很奇怪。如果intent不是null,可以通過intent.getData()擷取到視頻檔案的儲存路徑,但是由於intent是否為null不確定,所以盡量不要通過intent.getData()方法擷取其路徑,而應該自己在Activity中儲存一個欄位儲存我們之前設定的檔案路徑,這樣就沒問題了。
以上就是關於Android中常見Intent習慣用法,希望對大家的學習有所協助。
附源碼: 《Android中Intent習慣用法》