Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android成長之路(7)——關於隱式Intent的用法

Android成長之路(7)——關於隱式Intent的用法

編輯:關於Android編程

Android其中最重要的特性之一,就是一個應用可以基於“action”來切換到另一個應用。比如,你的應用想要查找地方,在地圖上顯示。但是不一定要創建一個activity來顯示地圖,可以使用Intent發起一個請求來查看地址,然後Android系統會啟動一個可以顯示地圖的應用。

之前,會使用到顯式的Intent來讓一個activity跳轉到另一個activity。但是,當想要跳轉到一個獨立的應用時,比如查看地圖,這時候就一定要使用隱式Intent。

創建一個隱式Intent

隱式Intent不用像顯式Intent那樣傳入一個目標activity的類名,只需要傳入操作的響應,也就是action。這個action表明了你想要做什麼。Intents通常會包含與action相關的數據,比如想要查看的地址,或者想要發送的郵件信息等。這取決於你想要做什麼,數據可能是Uri類型,也可能是其他類型,或者根本就不需要數據。

如果數據是Uri類型的,下面一個簡單的Intent()構造函數例子,可以用來定義action和數據。
比如說,怎麼樣創建一個Intent初始化一個電話撥號,用Uri類型來作為電話號碼的類型。

Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

查看地圖:

Uri location = Uri.parse("geo:38.899533,-77.036476"); 
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

打開網頁:

Uri website = Uri.parse("http://xxxxx.com"); 
Intent webIntent = new Intent(Intent.ACTION_VIEW, website);

發送郵件:

Uri emailUri = Uri.parse("mailto:[email protected]"); 
intent emailIntent = new Intent(Intent.ACTION_SENDTO, emailUri); 

想要傳遞其他不同類型的數據,可以調用不同的putExtra()方法,把數據添加進去隱式的Intent中。

默認的情況下,系統基於所包含的 Uri 數據確定Intent需要的相應 MIME 類型。如果Intent中不包含Uri,應該使用 setType()來指定與Intent相關的數據。設置MIME類型指定那一種類型的activity可以接收這個Intent。

發送一個郵件附件:

Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"}); 
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://path/to/email/attachment"));

這個Intent沒有Uri,所以聲明MIME類型為 “text/plain”

