Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
294 views
in Technique[技术] by (71.8m points)

android - Delay while launching activity from service

I have a broadcast receiver which receives screen off broadcast, and when the screen is turned off, it launches an Activity. I've named this activity LockScreenActivity. I've added getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); in onCreate() of the activity, so that it appears on top of the Android lockscreen, which is exactly what I want. This works well enough, mostly. Problem happens whenever I press home button to send any app to the background.

When I press home button to return to home screen from any app, and then immediately press power button to turn off the screen, and press power button in a second to turn in back on, it shows the Android lock screen. My activity gets created some 3-4 seconds later. This is a huge delay and is unacceptable. In other scenarios, this delay does not happen. If I've been in home screen for some time, or some other app is open, and I click power button twice in quick succession, I see my LockScreenActivity before seeing the Android lockscreen.

My app also has a few other normal activities (launched from App drawer) and a service which shows a persistent notification. The same LockScreenActivity is launched whenever the notification is clicked. Interestingly enough, if I press home button to minimize some app and immediately click the notification of my app, it opens the LockScreenActivity without any delay.

I've searched a lot for any solution. Here's what I've tried out so far:

  1. Made the broadcast receiver run in separate thread by passing a handler when registering the broadcast receiver
  2. Acquired a wakelock in the broadcast receiver's onReceive() and released it in onResume() of LockScreenActivity
  3. Made a separate task stack for LockScreenActivity by specifying a different taskAffinity in manifest. I've also played with lot of combinations of the options related to activity stack, but nothing has helped so far.
  4. Instead of launching the activity directly, sent an intent to the service which then launches the activity. Nothing has worked, unfortunately.

Here's the manifest declaration of the activity, and service:

<activity
        android:name=".LockScreenActivity"
        android:label="@string/app_name"
        android:excludeFromRecents="true"
        android:taskAffinity="@string/task_lockscreen">
</activity>
<service
        android:name=".services.BaseService"
        android:enabled="true" >
</service>

Broadcast receiver:

public class ScreenReceiver extends BroadcastReceiver {
private static final String LOG_TAG = ScreenReceiver.class.getSimpleName();
private static final String HANDLER_THREAD_NAME = "screen_receiver_thread";
public static final String WAKELOCK_TAG = "lockscreen_overlay_create_wakelock";

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
        WakeLockHelper.acquireWakeLock(WAKELOCK_TAG, context);
        Log.d(LOG_TAG, "Screen off");
        context.startService(BaseService.getServiceIntent(context, null, BaseService.ACTION_START_LOCKSCREEN_ACTIVITY));
    } else {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            Log.d(LOG_TAG, "Screen on");
        }
    }
}

public static void registerScreenReceiver(Context context){
    new BroadcastReceiverRegistration().execute(context);
}

private static class BroadcastReceiverRegistration extends AsyncTask<Context, Void, Void>{
    @Override
    protected Void doInBackground(Context... params) {
        Context context = params[0];
        if(Utility.checkForNullAndWarn(context, LOG_TAG)){
            return null;
        }
        HandlerThread handlerThread = new HandlerThread(HANDLER_THREAD_NAME);
        handlerThread.start();
        Looper looper = handlerThread.getLooper(); //Will block till thread is started, hence using AsyncTask
        Handler handler = new Handler(looper);

        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_SCREEN_ON);
        BroadcastReceiver mReceiver = new ScreenReceiver();

        context.registerReceiver(mReceiver, filter, null, handler);
        return null;
    }
}
}

WakeLockHelper is a class which manages all the wakelocks in my app. registerScreenReceiver() is called by the Service when it starts.

Here's the onCreate() of LockScreenActivity:

protected void onCreate(Bundle savedInstanceState) {
    Log.d(LOG_TAG, "LockscreenActivity onCreate()");
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_lock_screen);
    if (savedInstanceState == null) {
        getFragmentManager().beginTransaction()
                .add(R.id.container, new LockScreenFragment())
                .commit();
    }
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    getWindow().setDimAmount((float) 0.4);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);

    int systemUiVisibilityFlags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
            View.SYSTEM_UI_FLAG_FULLSCREEN |
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        systemUiVisibilityFlags = systemUiVisibilityFlags | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
    }
    getWindow().getDecorView().setSystemUiVisibility(systemUiVisibilityFlags);
}

Here's the log when things work as expected: (Screen is intially on; it is turned off and on again)

09-19 11:00:09.384  31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen off
09-19 11:00:09.384  31226-31226/com.pvsagar.smartlockscreen D/BaseService﹕ Starting lockscreen overlay.
09-19 11:00:09.394  31226-31226/com.pvsagar.smartlockscreen D/LockScreenActivity﹕ LockscreenActivity onCreate()
09-19 11:00:09.735  31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen on

Not much delay between screen off and sending the intent and the LockScreenActivity actually starting. Now Screen is initially on; I press home from some app (any app, even some other activity of my own app); Screen is turned off and on again:

09-19 11:02:51.557  31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen off
09-19 11:02:51.557  31226-31226/com.pvsagar.smartlockscreen D/BaseService﹕ Starting lockscreen overlay.
09-19 11:02:51.708  31226-31243/com.pvsagar.smartlockscreen D/ScreenReceiver﹕ Screen on
09-19 11:02:54.851  31226-31226/com.pvsagar.smartlockscreen D/LockScreenActivity﹕ LockscreenActivity onCreate()

Here, as you can see, screen off broadcast is received in time, service gets the intent immediately and send the intent to LockScreenActivity, but onCreate() of LockScreenActivity is delayed by 3 seconds.

This is how the LockScreenActivity is started by the service:

Intent lockscreenIntent = new Intent(this, LockScreenActivity.class);
lockscreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.d(LOG_TAG, "Starting lockscreen overlay.");
startActivity(lockscreenIntent);

This is the pending intent passed to the notification (just to show that it does the same thing):

Intent notificationIntent = new Intent(context, LockScreenActivity.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);
notificationBuilder.setContentIntent(pendingIntent);

I'm not having any idea why this is happening. GC is also not running too much. What could be the problem? What can I possibly do to overcome this? Any suggestions/ideas would be greatly appreciated.

The entire code can be found at: https://github.com/aravindsagar/SmartLockScreen/tree/backend_code_strengthening

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

After much digging, found out the cause of the problem. Apparently it's not a bug, it is a feature which does not allow Services or BroadcastReceivers to launch activities for up to 5 seconds after home button is pressed. No easy way to overcome this.

More info here: https://code.google.com/p/android/issues/detail?id=4536

I replaced the activity with a Window added to the window manager of the running service. This does not cause any delay.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...