如何在多项活动中正确使用 Google Plus 登录?

Posted

技术标签:

【中文标题】如何在多项活动中正确使用 Google Plus 登录?【英文标题】:How to correctly use Google Plus Sign In with multiple activities? 【发布时间】:2014-04-17 14:11:00 【问题描述】:

将 Google+ api 客户端生命周期与多活动应用程序的流程联系起来的好方法/推荐方法是什么?使活动依赖于 onConnected api 客户端方法来触发其功能,将其用作一次性“激活”事物,或者完全是其他事物?

我目前正在努力了解如何在我的 android 应用中正确使用 Google+ 登录,该应用有多个活动。

这个想法是,在第一阶段,使用 G+ 登录只是为了验证用户并能够获取她的电子邮件、发送通知和类似的东西。最终我计划推出其他 Google 功能,比如地图或其他 Google Play 服务,所以我认为已经实施它很有用。

但是,我的应用没有按预期运行,我已将问题范围缩小到以下事实:当存在多个活动时,我尚未了解 G+ 登录应用周期。

实现此身份验证方法的正确或推荐方法是什么?是否有某种模式可以引导我朝着正确的方向前进?

比如我找到了api客户端生命周期的a very simple diagram,但是这和app流程有什么关系呢?

最初我有一个登录活动,我在其中放置了登录按钮。在Google's guide 之后,我可以登录,当调用 onConnected 方法时,我会启动 Home Activity(有点像应用程序的仪表板或主屏幕)。

这有点工作。例如,为每个活动处理 onStart 和 onStop 的好方法是什么?我应该每次为每个活动重新连接并重新验证 api 客户端吗?所以也许有一个 BaseActivity 来实现这一切是个好主意。

另一个问题是,我应该使用相同的 api 客户端对象并以某种方式传递它,还是将它存储在 Base Activity 类中?还是我应该每次都创建和初始化一个新的 api 客户端对象?

如何仅使用登录活动通过 G+ 进行身份验证,然后获取电子邮件并将其存储在本地数据库中,并将用户标记为“已验证”或“活动”或其他内容。这将阻止我每次关闭应用程序或暂停连接时都必须重新进行身份验证,甚至可以节省一些电池电量。

该应用并未真正使用 G+ 发帖或任何其他类似功能。理想情况下,它应该可以在离线状态下正常工作,并且只需要连接初始身份验证或其他一次性的东西。

非常感谢任何正确方向的建议或指示。

编辑:我已经阅读了我能找到的所有使用 Google+ 的指南和教程,并且每个人都从单一活动的角度解决了这个问题。我认为这是一个足够普遍的问题,它可以从某种模式或至少是一般准则中受益。

【问题讨论】:

***.com/questions/16827839/… 谢谢,我已经看过那个帖子了,虽然它声明您可以安全地使用多个 api 客户端实例,但我正在寻找更详细的建议或建议,尤其是关于活动到 api 客户端。 IE。每个 Activity 是否应该依赖 api 客户端 onConnected() 回调才能正常运行?如果用户撤销对应用程序的访问权限,我应该多久重新连接一次客户端?和类似的东西。 【参考方案1】:

为每个活动重新连接绝对没问题。从广义上讲,我看到人们实现这一点的方式有 3 种:

    主要在基本活动中实施,并让其他活动对其进行扩展。这是每个活动中的连接/断开连接,但只有一个地方有代码。 在片段中实现连接/断开连接,并将其包含在需要身份验证的活动中。如果您已有无法扩展的基本活动(例如某些游戏案例),这将很有帮助。 实施服务以连接/断开连接。如果需要登录,这可以触发广播意图或类似内容。

所有这些工作,我已经看到它们都用于现实世界的应用程序。要记住的主要事情是将 99% 的逻辑(用户登录或退出,并且您会被告知)与相对罕见的“此时登录”用例分开。例如,您可能多次触发 onConnected/onConnection 失败,但大多数情况下您忽略或只是稍微改变了应用程序的状态。只有在带有登录按钮的屏幕上,您才需要连接结果解析和 onActivityResult 内容。将 google play 服务连接想象成主要是询问用户的状态,而不是让他们登录,你应该没问题。

