【问题标题】:GoogleApiClient not connected if used from fragment如果从片段中使用 GoogleApiClient 未连接
【发布时间】:2015-12-03 20:58:31
【问题描述】:

我今天发现了一个奇怪的行为。

我的活动在 onStart() 中连接到 GoogleApiClient 并在 onStop() 中断开连接

活动使用 GridViewPager 来显示我的片段。为了通过数据层发送消息,我在活动和片段之间使用回调接口。

如果我从 Activity 布局中的按钮调用 sendMessage(),它可以正常工作。如果片段使用回调接口 sendMessage() 执行 sendMessage(),则显示“未连接”Toast。

两种方式都调用了 Activity 中的相同方法,那么它的行为怎么可能不同呢?

我应该提到,该问题仅在应用程序第一次重新启动后出现。

活动

public class WearPlex extends WearableActivity implements
    NavigationRemoteCallbacks,
    GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener {

private List<Node> nodeList = new ArrayList<Node>();
private List<Fragment> fragmentList = new ArrayList<Fragment>();
private GoogleApiClient googleApiClient;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_wear_plex);
    setAmbientEnabled();

    fragmentList.add(NavigationRemoteFragment.getInstance(this));

    GridViewPager mGridPager = (GridViewPager)findViewById(R.id.gridViewPager);
    mGridPager.setAdapter(new MainGridPageAdapter(getFragmentManager(), fragmentList));

    googleApiClient = new GoogleApiClient.Builder(this)
            .addApi(Wearable.API)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();
}

@Override
protected void onStart() {
    super.onStart();
    googleApiClient.connect();
}

@Override
protected void onStop() {
    googleApiClient.disconnect();
    super.onStop();
}

@Override
public void onConnected(Bundle bundle) {
    Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();

    nodeList.clear();
    Wearable.NodeApi.getConnectedNodes(googleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
        @Override
        public void onResult(NodeApi.GetConnectedNodesResult nodes) {
            for (Node node : nodes.getNodes()) nodeList.add(node);
        }
    });
}

@Override
public void navigationRemoteSendCommand(String commandPath) {
    sendMessage(commandPath, null);
}

public void debugOnClick(View view) {
    sendMessage("/debug", null);
}

public void sendMessage(String path, byte[] data) {
    if (googleApiClient.isConnected()) {
        for (int i = 0; i < nodeList.size(); i++) {
            if (nodeList.get(i).isNearby()) {
                Toast.makeText(this, "Send message", Toast.LENGTH_SHORT).show();
                Wearable.MessageApi.sendMessage(googleApiClient, nodeList.get(i).getId(), path, data);
            }
        }
    } else {
        Toast.makeText(this, "Not connected", Toast.LENGTH_SHORT).show();
    }
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    Toast.makeText(this, "Connection failed", Toast.LENGTH_SHORT).show();
}

片段

public class NavigationRemoteFragment extends Fragment {

private static NavigationRemoteFragment navigationRemoteFragment = null;

private NavigationRemoteCallbacks callbackHandler = null;

private ImageButton navBtnCenter;

public static NavigationRemoteFragment getInstance(NavigationRemoteCallbacks handler) {
    if (navigationRemoteFragment == null) {
        navigationRemoteFragment = new NavigationRemoteFragment();
        navigationRemoteFragment.callbackHandler = handler;
    }

    return navigationRemoteFragment;
}

public NavigationRemoteFragment() {
    // Required empty public constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View v = inflater.inflate(R.layout.fragment_navigation_remote, container, false);

    navBtnCenter = (ImageButton)v.findViewById(R.id.navBtnCenter);

    navBtnCenter.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            callbackHandler.navigationRemoteSendCommand("/debug");
        }
    });

    return v;
}

}

回调接口

public interface NavigationRemoteCallbacks {
    public void navigationRemoteSendCommand(String commandPath);
}

编辑 MainGridPageAdapter 的 1 个代码

public class MainGridPageAdapter extends FragmentGridPagerAdapter {

private List<Fragment> fragmentList = null;

public MainGridPageAdapter(FragmentManager fm, List<Fragment> fragmentList) {
    super(fm);
    this.fragmentList = fragmentList;
}

@Override
public Fragment getFragment(int i, int i1) {
    if (i1 < fragmentList.size()) return fragmentList.get(i1);
    return null;
}

@Override
public int getRowCount() {
    return 1;
}

@Override
public int getColumnCount(int i) {
    return fragmentList.size();
}

【问题讨论】:

    标签: android google-signin


    【解决方案1】:

    您没有显示MainGridPageAdapter 的代码,所以我不知道它是如何管理片段的。您提到问题发生在重新启动后。查看WearPlex.onCreate() 中的代码,我怀疑问题是由于片段持有对旧的、已破坏的活动实例的引用。

    FragmentManager 的一个记录不充分的行为是它会在重新启动时保存其状态。这经常被忽视,导致重启后重复的片段实例。在宿主活动的onCreate() 方法中管理片段创建的正确模式是:

        if (savedInstanceState == null) {
            // Not a restart
            // Create a new instance of the fragment
            // Add it to the fragment manager
        } else {
            // Restart
            // The fragment manager has saved and restored the fragment instances
            // Use findFragmentById() to get the fragment if you need it
        }
    

    您没有在onCreate() 中使用savedInstanceState 来测试重启。重新启动后,您是否看到比预期更多的碎片?如果是这样,则原始片段包含对旧活动的引用,该活动已停止,并且具有断开连接的 GoogleApiClient。如果单击其中一个片段的 NavBtn,您将看到“未连接”的 toast。

    更新

    问题是由您创建NavigationRemoteFragment 的新实例的方式引起的,特别是使用static 成员navigationRemoteFragment。重新启动后,重新创建活动时,代码会调用NavigationRemoteFragment.getInstance(this)getInstance() 发现 navigationRemoteFragment not null 因为它是静态的,并且不会创建新片段。返回的片段是旧的,其中包含对旧活动的引用,该活动已停止并具有断开连接的GoogleApiClient

    这可以通过使用isDestroyed 方法并添加一些调试日志来确认:

    @Override
    public void navigationRemoteSendCommand(String commandPath) {
        if (isDestroyed()) {
            Log.w("TEST", "This is an old instance of the activity");
        }
        sendMessage(commandPath, null);
    }
    

    【讨论】:

    • 首先感谢您的快速响应。我在第一篇文章中附上了MainGridPageAdapter。如果我从onCreate() 中调用getFragmentManager().getBackStackEntryCount(),则在重新启动应用程序后总是0。我不认为片段管理器中有多个片段实例,因为 GridViewPager 只显示一个片段。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-03
    • 1970-01-01
    相关资源
    最近更新 更多