译:Activity真正“可见”的时间点

博客源址译:Activity真正“可见”的时间点

博客时间2011-09-22 10:20

Activity生命周期中,onStart, onResume, onCreate都不是真正visible的时间点,真正的visible时间点是onWindowFocusChanged()函数被执行时。

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        // TODO Auto-generated method stub
        if(hasFocus){
           //Do something here
        }
        super.onWindowFocusChanged(hasFocus);
    }

译注:从onWindowFocusChanged被执行起,用户可以与应用进行交互了,而这之前,对用户的操作需要做一点限制。



[原文:http://www.seangri-la.com/cgi-bin/moin.cgi/onResume_!%3D_Visible_to_user]

onStart, onResume, onCreate != Visible to user

There is some implication that Activity lifecycle methods onStart and onPause correspond to visibility of the application to the user. The documentation for Activity says:

However, in Java ME, there has been confusion, even among implementers of the specification, as to whether or not the lifecycle methods startApp() and pauseApp() refer to visibility or to something else. There are JavaME handsets for which startApp() is called when the app is about to become visible, and pauseApp() is called when it is about to go into the background. This is useful for the MIDP1 and MIDP2 UI classes for which the application is not notified of visibility changes but must instead poll for them. Other MIDP implementers did not consider these methods as referring tofocus or visibility, but rather actual resource availability. Those handsets did not give up resources when the handset is paused, so the implementations never called pauseApp(). Recognizing the different interpretations for pauseApp(), MIDP3 clarified its purpose (and removed the overloading of visibility and resource availability) by deprecating pauseApp() in such a way that MIDP3 apps may include the method for backwards compatibility, but it will never be called by a MIDP3 implementation. MIDP3 expanded visibility change notification to compensate for this.

Partial Visibility and Full Visibility vs.Focus

It is important to differentiate between partial visibility, full visibility, andfocus.

Visibility is when the application's window is fully visible to the user.Partial visibility is when the application'swindow is partially obscured by anotherwindow, say, a system-level dialog.

Focus,on the other hand, is the ability for the application to receive input from the user.Focus can only happen when there is fullvisibility, as if there is only partial visibility, it is the system-level dialog that is waiting for user input which currently has thefocus.

In other words, multiple applications may be visible (one of these being a system application or dialog), but only one will havefocus - the one which is layered (and has full visibility) above the other (which has partial visibility.)

Gaining visibility vs. focus

But I digress. Reading the javadoc for onResume(), one sees the following:

Keep in mind that onResume is not the best indicator that your activity is visible to the user; a system window such as the keyguard may be in front. Use onWindowFocusChanged(boolean) to know for certain that your activity is visible to the user (for example, to resume a game).

Even this documentation is not too clear. It probably should read, "know for certain that your activity isfully visible to the user and has focus.

The javadoc for Activity.onWindowFocusChanged() provides more info:

Called when the current Window of the activity gains or loses focus. This is the best indicator of whether this activity is visible to the user.

Note that this provides information what global focus state, which is managed independently of activity lifecycles. As such, while focus changes will generally have some relation to lifecycle changes (an activity that is stopped will not generally get window focus), you should not rely on any particular order between the callbacks here and those in the other lifecycle methods such as onResume().

As a general rule, however, a resumed activity will have window focus... unless it has displayed other dialogs or popups that take input focus, in which case the activity itself will not have focus when the other windows have it. Likewise, the system may display system-level windows (such as the status bar notification panel or a system alert) which will temporarily take window input focus without pausing the foreground activity.

The fact that onResume() does not guarantee visibility is very easily demonstratedon a Sprint HTC Hero (and probably other devices with keyguards). Start your application normally, and while it is stillon the screen, press the "end/power" button once (which turns off the displayon the Hero.)

On the Hero, there are at least two ways that the user can turn the phone backon - pressing the "end/power" key again, or pressing "menu" twice. The former will engage the key guard which has to be swiped before the app is displayed; the latter avoids the keyguard and displays the app immediately.

Place the following code in your Activity class (I've omitted onCreate(...) which you of course must provide):

...
import android.util.Log;
...
public class VisibilityTest extends Activity {

    ...
    public static final String LOG_TAG = "VisibilityTest";
    ...

    ...
    /** Called when the Activity has acquired resources, but might be obscured by some system app (like the screen guard) */


    @Override
    protected void onResume() {
        super.onResume();
        Log.v(LOG_TAG, "(" + this + ") onResume() called");
    }

    /** Called when the current Window of the activity gains or loses focus.  The best indicated of activity visibility */
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        Log.v(LOG_TAG, "(" + this + ") onWindowFocusChanged() called, hasFocus = " + Boolean.toString(hasFocus));
    }

Then run "adb logcat | grep FocusTest" to see the log (and filter out unnecessary stuff).

You will see that onResume() is called as soon as the phone screen powers back up, even with the keyguard present. onWindowFocusChanged(hasFocus), however, is not called until the screen guard is removed and the app is actually visible.

If you do not have an unlock pattern (swipe your finger across the dots to unlock it) set upon your phone, pressing menu twice will cause onWindowFocusChanged() to be called immediately after onResume. With the key guard or the unlock pattern blocking access to the app, however, onWindowFocusChanged will be delayed until you unlock the screen and the app is actually visible again.

Furthermore, you can also demonstrate partial visibility on the Sprint HTC Hero by holding down the search button for several seconds while the app is visible. This activates Google Search in voice mode. You will observe that onPause() is called and onWindowFocusChanged(hasFocus) is called and returns "false". However, you will still be able to see most of the application below the Google Search dialog.

Naturally, one also should not assume that onCreate() means visibility, either. Some "Hello World" examples do not show the use of onResume(). One might assume that creation of the UI elements in onCreate() implies visibility and/orfocus. This is not true. It is only a method where the application may set up a user interface for an Activity if one is desired. Note that in the Android lifecycle diagrams, onResume() is called after onCreate(). As we've demonstrated above, even that is not necessarily enough. This can be demonstrated by attaching a phone to the PC and launching the app from Eclipse while the screen guard is active. onCreate() and onResume() will be called, but onWindowFocusChanged() will not. This is unlikely to be encountered in the real world where a user is launching the app directly, but might be encountered if the app is launced by an Intent from another application while the screen guard ison.

The whole idea of tying visibility to the lifecyle methods is problematic because it is overloading visibility and resource availability. This was the same trap that MIDP fell into and was eventually revised to eliminate. The solution for MIDP was that both resource availability and visibility were removed from the lifecycle methods, and in fact, those methods were reduced to "running" or "not running". Finer grained resource, visibility andfocusinformation was moved to other APIs that already existed or were created (TODO: double check final MIDP3 spec to verify this.)

Even though this class is an improvement of one or two orders of magnitude over equivalent APIs in MIDP, visibility, partial visibility,focus, and availability of resources are blurred in the documentation for the Activity class.

Losing visibility and/or focus

If you also instrument onPause as above, you will see that onWindowFocusChanged is called AFTER onPause. This kind of makes sense; the method name itself is in the past tense.

onPause should be a pretty good indicator that you are about to losefocus, rather than youhave already lostfocus. To confuse things more, however, the documentation for onStop() also talks about visibility:

    Called when you are no longer visible to the user.

This definitely is misleading. By the time onStop is called, it is possible that you have not been visible to the user for a long time. In tests with my HTC hero, if I press the end/power keyon my Hero, I will not see onStop() called even though the screen is turned off. If we are referring to "activity A", and a second "activity B" is launchedontop of it (without the user dismissing "Activity A", "activity A" will most certainly lose visibility to "B", but onStop() might not be called, per the documentation for onSaveInstanceState() (which is called prior to onStop()):

   the system may avoid calling onSaveInstanceState(Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.

In other words, the system is trying to keep activity "A" around because it is below "B" in the navigation stack and is likely to be returned to. (I presume that a good example of this is when an incoming call arrives, but I am out of coverage at the moment, and cannot test this.)

Conclusion

onWindowFocusChanged() is, as its documentation says, the most reliable way to tell if the user is able to interact with your application.

如文档所述,onWindowFocusChanged()函数是告诉是否用户能够与应用交互的最可靠的方法。(译注:从这个时候起,用户可以与应用进行交互了,而这之前,对用户的操作需要做一点限制)

The lifecycle method onResume() does not guarantee full or even partial visibility. Visibility may be partially or fully obscured by a system activity such as a key guard.

The lifecycle method onPause() does not guarantee loss of visibility. The appearance of a system dialog may lead to onPause() being called, but the application might still be visible beneath the dialog. Or, the application might be completely obscured.

As the documentation for onWindowFocusChanged() says, if you need to know when your app hasfocus or does not (for example, to pause and resume a game automatically before and after an interruption by another app) then you will need onWindowFocusChanged. If actual visibility is not that important (for example, if you don't care if your app is obscured by a screen lock), then onPause() and onResume() should be enough - or, like theHelloWorld examples, your app will be such that even these won't matter. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值