免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3153 | 回复: 0
打印 上一主题 下一主题

Android Activity and sub-Activity [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-11-26 13:07 |只看该作者 |倒序浏览
Introduction
This tutorial will show you how to create a sub-Activity from a
calling-Activity, and process the results produced by the sub-Activity, if you
want to do so. Also, the various ways of launching a sub-Activity are covered,
along with the Android Activity history stack. A subclass of Activity is also
provided that makes it trivial to launch sub-Activities and respond to results
from them.
Android 101
To get started with Android,
click here
. Instead of
reading the SDK docs to get the basics, I recommend that you read
this book
to get going.
What’s an activity?
An activity is the equivalent of a Frame/Window in GUI toolkits. It takes up
the entire drawable area of the screen (minus the status and title bars on top).
An activity is what gets bound to the AndroidManifest.xml as the main entry
point into an application. For long running tasks, it’s best to use a service
that talks to this activity.
Service
Activities are meant to display the UI and get input from the user. However,
long running tasks are not meant to be spawned in a activity, since they can be
frozen when the focus is switched away from them (by the user or incoming phone
call or system event). Services on the other hand keep running for the duration
of the user’s ‘session’ on the device.
sub-Activity
An Activity (calling-Activity) can spawn another Activity (sub-Activity) in 2
ways:
  • Fire and forget – create an event (Intent) and fire it
  • Async callback – create an event (Intent), fire it, and wait for it’s
    response in a callback method (of the calling-Activity).
    Here

    are more details on spawning new “screens” or Activities from the Android SDK
    documentation.
    Here’s some sample code of an Intent being created and a sub-Activity (called
    WidgetActivity) being launched in response to a button being pressed (a detailed
    analysis of this is provided in the sections below) in the MainActivity
    class:
        b4.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            Intent i = new Intent(ctx, WidgetActivity.class);
            Log.i(Global.TAG, "b4 pressed - about to launch sub-activity");
            // the results are called on widgetActivityCallback
            ctx.startSubActivity(i, Global.WIDGET_REQ_CODE);
            Log.i(Global.TAG, "b4 pressed - sucessfully launched sub-activity (startSubActivity called)");
          }
        });
    Activity history stack
    Please note that Android maintains a history stack of all the Activities that
    have been spawned in an application’s Linux process. In the sample code above,
    the calling-Activity simply provides the class name of the sub-Activity. When
    the sub-Activity finishes, it puts it’s result code and any data back on the
    stack and finishes itself. Since Android maintains the stack, it knows which
    calling Activity to pass the result back to. The calling-Activity has an
    onActivityResult method that handles all the callbacks from the sub-Activities.
    This is pretty confusing at first, since to keep it all straight we have to use
    CorrelationIds (more on that below).
    1. Fire and forget
    An Intent is just an event. It can have a target of an Activity class along
    with some data that’s passed in via a Bundle (which is like a Hashtable).
    Fire/forget is easy. Just create an Intent (with a reference to the sub-Activity
    class), and call startActivity(intent, correlationId) and this will launch the
    sub-Activity. This correlationId (requestCode) is used to identify which
    sub-Activity called the callback method (which happens when doing the async
    callback). So it’s not really needed in the fire-forget scenario.
    2. Async callback, and correlationId
    The calling-Activity has to provide a correlationId/request code to the
    Intent/event, before firing/raising it. This is then used by the sub-Activity to
    report it’s results back to the calling-Activity when it’s ready. The
    calling-Activity does not stop when it spawns the sub-Activity (it does not wait
    for the callback to happen to become unblocked). Please note that this
    sub-Activity is not "modal", that is, the calling Activity does not block, when
    startSubActivity() is called. So if you’re thinking that this is like a modal
    dialog box, where the calling-Activity will wait for the sub-Activity to produce
    a result, then you have to think of it differently.
    The sub-Activity is an Activity. It has to have an onCreate() method to build
    the UI. The only difference is that once it’s complete, it has to report it’s
    results to the caller by doing the following:
  • setResult(resultCode, strdata, bundledata) – this is how results are sent to
    the caller. The resultCode is either RESULT_CANCELED or RESULT_OK. Note that you
    don’t have to identify which calling-Activity this result goes to, and you don’t
    have to identify what correlation ID/requestCode to use; these are inferred by
    Android since it keeps a history stack of Activity objects around. Also note
    that you have to provide the correlationID when you created the Activity that
    launches the sub-Activity… so this is how Android knows how to pass the results
    to the calling-Activity with the correct correlationID. Whew!


  • finish() – equivalent of calling return… it sends control back to the
    caller. Note that the calling-Activity never blocked, this is not "modal"
    behavior. finish() just reclaims the resources allocated for the sub-Activity,
    and removes it from the Activity history stack (for this application process).
    On the calling-Activity side, the method that gets called when the
    sub-Activity completes (with result or error, with or without data) is
    onActivityResult(…). This method contains the result code, and any data that’s
    set by the sub-Activity. If there are any errors, these get reported there as
    well. There’s a special result code that is passed if there are any problems –
    RESULT_CANCELED. If the sub-Activity is launched, and then you press the "Back
    button" on the emulator then RESULT_CANCELED is passed on your behalf, and
    finish() is automatically called. Note that you can pass parameters back to the
    calling-Activity (via a Bundle) when you cancel the sub-Activity. Here’s some
    sample code that does this when a button is pressed:
        b2.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            Bundle map = new Bundle();
            map.putString("de", "nied");
            activity.returnErrorToCaller(map);
            Log.i(Global.TAG,
                  "cancel button pressed, calling returnToCaller with bundle" + map.toString());
          }
        });/** call this to return control to the calling activity - {@link MainActivity#onActivityResult} */
    public void returnErrorToCaller(Bundle bundle) {
      // sets the result for the calling activity
      setResult(RESULT_CANCELED, null, bundle);
      // equivalent of 'return'
      finish();
    }
    Note that when you press the "Back button" on the emulator, the
    RESULT_CANCELED is sent back to the calling-Activity, but no parameters are
    passed, they are null.
    Intent and sub-Activity details
    When creating an Intent (event) that will be used to create a new
    sub-Activity, you can pass the class of this Activity as a parameter to the
    Intent. Here’s the code to create an Intent:
            Intent i = new Intent(ctx, WidgetActivity.class);
            Log.i(Global.TAG, "b4 pressed - about to launch sub-activity");
            // the results are called on widgetActivityCallback
            ctx.startSubActivity(i, Global.WIDGET_REQ_CODE);
    When you create this new sub-Activity, you have to register it in the
    AndroidManifest.xml file. Here’s an example:
    "1.0" encoding="utf-8"?>
    "http://schemas.android.com/apk/res/android"
        package="com.devlife">
        "@drawable/stlogo">
            ".mainactivity.MainActivity" android:label="@string/app_name">
                
                    "android.intent.action.MAIN" />
                    "android.intent.category.LAUNCHER" />
                
            
          ".subactivity.WidgetActivity" android:label="@string/widgetactivity_name"/>
       
    Here are a few notes on this AndroidManifest.xml file:
  • The name of the sub-Activity defined by the "android:name" attribute, has to
    be the name of the class (in this case
    "com.devlife.subactivity.WidgetActivity").
  • If you’ve specified a root package attribute for the manifest tag, then you
    have to precede this class name with a "."; if you don’t have a root package
    attribute defined, then just insert the fully qualified name of the class.
  • In the example above, I have a package attribute defined as:
    package="com.devlife". Android uses this name field to do a class lookup when
    you create an Intent and point it to a .class object.
    Processing the result from the sub-Activity in the calling-Activity (cancel
    or ok)
    When creating the Intent, to launch this sub-Activity, you have to pass it a
    correlation ID, which will be used by the calling-Activity to determine what
    code is run in the onActivityResult() method. This correlation ID is called a
    request code, and it’s necessary due to the fact that one Activity might spawn
    more than one sub-Activity, and if these sub-Activities send a result code (and
    some data) back to the calling-Activity, then there has to be a way to sort out
    who sent what. This callback mechanism is pretty clunky, and a bit confusing.
    The implementation of the onActivityResult() method has to have a switch
    statement with case statements for each correlation ID. It makes sense to create
    a global static class that holds these correlation IDs, that are implicitly
    mapped to each of your sub-Activity classes. Here’s an example of the callback
    implementation in the calling-Activity MainActivity:
    @Override protected void onActivityResult(int requestCode,
                                              int resultCode,
                                              String strdata,
                                              Bundle bundle)
    {
      Log.i(Global.TAG, "MainDriver main-activity got result from sub-activity");
      if (resultCode == Activity.RESULT_CANCELED) {
        Log.i(Global.TAG2,
              "WidgetActivity was cancelled or encountered an error. resultcode == result_cancelled");
        Log.i(Global.TAG2,
              "WidgetActivity was cancelled - data =" + bundle);
      }
      else
        switch (requestCode) {
          case Global.WIDGET_REQ_CODE:
            widgetActivityCallback(resultCode, bundle);
            break;
        }
      Log.i(Global.TAG, "MainDriver main-activity got result from sub-activity");
    }
    In addition the correlation ID, a result code is passed from the sub-Activity
    to calling-Activity. It’s strange that the sub-Activity has to "kill itself" in
    order for this result to pass back to the caller. This exchange is very clunky
    as well. Here’s an example of a sub-Activity passing data, result code, and
    correlation ID back to the calling-Activity when a button is pressed; please
    note that the calling Activity is implicitly determined (not explicitly, since
    Android maintains a history stack of Activities in the application’s
    process:
        b.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            Bundle map = new Bundle();
            map.putString("monkey", "donkey");
            activity.returnToCaller(map);
            Log.i(Global.TAG, "button pressed, calling returnToCaller with bundle" + map.toString());
          }
        });/** call this to return control to the calling activity - {@link MainActivity#onActivityResult} */
    public void returnToCaller(Bundle bundle) {
      // sets the result for the calling activity
      setResult(RESULT_OK, null, bundle);
      // equivalent of 'return'
      finish();
    }
    Keep in mind that if the "Back button" is pressed, while the sub-Activity is
    visible, then a RESULT_CANCELED resultCode is passed to the onActivityResult()
    method of the calling-Activity. Also, the bundle parameter is null in this case.
    So there are 3 ways to return control back to the calling-Activity:
  • Pass RESULT_OK, with or without some return value (passed as a Bundle or
    String) - in this example a Bundle is  used ({"monkey"="donkey"}).
  • Pass RESULT_CANCELED, with or without some return value; in this example a
    Bundle is passed back to the calling-Activity ({"de"="nied]").
  • Press the "Back button" which will pass RESULT_CANCELED, with null bundle
    and string parameter to the calling-Activity.
    Summary - Return to sender… err… caller?
    As you have probably inferred by now, the sub-Activity doesn’t need to know
    anything about it’s caller. The calling-Activity has to provide a correlationId
    and a class name to the Intent that launches the sub-Activity. This
    correlationId is then used by the calling-Activity to figure out what to do with
    the result (and which sub-Activity produced these results).
    In the code example above, you don’t really need to know what the calling
    Activity’s class is… since it just fires an Intent to launch the sub-Activity.
    When the sub-Activity finishes, the results are sent back to the
    calling-Activity implicitly by Android itself. This is why I only showed the
    calling-Activity (in my example MainActivity) fire the Intent and process the
    callback with results.
    Is there a better way? Yes, there is!
    Instead of dealing with the clunky requestCode/correlationId, I decided to
    create a subclass of Activity that would make launching sub-Activities, and
    dealing with their result (OK or CANCELED) trivial. Here’s the class that makes
    it all very simple, it’s called SimpleActivity:
    /**
    * SimpleActivity is a subclass of Activity that makes it trivial to create a sub-Activity, and handle it's
    * results (ok or cancel). No need to deal with requestCodes, since this class handles creating correlationIds
    * automatically.
    *
    * @author Nazmul Idris
    * @version 1.0
    * @since Jul 3, 2008, 12:08:46 PM
    */
    public class SimpleActivity extends Activity {
    /** holds the map of callbacks */
    protected HashMap _callbackMap = new HashMap();
    /** use this method to launch the sub-Activity, and provide a functor to handle the result - ok or cancel */
    public void launchSubActivity(Class subActivityClass, ResultCallbackIF callback) {
      Intent i = new Intent(this, subActivityClass);
      Random rand = new Random();
      int correlationId = rand.nextInt();
      _callbackMap.put(correlationId, callback);
      startSubActivity(i, correlationId);
    }
    /**
    * this is the underlying implementation of the onActivityResult method that handles auto generation of
    * correlationIds and adding/removing callback functors to handle the result
    */
    @Override protected void onActivityResult(int correlationId, int resultCode, String paramStr, Bundle paramMap) {
      try {
        ResultCallbackIF callback = _callbackMap.get(correlationId);
        switch (resultCode) {
          case Activity.RESULT_CANCELED:
            callback.resultCancel(paramStr, paramMap);
            _callbackMap.remove(correlationId);
            break;
          case Activity.RESULT_OK:
            callback.resultOk(paramStr, paramMap);
            _callbackMap.remove(correlationId);
            break;
          default:
            Log.e(Global.TAG3, "Couldn't find callback handler for correlationId");
        }
      }
      catch (Exception e) {
        Log.e(Global.TAG3, "Problem processing result from sub-activity", e);
      }
    }
    /**
    * ResultCallbackIF is a simple interface that you have to implement to handle results - ok or cancel from a
    * sub-Activity.
    *
    * @author Nazmul Idris
    * @version 1.0
    * @since Jul 3, 2008, 12:11:31 PM
    */
    public static interface ResultCallbackIF {
      public void resultOk(String resultString, Bundle resultMap);
      public void resultCancel(String resultString, Bundle resultMap);
    }//end interface ResultCallbackIF
    }//end class SimpleActivity
    Here’s some sample code to show you how clean it is to use SimpleActivity,
    instead of the default stuff; this code (it’s a subclass of SimpleActivity)
    spawns a new sub-Activity in response to a button press, and the callback to
    handle the results is passed in-line, with the call to spawn the
    sub-Activity:
        // setup b2 to spawn a subactivity - TableActivity
        b2.setOnClickListener(new View.OnClickListener() {
          public void onClick(View view) {
            Log.i(Global.TAG3, "b2 pressed - about to launch sub-activity");
            ctx.launchSubActivity(TableActivity.class,
                                  new SimpleActivity.ResultCallbackIF() {
                                    public void resultOk(String str, Bundle result) {
                                      Log.i(Global.TAG3, "subactivity completed successfully, result=" + result);
                                    }
                                    public void resultCancel(String str, Bundle result) {
                                      Log.i(Global.TAG3, "subactivity was cancelled, result=" + result);
                                    }
                                  });
            Log.i(Global.TAG3, "b2 pressed - sucessfully launched sub-activity (startSubActivity called)");
          }
        });
    No need to mess with correlationIds, or overriding onActivityResult in the
    calling-Activity. Simple, elegant, and it works


    .
    Feedback and comments
    To share your thoughts on this tutorial
    click here
    .
    Further reading in related categories
    Android
    Android WebView
    (WebKit)
    This article shows you the limitations and capabilities of the WebView
    component. You will see how to download files from the network and use them in
    HTML, as well as assets loaded in the APK file.
    Android Service creation
    and consumption
    This tutorial will show you how to create a simple service, that does not
    use IPC (inter process communication). Services are great for running long
    running tasks and business logic, outside an Activity, which is tied to the user
    interface. For example, if you have a background task that has to download data
    periodically, then you should put that task in a Service. You can explicitly
    start a service and stop it as well. With IPC you can connect to a running
    service and call methods on it, however, in this example, I won't be using any
    IPC; instead all data transfer will happen via a shared object and a listener.
    Android Animation
    Framework
    This tutorial is an introduction to the built in animation frameworks that
    are part of the Android UI library. Without writing any animation/drawing code,
    you can do 2 types of animations - layout transitions that affect ViewGroups,
    and sequences inside a View. You can also do frame by frame animation, but this
    tutorial will not cover that. The basics covered here affect layout transitions,
    and animation of a View itself, using tweening animation, which includes each of
    the following effects (or any combination) - Alpha, Rotate, Scale, and
    Translate.
    Android ListView and
    custom adapter
    This tutorial will show you how to use ListView to display selectable lists
    of non trivial data, using complex cell renderers. The ListView is a selectable
    list. You can attach a variety of data models to it and load different display
    layouts (cell renderers). You can create your own model and cell renderer. This
    model-view combo is called an Adapter. In this tutorial, I will show you how to
    extend create your own Adapter from scratch, and create your own cell renderers
    from scratch as well.
    Android LinearLayout
    This tutorial shows you how to use the LinearLayout container (using Java
    code, not XML markup), which is the simplest layout mechanism available on
    Android. If you're familiar with Swing's BoxLayout then you will have a good
    idea of what this container has to offer. Linear layouts are really simple… you
    can add components horizontally or vertically to a ‘bag’ or ‘box’.
    Android UI Themes
    This tutorial will show you how to use Android's theme-ing capabilities. You
    can set background color, image, etc. on widgets, dialogs, and activities.
    Android TableLayout
    This tutorial will show you how to use the TableLayout container, which is
    like an HTML table. The UI layout code is done in Java, not XML. A class
    (LayoutUtils) is provided to make it easier to attach layout params to View
    objects.
    Android Option and
    Context menu
    This tutorial will show you how to create options menu (hooks into the MENU
    button) and context menu (press and hold a component).
    Android XML View
    inflation
    This tutorial will show you how to instantiate or inflate a View from XML;
    this is useful for components that don't provide a Java API to tweak with
    certain style attributes. The Button class is used as an example; you can only
    get certain styles to show up via XML that aren't available via the Java API.
    Android SDK and tools -
    Getting started
    This tutorial has helpful pointers for developers who are just getting
    started with Android. In addition to Google’s documentation on the SDK itself,
    there are lots of tools that come with the SDK and others that you can download
    elsewhere that make it a little bit easier to work with Android.
    How to build a
    service-enabled Android app - Part 3/3 Multithreading
    I've written 3 tutorials to show you how to create a service enabled Android
    application that performs all of it's network I/O in a background thread (not
    the UI thread). These tutorials are split into three parts. This tutorial shows
    you how to use background threads to perform long running network IO operations,
    so that the main UI thread is not locked up.
    How to build a
    service-enabled Android App - Part 2/3 Networking
    I've written 3 tutorials to show you how to create a service enabled Android
    application that performs all of it's network I/O in a background thread (not
    the UI thread). These tutorials are split into three parts. This one shows you
    how to use Apache HTTP Client to connect to services over HTTP or HTTPS and
    exchange serialized Java objects with services.
    How to build a
    service-enabled Android app - Part 1/3 UI
    I've written 3 tutorials to show you how to create a service enabled Android
    application that performs all of it's network I/O in a background thread (not
    the UI thread). This tutorial shows you how to build a simple UI without using
    XML, by writing Java code to layout the UI.
                   
                   
                   

    本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/85805/showart_1667037.html
  • 您需要登录后才可以回帖 登录 | 注册

    本版积分规则 发表回复

      

    北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
    未成年举报专区
    中国互联网协会会员  联系我们:huangweiwei@itpub.net
    感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

    清除 Cookies - ChinaUnix - Archiver - WAP - TOP