`

Design TIps for Android Application Designers

阅读更多

Design Tips

The following are tips and guidelines for application designers and developers.

When writing an activity that won't be re-used, don't specify intent filters — use explicit intents

If you're writing an activity that you don't want other activities to use, be sure not to add any intent filters to that activity. This applies to an activity that will be launched only from the application launcher or from other activities inside your application. Instead, just create intents specifying the explicit component to launch — that is, explicit intents. In this case, there's just no need for intent filters. Intent filters are published to all other applications, so if you make an intent filter, what you're doing is publishing access to your activity, which means you can cause unintentional security holes.

<!-- <h3 id="others_to_reuse_tip">When writing an activity for others to re-use, don't define your own URI schemes</h3> <p> If publishing to others, don't define your own URI schemes in an Intent type. Schemes (such as http: and mailto:) are an Internet standard with a universal namespace outside of just Android, so you aren't allowed to just make up your own. Instead, you should just define your own actions. The action namespace is designed to not have conflicts. Typically, your activity has one scheme with many different actions. </p> <p> Example: You want to show the user a bar code for some text. The wrong way to do this is for the intent filter protocol to be &lt;action android:name="android.intent.action.VIEW" /&gt; and &lt;data android:scheme="barcode" /&gt;. Do not do this. </p> <p> Instead you should define &lt;action android:name="com.example.action.SHOW_BARCODE" /&gt; and have the invoker supply the data as an extra field in the Intent object. </p> <p> Be aware this intent filter protocol ("com.example.action.SHOW_BARCODE", in this example) is a public API that you can't change once it's defined. You must support it in the future because others are going to be relying on it. If you want to add new features that are incompatible with the current protocol, just define a new protocol and continue to support the old one. </p> -->

When reusing an activity owned by others, handle the case where no activity matches

Your applications can re-use activities made available from other applications. In doing so, you cannot presume your intent will always be resolved to a matching external activity — you must handle the case where no application installed on the device can handle the intent.

You can either test that an activity matches the intent, which you can do before starting the activity, or catch an exception if starting the activity fails. Both approaches are described in the blog posting Can I use this Intent?.

To test whether an intent can be resolved, your code can query the package manager. The blog post provides an example in the isIntentAvailable() helper method. You can perform this test when initializing the user interface. For instance, you could disable the user control that initiates the Intent object, or display a message to the user that lets them go to a location, such as the Market, to download its application. In this way, your code can start the activity (using either startActivity() or startActivityForResult()) only if the intent has tested to resolve to an activity that is actually present.

Consider how you want your activities to be launched or used by other applications

As a designer or developer, it's up to you to determine how users start your application and the activities in it. As an application is a set of activities, the user can start these activities from Home or from another application.

  • Launch your main activity from an icon at Home - If your application can run standalone, it should probably be started by the user touching an icon in application launcher (typically implemented as a sliding drawer on the Home screen), or from a shortcut icon on the Home screen, or from the task switcher. (The mechanism for this is for the activity to have an intent filter with action MAIN and category LAUNCHER.)
  • Launch your activity from within another application - Perhaps your activities are meant for re-use. For example, many applications have data they want to share with other users. Activities that can share data with other users include email, text messaging and uploading to a public website.

    If one or more of your activities can be an alternative to an existing activity in another application, you can make it available to users at the point they request that activity. For example, if your activity can send data to others (such as by email, text messaging, or uploading), consider setting up that activity to appear as a choice to the user. To give a specific example, Gallery enables a user to view and share pictures. When the user chooses "Share" from the menus, the system compares the "Share" request (an Intent object) to available activities (by looking at their intent filters) and displays choices to share. In this case, it matches Email, Gmail, Messaging and Picasa. If your activity can send a picture or upload it to a website, all it needs to do is make itself available for sharing (by setting its intent filter).

    Another activity can start your activity either with or without expecting a result back. 

    • Start an activity expecting a result - This approach is closed loop, where the activity being started must either return a valid result or be canceled. In the previous examples of sharing a photo from a Gallery, the user ends up back in the Gallery after completing the send or upload procedure. These are examples of starting an activity external to the Gallery. (Such an activity is started with startActivityForResult().)
    • Start an activity not expecting a result - This approach is open-ended. An example is choosing an house address in an email message (or web page), where the Maps activity is started to map the location. No result from maps is expected to be returned to the email message; the user can return by pressing the BACK key. (Such an activity is started with startActivity().)
  • Launch your activity only from within another application - The previous cases of sharing by way of Email, Gmail, Messaging and Picasa (from within Gallery) are all activities that can also be started from icons in the application launcher at Home. In contrast, the activities for cropping a picture and attaching a file cannot be started from Home, because they do not stand alone and require a context. 

    In fact, not all applications have icons and can be started from Home. Take for example a small app that is infrequently used and replaces existing functionality, that already has a natural entry point inside an existing application. For example, an Android phone typically has a built-in ringtone picker that can be selected from the sound settings of the Settings application. A custom ringtone picker application that you write could be launched by an intent identical to the built-in ringtone picker. At the point where the user chooses "Phone ringtone", they are presented with a dialog letting them choose between "Android System" and your ringtone picker (and letting them save their choice) as shown in the following figure. A ringtone is something you set infrequently, and already has a well-defined starting point, so probably does not need an application icon at Home.

  • Launch two or more main activities within a single application from separate icon at Home - As we have defined it, all the code in a single .apk file is considered to be one application. You can write an application that contains two main activities launchable from Home.

    The Camera.apk application is a good example of an application that contains two independent main activities — Camera and Camcorder — that each have their own icons in application launcher, that can be launched separately, and so appear to the user as separate applications. They both share use of the same lens, and both store their images (still and moving) in the Gallery. 

    In order for your application to contain two different, independent activities launchable from Home, you must define them to be associated with different tasks. (This means setting the main activity for each task to a different <!--a href=#affinities title=affinity-->task affinity<!--/a--> — in this case, "com.android.camera" and "com.android.videocamera".)

    Contacts and Dialer are another example of two main activities launchable from Home that reside in the same application.

  • Making your application available as a widget - An application can also display a portion of itself as an app widget, embedded in Home or another application, and receive periodic updates.

Allow your activities to be added to the current task

If your activities can be started from another application, allow them to be added to the current task (or an existing task it has an affinity with). Having activities added to a task enables the user to switch between a task that contains your activities and other tasks. <!--See <a href="#tasks" mce_href="#tasks" title=Tasks>Tasks</a> for a fuller explanation.-->Exceptions are your activities that have only one instance. 

For this behavior, your activity should have a <!--a href=launch_modes title="standard or singleTop"-->launch mode<!--/a--> of standard or singleTop rather than singleTask or singleInstance. These modes also enable multiple instances of your activity to be run.

Notifications should let the user easily get back to the previous activity

Applications that are in the background or not running can have services that send out notifications to the user letting them know about events of interest. Two examples are Calendar, which can send out notifications of upcoming events, and Email, which can send out notifications when new messages arrive. One of the user interface guidelines is that when the user is in activity A, gets a notification for activity B and picks that notification, when they press the BACK key, they should go back to activity A. 

The following scenario shows how the activity stack should work when the user responds to a notification.

  1. User is creating a new event in Calendar. They realize they need to copy part of an email message into this event
  2. The user chooses Home > Gmail
  3. While in Gmail, they receive a notification from Calendar for an upcoming meeting
  4. So they choose that notification, which takes them to a dedicated Calendar activity that displays brief details of the upcoming meeting
  5. The user chooses this short notice to view further details
  6. When done viewing the event, the user presses the BACK key. They should be taken to Gmail, which is where they were when they took the notification

This behavior doesn't necessarily happen by default.

Notifications generally happen primarily in one of two ways:

  • The chosen activity is dedicated for notification only - For example, when the user receives a Calendar notification, choosing that notification starts a special activity that displays a list of upcoming calendar events — this view is available only from the notification, not through the Calendar's own user interface. After viewing this upcoming event, to ensure that the user pressing the BACK key will return to the activity the user was in when they picked the notification, you would make sure this dedicated activity does not have the same task affinity as the Calendar or any other activity. (You do this by setting task affinity to the empty string, which means it has no affinity to anything.) The explanation for this follows.

    Because of the way tasks work, if the taskAffinity of the dedicated activity is kept as its default, then pressing the BACK key (in step 6, above) would go to Calendar, rather than Gmail. The reason is that, by default, all activities in a given application have the same task affinity. Therefore, the task affinity of the dedicated activity matches the Calendar task, which is already running in step 1. This means in step 4, choosing the notification brings the existing Calendar event (in step 1) forward and starts the dedicated activity on top of it. This is not what you want to have happen. Setting the dedicated activity's taskAffinity to empty string fixes this.

  • The chosen activity is not dedicated, but always comes to the foreground in its initial state - For example, in response to a notification, when the Gmail application comes to the foreground, it always presents the list of conversations. You can ensure this happens by setting a "clear top" flag in the intent that the notification triggers. This ensures that when the activity is launched, it displays its initial activity, preventing Gmail from coming to the foreground in whatever state the user last happened to be viewing it. (To do this, you put FLAG_ACTIVITY_CLEAR_TOP in the intent you pass to startActivity()).

There are other ways to handle notifications, such as bringing the activity to the foreground, set to display specific data, such as displaying the text message thread for the person who just sent a new text message.

A notification always starts an activity as a new task (that is, it puts FLAG_ACTIVITY_NEW_TASK in the intent it passes to startActivity()). This is done because interruptions to a task should not become part of that task.

Use the notification system — don't use dialog boxes in place of notifications

If your background service needs to notify a user, use the standard notification system — don't use a dialog or toast to notify them. A dialog or toast would immediately take focus and interrupt the user, taking focus away from what they were doing: the user could be in the middle of typing text the moment the dialog appears and could accidentally act on the dialog. Users are used to dealing with notifications and can pull down the notification shade at their convenience to respond to your message.

Don't take over the BACK key unless you absolutely need to

As a user navigates from one activity to the next, the system adds them to the activity stack. This forms a navigation history that is accessible with the BACK key. Most activities are relatively limited in scope, with just one set of data, such as viewing a list of contacts, composing an email, or taking a photo. But what if your application is one big activity with several pages of content and needs finer-grained control of the BACK key? Examples of such Google applications are the Browser, which can have several web pages open at once, and Maps, which can have several layers of geographic data to switch between. Both of these applications take control of the BACK key and maintain their own internal back stacks that operate only when these applications have focus.

For example, Maps uses layers to present different information on a map to the user: displaying the location of a search result, displaying locations of friends, and displaying a line for a street path providing direction between points. Maps stores these layers in its own history so the BACK key can return to a previous layer.

Similarly, Browser uses browser windows to present different web pages to the user. Each window has its own navigation history, equivalent to tabs in a browser in a desktop operating system (such as Windows, Macintosh or Linux). For example, if you did a Google web search in one window of the Android Browser, clicking on a link in the search results displays a web page in that same window, and then pressing BACK would to the search results page. Pressing BACK goes to a previous window only if the current window was launched from that previous window. If the user keeps pressing back, they will eventually leave the browser activity and return Home

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics