Getting Started-----Interacting with Ohter Apps

       一个Android应用一般都有若干个activities。每个activity展示一个用户界面,用于执行特定的用户任务(例如浏览地图或者拍照)。为了让用户从一个activity跳到另一个activity。你的app必须使用一个 <a href="http://developer.android.com/reference/android/content/Intent.html" style="color: #258aaf;">Intent</a>定义你的app想要做某事的“意图”。当你通过调用如 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivity(android.content.Intent)" style="color: #258aaf;">startActivity()</a>这样的方法传递一个Intent给系统时,系统使用Intent标识和开启合适的应用组件。使用intent甚至可以让你启动其他的app里的activity。

        为了启动特定的组件(例如一个特定的Activity实例)intent可以是显示的,也可以是隐式的,为了启动任何可以处理该意图action(例如拍照)的组件。

        本文告诉你如何使用Intent执行一些和其他的app交互的基本操作,例如启动另一个应用,从该app接受返回的结果,和响应来自于其他app的intent。

        Lessons

         Sending the User to Another App

              如何产生隐式意图,启动能执行该action的其他的app

        Getting a Result from an Activity

             如何启动另一个activity,并从该activity获取返回结果

        Allowing Other Apps to Start Your Activity

             如何定义一个你的app接受的隐式意图的意图过滤器使得你的app里的activity开发给其他应用使用

           

        Sending the User to Another App

          Android最重要的特性之一就是基于“action”用户能从一个app跳到另一个app。例如,你的app有一个想要显示在地图的地址,你不必在你的app里创建一个显示地图的activity。你可以产生一个请求展示该地址的意图。然后,Android系统会启动一个能将该地址显示到地图上的app。

        如Building Your First App里所解释的,你必须使用intent在你的app里的activity间切换。你一般通过显示意图(定义了你想要启动的组件的确切的类名)实现同一个app里的activity间的切换。然而,当你想要其他的app处理你的app的intent时,例如浏览一个地图,你必须使用隐式意图。

        本文讲述如何产生特定操作的隐式意图,以及如何使用隐式意图启动其他的app里的activity来处理你的隐式意图。

        

        Build an implicit Intent

        隐式意图并不需要声明启动的组件类名,而是声明一个执行的动作(action)。该action描述了你想做的事情,例如,浏览、编辑、发送或者获取一些数据。intent也可以通过action来附加要传递的数据,例如你想要访问的地址、或者你想要发送的email信息。根据你想要创建的intent,数据可能是一个Uri,或者其他的数据类型,也可能不包含任何数据。

        如果你的数据是是一个Uri,那么你可以简单的调用Intent()构造方法来定义action和数据。

        例如,下面是如何定义一个intent来打电话,并用Uri来表示电话号码。

        

Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
         当你的应用调用startActivity()来启动该Intent时,电话应用程序就会向指定的电话号码打电话。

        以下是一些其他的intent,和action和Uri映射对。

  •     浏览地图