【讨论】:

我想这就是我想要的。确认现实世界的应用程序确实使用了这些方法中的任何一种。 Lee 的回答是一个很好的跟进,但我想这更类似于我想知道的。 "为每个活动重新连接绝对没问题。" -> 但这会显示一个连接对话框,这对玩家来说是一个干扰因素。 对于 g+ 登录,连接时不会自动显示同意屏幕,只有在解决了 userrecoverableauhexception os 的情况下。【参考方案2】:

我同意 Ian Barber 的回答,但为了进一步解释,您的 Activitys 应分为两种类型 - 解决登录问题的 Activitys 和需要登录的 Activitys。

大多数Activitys 不关心用户身份验证,并且在您的应用中具有相同的逻辑。他们将创建一个GoogleApiClient,它连接到设备上运行的 Google Play 服务进程并读取用户的缓存登录状态 - 如果用户已登录,则返回 onConnected(),否则返回 onConnectionFailed()。如果用户未登录,您的大多数Activitys 将希望重置您的应用程序状态并启动您的LoginActivity。每个Activity 应维护自己的GoogleApiClient 实例,因为它是用于访问的轻量级对象Google Play 服务进程持有的共享状态。例如,这种行为可以封装在共享的BaseActivity 类或共享的SignInFragment 类中,但每个实例都应该有自己的GoogleApiClient 实例。

但是,您的 LoginActivity 需要以不同的方式实现。它还应该创建一个GoogleApiClient,但是当它收到指示用户登录的onConnected() 时,它应该为用户和finish() 启动一个适当的Activity。当您的LoginActivity 收到表明用户未登录的onConnectionFailed() 时,您应该尝试解决startResolutionForResult() 的登录问题。

【讨论】:

感谢您的额外解释。基本上将这两种类型的活动分开是有意义的。我只是担心在每个 Activity 中使用连接/断开连接会有点矫枉过正,但这似乎是标准做法。【参考方案3】:

0。 TL;DR

对于不耐烦的程序员,可以在GitHub 上找到以下实现的工作版本。

在许多不同的应用程序中多次重写登录活动代码后,简单(但不那么优雅)的解决方案是将 Google API 客户端创建为应用程序类对象。但是,由于连接状态会影响 UX 流程,因此我对这种方法并不满意。

将我们的问题简化为连接概念,我们可以考虑:

    它隐藏了 Google API 客户端。 它有有限的状态。 这是一个(相当)独特的。 当前状态会影响应用的行为。

1。代理模式

由于Connection封装了GoogleApiClient,所以会实现ConnectionCallbacksOnConnectionFailedListener

@Override
public void onConnected(Bundle hint) 
    changeState(State.OPENED);


@Override
public void onConnectionSuspended(int cause) 
    changeState(State.CLOSED);
    connect();


@Override
public void onConnectionFailed(ConnectionResult result) 
    if (currentState.equals(State.CLOSED) && result.hasResolution()) 
        changeState(State.CREATED);
        connectionResult = result;
     else 
        connect();
    

Activity 可以通过 connectdisconnectrevoke 方法与 Connection 类进行通信,但它们的行为由当前状态决定。状态机需要以下方法:

protected void onSignIn() 
    if (!googleApiClient.isConnected() && !googleApiClient.isConnecting()) 
        googleApiClient.connect();
    


protected void onSignOut() 
    if (googleApiClient.isConnected()) 
        Plus.AccountApi.clearDefaultAccount(googleApiClient);
        googleApiClient.disconnect();
        googleApiClient.connect();
        changeState(State.CLOSED);
    


protected void onSignUp() 
    Activity activity = activityWeakReference.get();
    try 
        changeState(State.OPENING);
        connectionResult.startResolutionForResult(activity, REQUEST_CODE);
     catch (IntentSender.SendIntentException e) 
        changeState(State.CREATED);
        googleApiClient.connect();
    


