9

每次打开/关闭应用程序时,无论是从任务抽屉启动还是恢复,我都想在服务器上签入和签出用户。有没有办法做到这一点,同时避免在每个活动中调用一个函数?

谢谢!

4

4 回答 4

11

编辑

这个答案中,matdev 让我注意到了通过ProcessLifeCycleOwner. 见https://developer.android.com/topic/libraries/architecture/lifecycle

因此,为了更好地组织所需的会话管理功能,应使用以下结构。注册. SessionTracker_ onCreate_ MyApplication与跟踪用户会话相关的功能然后被隔离到SessionTracker类中。

首先添加到您的 build.gradle

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

然后,执行以下操作:

public class MyApplication extends Application {  

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(SessionTracker.getInstance());
    }
}

public class SessionTracker implements LifecycleObserver {
    private static SessionTracker sSessionTracker;
    private static final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;  // Time allowed for transitions

    private Timer mStopDelayTimer;
    private TimerTask mActivityTransitionTimerTask;
    private boolean mWasInBackground = true;
    private AppSession mAppSession;

    public static SessionTracker getInstance() {
        if (sSessionTracker == null) {
            sSessionTracker = new SessionTracker();
        }
        return sSessionTracker;
    }

    private SessionTracker() {
        // no-op
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onLifecycleStop() {
        submitAppSession(appSession);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onLifecycleStart() {
        mAppSession = new AppSession();
    }

    private void submitAppSession(AppSession appSession) {
        // TODO submit app session here
    }
}

public class AppSession {
    /* TODO */
}

以前的答案

d60402在这里提供的答案和 Hanno Binder 使用Application.registerActivityLifecycleCallbacks()注册活动回调的建议使我找到了这个解决方案。

我扩展了 Application 并将回调注册到 Activity 方法 onPause 和 onStart ,如下所示。在这些方法中,启动/停止计时器(在调用 onPause 的地方退出一个活动,在调用 onStart 的地方进入一个新活动)。当应用程序确定在后台/前台(真/假)时,会切换标志“wasInBackground”。如果运行 onStart 回调时应用程序在后台,则调用“appEntered”。如果在 onPause 和 onStart 回调之间传递的时间大于指定时间(为活动转换提供足够的时间),则在认为应用会话完成的地方调用“appExited”。

public class MyApplication extends Application {

public static final String LOG_TAG = "MyApp";

public boolean wasInBackground = true;

private AppSession appSession;
private Timer mActivityTransitionTimer;
private TimerTask mActivityTransitionTimerTask;
private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;  // Time allowed for transitions

Application.ActivityLifecycleCallbacks activityCallbacks = new Application.ActivityLifecycleCallbacks() {

    @Override
    public void onActivityResumed(Activity activity) {

        if (wasInBackground) {
            //Do app-wide came-here-from-background code
            appEntered();
        }
        stopActivityTransitionTimer();
    }

    @Override
    public void onActivityPaused(Activity activity) {
        startActivityTransitionTimer();
    }

    ...

};

@Override
public void onCreate() {
    super.onCreate();
    registerActivityLifecycleCallbacks(activityCallbacks);
}

public void startActivityTransitionTimer() {
    this.mActivityTransitionTimer = new Timer();
    this.mActivityTransitionTimerTask = new TimerTask() {
        public void run() {
            // Task is run when app is exited
            wasInBackground = true;
            appExited();
        }
    };

    this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
            MAX_ACTIVITY_TRANSITION_TIME_MS);
}

public void stopActivityTransitionTimer() {
    if (this.mActivityTransitionTimerTask != null) {
        this.mActivityTransitionTimerTask.cancel();
    }

    if (this.mActivityTransitionTimer != null) {
        this.mActivityTransitionTimer.cancel();
    }

    this.wasInBackground = false;
}

private void appEntered() {
    Log.i(LOG_TAG, "APP ENTERED");

    appSession = new AppSession();
}

private void appExited() {
    Log.i(LOG_TAG, "APP EXITED");

    appSession.finishAppSession();

    // Submit AppSession to server
    submitAppSession(appSession);
    long sessionLength = (appSession.getT_close() - appSession.getT_open())/1000L;
    Log.i(LOG_TAG, "Session Length: " + sessionLength);
}
于 2015-05-16T19:16:56.403 回答
6

你可以看看Application.registerActivityLifecycleCallbacks()&c。

于 2015-04-28T16:14:20.470 回答
6

android.arch.lifecycle包提供了类和接口,让您知道应用程序何时进入后台或前台。

您的应用程序应该实现 LifecycleObserver 接口:

public class MyApplication extends Application implements LifecycleObserver {

    @Override
    public void onCreate() {
        super.onCreate();
        ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    private void onAppBackgrounded() {
        Log.d("MyApp", "App in background");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    private void onAppForegrounded() {
        Log.d("MyApp", "App in foreground");
    }
}

为此,您需要将此依赖项添加到您的 build.gradle 文件中:

dependencies {
    implementation "android.arch.lifecycle:extensions:1.1.1"
}

按照 Google 的建议,您应该尽量减少在活动的生命周期方法中执行的代码:

一个常见的模式是在活动和片段的生命周期方法中实现依赖组件的动作。但是,这种模式会导致代码组织不良和错误泛滥。通过使用生命周期感知组件,您可以将依赖组件的代码移出生命周期方法并移入组件本身。

您可以在这里阅读更多内容: https ://developer.android.com/topic/libraries/architecture/lifecycle

于 2018-11-23T08:41:54.420 回答
1

好的。我将我的评论作为答案发布,因为提出问题的原始用户发现这真的很有帮助。

上述答案的问题在于,应用程序不想在活动关闭时签入和签出,而是在启动或恢复应用程序时。因此,当您在打开应用程序时关闭或在活动之间移动时,这可能会出现问题,这仍然会调用 oncreate() 和 onpause() 函数。

这个问题之前也在stackoverflow上讨论过。下面是链接。

如何检测Android应用程序何时进入后台并返回前台

解决这个问题可能有不同的方法。上面的链接提供了有关如何解决它的更多见解。

于 2015-04-30T15:26:35.073 回答