// Map point based on address
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
// Or map point based on latitude/longitude
// Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
 
  •  访问一个Web网页

     

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

        其它的隐式意图需要“附加的”的其他的数据,比如字符串,你可以使用不同的putExtra()方法来添加一个或者多个“附加”数据。

       默认地,系统通过添加到intent里的Uri数据类型累决定适合的MIME类型,如果你的Intent里没有Uri,你应该使用setType()方法来明确intent里的数据类型。更进一步设置MIME类型明确了哪一类的activity解释和处理该意图。

        下面是一些intent的例子,通过添加额外的数据来明确期望的action:

  • 发送带附件的邮件:
    Intent emailIntent = new Intent(Intent.ACTION_SEND);
    // The intent does not have a URI, so declare the "text/plain" MIME type
    emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
    emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"}); // recipients
    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"));
    // You can also attach multiple items by passing an ArrayList of Uris 
  • 产生日历事件:
    Intent calendarIntent = new Intent(Intent.ACTION_INSERT, Events.CONTENT_URI);
    Calendar beginTime = Calendar.getInstance().set(2012, 0, 19, 7, 30);
    Calendar endTime = Calendar.getInstance().set(2012, 0, 19, 10, 30);
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis());
    calendarIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis());
    calendarIntent.putExtra(Events.TITLE, "Ninja class");
    calendarIntent.putExtra(Events.EVENT_LOCATION, "Secret dojo");

        注:仅仅API14或者更高的API支持日历时间的intent。

        注:把你的intent定义的尽可能明确是什么重要的。例如,如果你想要展示一个image,你可以使用ACTION_VIEW,同时你也应该指定MIME类型为 image/*,这防止能浏览其他类型数据的app(例如地图应用)被该intent出发和调起。

        Verify There is an App to Receive the Intent

         虽然Anroid平台确保特定的intent会被内建app中(例如Phone、Email或者日历应用)的一个处理,你也应该总是在使用前确认有app能处理你的意图。

        注意:如果你的发起一个intent,但android设备上没有可以处理你的intent的app,你的app将crash。

        为了确保有一个activity能响应的意图,调用queryIntentActivities()方法获得一个能处理你的intent的activity列表。如果返回的list非空,你能安全地使用该intent。例如:

        

PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
         如果isIntentSafe是true,那么至少一个app将响应该intent,如果为false,那么没有任何app处理该intent。

        注:你应该在你的activity第一次启动时就执行该检查。以防you need to disable the feature that uses the intent before the user attempts to use it。如果你知道一个特点的app能处理该intent,你也能提供一个链接让用户去下载该app(参见如何 link to your product on Google Play)。

       

        Start an Activity with the Intent

        一旦你已产生了你的Intent并设置了额外的信息,你能调用startActivity()将该intent发送给系统。如果系统发现不止一个activity能处理该intent,系统将展示一个选择对话框来供用户选择一个app来处理,如图1.如果有仅仅一个activity能处理,系统立即的启动它。

        
Getting Started-----Interacting with Ohter Apps
         如下是一个复杂点的例子,显示了如何产生一个intent来浏览地图,确保一个存在一个app处理该intent,然后开启它:

        

// Build the intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);

// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;

// Start an activity if it's safe
if (isIntentSafe) {
    startActivity(mapIntent);
}

         当你嗲用startActivity()方法来传递Intent,而且有许多应用匹配该Intent时,用户可以选择一个默认程序(通过选中如图1的对话框底的一个checkbox)来处理该意图。如果用户每次都想用同一个app来执行某个action时这是十分好的用户体验。

        然而,如果一个action能被多个app执行,而用户每次都希望不同的app来执行——例如一个“share”操作用户可能需要一个分享item分享到不同的app——这时你需要明晰的展示一个选择对话框,如图2。该选择对话框迫使用户每次选择一个app来执行action(用户不能选择一个默认app来处理intent)。

                 
Getting Started-----Interacting with Ohter Apps
 

        为了显示chooser,使用createChooser()来产生Intent,作为参数传递该Intent给startActivity()。例如:

        

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

// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);

// Verify the intent will resolve to at least one activity
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

        如上,把Intent传入createChooser()方法,从而显示了一个应用程序列表对话框,并将提供的文字作为对话框标题。

        

     Getting a Result from an Activity

       启动另一个activity不止一种方式。你也能启动一个Activity并接收一个返回结果。为了接收一个结果调用 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent,%20int)" style="color: #258aaf;">startActivityForResult()</a> (而不是startActivity())。

        例如,你的app能启动一个照相机应用,接收一个拍摄的照片作为返回结果。或者,你可能要启动一个联系人应用来选择一个联系人,你将接收联系人详情作为返回结果。

        当然,响应的activity必须被设计为能返回一个结果。当activity能返回结果时,它通过另一个intent对象来发送返回结果。你的activity能在onActivityResult()回调方法里接收到该结果。

        注:当你调用<a href="http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent,%20int)" style="color: #258aaf;">startActivityForResult()</a>时你能使用显示或者隐式意图。当使用你自己的activity接收结果数据时,你应该使用显式意图确保你的接收到期望的结果。

       Start the Activity

        当启动有返回结果的activity时,所用的intent并没有什么特别的。但是,你需要传递一个额外的整型参数给startActivityForResult()方法。

        整型参数是一个请求码,用于标识你的请求。当你接收到结果意图时,回调提供相同的请求码以便你的app能合理的区分属于你的结果并确定如何处理该结果。

        例如,如下显示一个如何开启选择联系人的activity:

        

static final int PICK_CONTACT_REQUEST = 1;  // The request code
...
private void pickContact() {
    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
    pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
    startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}

       

        Receive the Result

        当后续的Activity执行完成并返回结果时,系统将调用前面的Activity的onActivityResult()方法。该方法包含三个参数:

  •  你传递给 <a href="http://developer.android.com/reference/android/app/Activity.html#startActivityForResult(android.content.Intent,%20int)" style="color: #258aaf;">startActivityForResult()</a>的请求码。
  •  第二个Activity指定的结果码。如果操作成功结果码将是RESULT_OK,如果由于某些原因用户退出或者操  作失败,其值是RESULT_CANCELED
  •  携带返回结果数据的Intent。

        例如,如下显示了如何处理选择联系人intent的返回结果:

        

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request we're responding to
    if (requestCode == PICK_CONTACT_REQUEST) {
        // Make sure the request was successful
        if (resultCode == RESULT_OK) {
            // The user picked a contact.
            // The Intent's data Uri identifies which contact was selected.

            // Do something with the contact here (bigger example below)
        }
    }
}

         该例子里,由Android的Contract或者People应用返回的结果Intent提供了一个内容Uri,该Uri标识了用户选择的联系人。  

        为了成功里处理结果,你必须理解结果意图的格式是什么。当返回的结果的activity是你的应用里自己写的activity时,很容易知道是什么格式。然而,Android平台里的app提供了属于自己的APIs,它们有自己特定结果数据。例如,People应用(在一些旧版本的Android平台上是Contact应用)始终返回标识选定联系人的内容Uri,相机应用返回一个Bitmap,该Bitmap放在附加的“data”里(参见类 Capturing Photos)。

        

       Bouns:Read the contact data

        上面的代码显示了如何从联系人应用中获取一个返回结果,但并没有涉及从返回结果中获取联系人的获取细节,因为这需要一些有关 content providers的更进一步的知识。然而,如果你是好奇的,下面是如何从返回的联系人结果数据中获取电话号码的代码:

        
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Check which request it is that we're responding to
    if (requestCode == PICK_CONTACT_REQUEST) {
        // Make sure the request was successful
        if (resultCode == RESULT_OK) {
            // Get the URI that points to the selected contact
            Uri contactUri = data.getData();
            // We only need the NUMBER column, because there will be only one row in the result
            String[] projection = {Phone.NUMBER};

            // Perform the query on the contact to get the NUMBER column
            // We don't need a selection or sort order (there's only one result for the given URI)
            // CAUTION: The query() method should be called from a separate thread to avoid blocking
            // your app's UI thread. (For simplicity of the sample, this code doesn't do that.)
            // Consider using CursorLoader to perform the query.
            Cursor cursor = getContentResolver()
                    .query(contactUri, projection, null, null, null);
            cursor.moveToFirst();

            // Retrieve the phone number from the NUMBER column
            int column = cursor.getColumnIndex(Phone.NUMBER);
            String number = cursor.getString(column);

            // Do something with the phone number...
        }
    }
}
         注:Android2.3(API 9)之前,在<a href="http://developer.android.com/reference/android/provider/ContactsContract.Contacts.html" style="color: #258aaf;">Contacts Provider</a> (如上所示)上执行查询时,要求你的app声明 <a href="http://developer.android.com/reference/android/Manifest.permission.html#READ_CONTACTS" style="color: #258aaf;">READ_CONTACTS</a>权限(参见Security and Permissions)。然而,从Android2.3开始,Contract/People应用给你的app提供一个从Contract Provider读取信息的临时权限。但这个临时权限仅适用于所请求的特定联系人,除非你声明READ_CONTACTS权限,否则不能查询一个在那个intent Uri定义之外的联系人信息。

相关推荐