protected void onRevoke() 
    Plus.AccountApi.clearDefaultAccount(googleApiClient);
    Plus.AccountApi.revokeAccessAndDisconnect(googleApiClient);
    googleApiClient = googleApiClientBuilder.build();
    googleApiClient.connect();
    changeState(State.CLOSED);

2。状态模式

这是一种行为模式,允许对象在其内部状态发生变化时改变其行为。 GoF Design Patterns book 描述了如何用这种模式表示 TCP 连接(这也是我们的例子)。

来自状态机的状态应该是 singleton,而在 Java 中最简单的方法是创建名为 StateEnum,如下所示:

public enum State 
    CREATED 
        @Override
        void connect(Connection connection) 
            connection.onSignUp();
        
        @Override
        void disconnect(Connection connection) 
            connection.onSignOut();
        
    ,
    OPENING ,
    OPENED 
        @Override
        void disconnect(Connection connection) 
            connection.onSignOut();
        
        @Override
        void revoke(Connection connection) 
            connection.onRevoke();
        
    ,
    CLOSED 
        @Override
        void connect(Connection connection) 
            connection.onSignIn();
        
    ;

void connect(Connection connection) 
void disconnect(Connection connection) 
void revoke(Connection connection) 

Connection 类保存上下文,即当前状态,它定义了 Connection 方法 connectdisconnectrevoke 的行为方式:

public void connect() 
    currentState.connect(this);


public void disconnect() 
    currentState.disconnect(this);


public void revoke() 
    currentState.revoke(this);


private void changeState(State state) 
    currentState = state;
    setChanged();
    notifyObservers(state);

3。单例模式

由于不需要重复创建此类,我们将其作为单例提供:

public static Connection getInstance(Activity activity) 
    if (null == sConnection) 
        sConnection = new Connection(activity);
    

    return sConnection;


public void onActivityResult(int result) 
    if (result == Activity.RESULT_OK) 
        changeState(State.CREATED);
     else 
        changeState(State.CLOSED);
    
    onSignIn();


private Connection(Activity activity) 
    activityWeakReference = new WeakReference<>(activity);

    googleApiClientBuilder = new GoogleApiClient
           .Builder(activity)
           .addConnectionCallbacks(this)
           .addOnConnectionFailedListener(this)
           .addApi(Plus.API, Plus.PlusOptions.builder().build())
           .addScope(new Scope("email"));

    googleApiClient = googleApiClientBuilder.build();
    currentState = State.CLOSED;

4。可观察模式

Connection 类扩展了 Java Observable,因此 1 个或多个活动可以观察到状态变化:

@Override
protected void onCreate(Bundle bundle) 
    connection = Connection.getInstance(this);
    connection.addObserver(this);


@Override
protected void onStart() 
    connection.connect();


@Override
protected void onDestroy() 
    connection.deleteObserver(this);
    connection.disconnect();


@Override
protected void onActivityResult(int request, int result, Intent data) 
    if (Connection.REQUEST_CODE == request) 
        connection.onActivityResult(result);
    


@Override
public void update(Observable observable, Object data) 
    if (observable != connection) 
        return;
    
    // Your presentation logic goes here...

【讨论】:

非常有趣的方法,而且看起来代码甚至是完整的。有时会尝试一下,并将其与我的方法进行比较。谢谢! 很棒的答案,看来您非常了解设计模式。感谢您做出如此出色的努力。

以上是关于如何在多项活动中正确使用 Google Plus 登录?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用google plus的accessToken从不同的活动中注销android中的用户

如何查看谁与 Google Plus 共享了您的网站或如何查看 Google Plus 指标和活动?

使用 google+ API 从 google plus 社区和活动获取数据

多项选择的测验应用程序

如何使用 Siri 语音命令验证用户在多项选择题中的回答

如何从不同的活动中注销G + plus sigin?