注意:定義Intent時要盡可能具體,這是非常重要的。比如,如果你使用ACTION_VIEW inten想要展示一張圖片,那你就應該指定MIME類型為 image/*. 這就防止了你的app被Intent觸發之後意外地查看到其它的數據類型(比如地圖應用)

檢驗是否有適合的應用來接收Intent

盡管Android系統保證每一個確切的Intent都會對應apps中發起的一個請求響應(比如電話、郵件或者地圖應用等),但是在調用一個Intent之前還是應該包含驗證這一步驟的。如果調用了一個Intent,但是設備中並沒有一個可以處理這個Intent請求響應的應用,這時候app就會崩潰了。

檢驗過程並不復雜,創建一個PackageManager的對象,調用queryIntentActivities() 來獲取activity的List容器,這個可以用來處理Intent。如果這個List不為空的話,那就說明至少有一個應用可以接這個Intent,調用Intent是安全的。

PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

使用Intent來啟動一個活動

一旦已經創建了Intent,並且設置好了額外的信息,就可以調用startActivity()來發送給系統了。如果系統發現有多個應用都可以出來響應這個Intent,那麼就會顯示一個對話框出來,讓你選擇其中一個應用。如果你之前已經選擇了默認的應用,那就會直接打開。

這裡寫圖片描述
效果圖,因為已經選擇默認的浏覽應用了,就直接打開<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160521/20160521092232473.gif" title="\" />

結合上面的檢驗步驟,代碼如下:

Uri website = Uri.parse("http://www.baidu.com"); 
Intent webIntent = new Intent(Intent.ACTION_VIEW, website);

PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

if(isIntentSafe){
      startActivity(webIntent);
}

用戶可以勾選選擇框來默認啟動的應用,可能每一次用戶都會選擇用一個應用來打開網頁或者拍照。但是呢,如果類似於分享這種,用戶可能就會要選擇幾個不同的應用來分享了。以應該有一個選擇對話框可以讓用戶選擇。畢竟不能讓用戶只能勾選一個默認應用吧。

所以,需要使用 createChooser()來創建一個intent,然後再把它傳進startActivity()中。這樣,通過傳入createChooser()方法返回的Intent,就會顯示一個選擇窗口,可以選擇不同的應用。標題就是傳入的title。

Intent intent = new Intent(Intent.ACTION_SEND);
...

// 最好在字符串資源中定義UI文本,這個文本顯示的是"Share this photo with"
String title = getResources().getString(R.string.chooser_title);

// 創建一個Intent來顯示選擇窗口,傳入intetn和標題
Intent chooser = Intent.createChooser(intent, title);

//驗證
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

這裡寫圖片描述

接收返回結果

啟動另一個應用並不是只有startActivity()這個方法,也可以使用startActivityForResult()來代替startActivity(),這樣啟動另一應用的同時,還可以獲得返回的結果。比如,啟動照相機應用,然後會接收拍到的照片作為結果。當然,在activity中,需要一個方法來接收這個返回結果。activity在 onActivityResult()方法中接收。

注意:當調用 startActivityForResult(),可以使用顯式Intent,也可以使用隱式Intent。當啟動自己的Activity來接收結果時,應使用顯式Intent確保可收到預期的結果。

接下來看看一個啟動照相機應用的例子。

先添加權限,請求系統硬件的權限,還有保存照片寫入文件的權限。

   
   

簡單的布局,一個Button啟動,一個ImageView顯示圖片




    

然後到MainActivity。首先,當然先初始化組件,在Oncreate()函數中調用startActivityForResult()方法啟動照相機,這個方法需要傳入兩個參數,一個是Intent對象,一個是請求碼,用來標識是哪一個發起的請求。

先定義兩個請求碼

 public static final int TAKE_PHOTO = 1;
 public static final int SHOW_PICTURE = 2;

onCreate方法中,先new一個File用來保存圖片,使用隱式Intent發起請求android.media.action.IMAGE_CAPTURE

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        takePhoto = (Button) findViewById(R.id.take_photo);
        picture = (ImageView) findViewById(R.id.picture);

        takePhoto.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                File outImage = new File(Environment.getExternalStorageDirectory(),"my_image.jpg");

                try {
                    if(outImage.exists()){
                        outImage.delete();
                    }
                    outImage.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                imageUri = Uri.fromFile(outImage);
                Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
                intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

                PackageManager packageManager = getPackageManager();
                List activities = packageManager.queryIntentActivities(intent,PackageManager.MATCH_DEFAULT_ONLY);
                boolean IsIntentSafe = activities.size() > 0;
                if(IsIntentSafe){
                    startActivityForResult(intent, TAKE_PHOTO);
                }

    }

接下來是另一方法onActivityResult,就是用來接收結果、處理結果的方法。
有三個參數
第一個是請求碼,就是上面 startActivityForResult方法中傳入的請求碼。
第二個是結果碼,RESULT_OK表示操作成功, RESULT_CANCELED表示用戶退出或者因為某種原因。
第三個參數就是傳送數據的Intent。

下面第一個處理是裁剪照片,使用startActivityForResult啟動裁剪,Intent傳入的是com.android.camera.action.CROP,請求碼SHOW_PICTURE。操作成功後,第二個處理是顯示照片

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode){
            case TAKE_PHOTO :
                if(resultCode==RESULT_OK){
                    Intent intent = new Intent("com.android.camera.action.CROP");
                    if(data !=null){
                        intent.setDataAndType(data.getData(),"image/*");
                    } else {
                        intent.setDataAndType(imageUri,"image/*");
                    }
                    intent.putExtra("scale", true);
                    intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
                    startActivityForResult(intent,SHOW_PICTURE);
                }
                break;
            case SHOW_PICTURE:
                if(resultCode==RESULT_OK){
                    try {
                        Bitmap bitmap = BitmapFactory.decodeStream(
                                getContentResolver().openInputStream(imageUri)
                        );
                        picture.setImageBitmap(bitmap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                }
                break;
            default:
                break;
        }
    }

添加Intent過濾器

前面講的都是:從你的應用開始另一個應用的Activity。但如果你的應用可以執行對另一個應用有用的操作,應准備好響應來自其他應用的操作請求。 例如,如果構建一款可與用戶的好友分享消息或照片的社交應用,最關注的是支持 ACTION_SEND,以便用戶可以從另一應用發起 “共享”操作並且啟動你的應用執行該操作。

要允許其他應用開始你的Activity,您需要 在相應元素的Manifest文件中添加一個 元素。

當應用安裝在手機上時,系統會識別你的intent-filter並添加信息至所有已安裝應用支持的Intent內部目錄。當其他應用通過隱式Intent調用 startActivity() 或 startActivityForResult() 時,系統會找到可以響應該Intent的Activity。

為了確定哪一種Intent你的應用可以出來,每一個intent filter的添加應該盡可能地指定響應的類型和接收的數據類型。如果Activity具有滿足以下 Intent 對象條件的intent filter,系統可能向Activity發送給定的 Intent:

Action
一個響應操作的字符串名稱。例如ACTION_SEND 或者 ACTION_VIEW 。在intent filter中使用< action >來指定。指定的值一定是響應的完整字符串名稱。

Data
跟intent相關的數據的描述。
在intent filter中使用< data >來指定。標簽中,可以使用一個或多個屬性,但是只能指定MIME類型,URI的前綴,URI的模式或者這些方法的組合和其他一些可接受的數據類型。如果你不需要聲明關於這個數據的Uri(比如當你的activity處理其他的“extra”類型,而不是Uri),應該只指定這個android:mimeType屬性來聲明處理數據的類型。比如text/plain或者 image/jpeg。

1、android:scheme
用於指定數據的協議部分,如 http 部分。
2、 android:host
用於指定數據的主機名部分,如 www.baidu.com部分。
3、android:port
用於指定數據的端口部分,一般緊隨在主機名之後。
4、 android:path
用於指定主機名和端口之後的部分,如一段網址中跟在域名之後的內容。
5、android:mimeType
用於指定可以處理的數據類型,允許使用通配符的方式進行指定。

Category
< category > 標簽則包含了一些附加信息,更精確地指明了當前的活動能夠響應的 Intent中還可能帶有的 category。只有< action >和< category >中的內容同時能夠匹配上 Intent 中指定的 action 和 category 時,這個活動才能響應該 Intent。但是category大部分很少用。所有的隱式intent默認定義為CATEGORY_DEFAULT。
每個Intent中只能指定一個action,但卻能指定多個category。為了能夠接收到其他應用發送過來的隱式intent,必須要在intent filter中包含 CATEGORY_DEFAULT類別 。方法 startActivity() 和 startActivityForResult() 就像聲明 了CATEGORY_DEFAULT 類別那樣處理所有的Intent。

比如,這裡定義了一個activity,可以出來 ACTION_SEND intent,當數據類型是text或者image。


    
        
        
        
        
    

在activity中處理接收的隱式Intent

為了決定在你的Activity執行哪種操作,先讀取用於開始Activity的 Intent。

當你的Activity開始時,調用 getIntent()來獲取一個啟動你的Activity的 Intent。可以在Activity生命周期的任何時間執行此操作,但通常應該在 onCreate() 或 onStart()中執行。

例如:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    // 獲取啟動你的activity的Intent
    Intent intent = getIntent();
    Uri data = intent.getData();

    // 找出這個intent的類型
    if (intent.getType().indexOf("image/") != -1) {
        // 使用image數據處理
    } else if (intent.getType().equals("text/plain")) {
        // 使用text來處理
    }
}

返回結果

如果想要向調用你的應用的Activity返回一個結果,只需調用 setResult() 來指定結果碼和結果的Intent。當操作完成且返回原來的Activity時,調用 finish() 關閉(和destroy)你的Activity。
例如:

Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
setResult(Activity.RESULT_OK, result);
finish();

必須要指定一個結果碼作為結果。通常是RESULT_OK 或者 RESULT_CANCELED,如果需要的話,還可以提供一個裝有數據的Intent。結果碼默認設置為RESULT_CANCELED。所以,如果用戶在完成操作之前和返回你設置的結果之前就點擊返回鍵,那麼原來的activity就會接收到RESULT_CANCELED作為結果。

如果只是簡單地返回一個整數表示其中一個選擇的結果,可以設置結果碼為任何大於0的值。如果想用結果碼傳遞一個整數又不需要包含一個Intent,可以調用setResult()緊緊傳入結果碼。
例如:

setResult(RESULT_COLOR_RED);
finish();

這裡不需要檢查activity是被startActivity() 還是startActivityForResult()啟動的。如果啟動你的activity的inten可能期待返回一個結果,那麼就簡單地調用setResult()。如果原來的activity已經調用了startActivityForResult(),那麼系統會傳送你提供給setResult()的結果。否則,結果會被忽略掉。

到這裡,結合之前照相機的例子想一想,也明白了大概的流程了。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved