You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Studio中ViewPager+Fragment集成Firebase+Facebook登录崩溃排查

Android Studio实现ViewPager+Fragment结合Firebase Auth Facebook登录教程

Hey there, let's fix that null pointer issue first and walk you through a complete implementation of ViewPager + Fragment with Firebase Auth Facebook login. Here's what's going wrong and how to do it right:

问题根源直击

Your NullPointerException happens because your LoginButton lives inside a Fragment's layout file (either fragment_main or fragment_second), but you're trying to reference it directly in MainActivity's onCreate method. At that point, the Fragment hasn't been inflated yet—so the button doesn't exist in the Activity's view hierarchy, hence the null reference. We need to move all login-related logic into the Fragment that contains the button.

完整实现步骤

1. 基础配置(已完成可跳过)

First, confirm you've got these basics sorted (same as your working non-Fragment setup):

  • Firebase project created, Android app added, Facebook login enabled in Firebase Console (with your Facebook App ID/Secret)
  • Facebook Developer Platform configured for your app (package name, signature hash, login product enabled)
  • Required entries in strings.xml:
    <string name="facebook_app_id">你的Facebook App ID</string>
    <string name="login_protocol_scheme">fb你的Facebook App ID</string>
    
  • Permissions and meta-data in AndroidManifest.xml:
    <uses-permission android:name="android.permission.INTERNET" />
    
    <meta-data
        android:name="com.facebook.sdk.ApplicationId"
        android:value="@string/facebook_app_id" />
    
    <activity
        android:name="com.facebook.FacebookActivity"
        android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
        android:label="@string/app_name" />
    <activity
        android:name="com.facebook.CustomTabActivity"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="@string/login_protocol_scheme" />
        </intent-filter>
    </activity>
    

2. 布局文件重构

2.1 MainActivity布局(activity_main.xml)

Keep this clean—only ViewPager and optional TabLayout for slider navigation:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.tabs.TabLayout
        android:id="@+id/tabs"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        app:tabMode="fixed"
        app:tabGravity="fill"
        app:layout_constraintTop_toTopOf="parent"/>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toBottomOf="@id/tabs"/>

</androidx.constraintlayout.widget.ConstraintLayout>

2.2 登录Fragment布局(fragment_login.xml)

Place your LoginButton here (this is where it belongs):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <com.facebook.login.widget.LoginButton
        android:id="@+id/login_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"/>

    <TextView
        android:id="@+id/login_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="未登录"/>

</LinearLayout>

2.3 首页Fragment布局(fragment_home.xml)

A simple page to show user info after login:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

    <TextView
        android:id="@+id/user_info"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="请先登录"/>

</LinearLayout>

3. 创建Fragment类

3.1 登录Fragment(LoginFragment.java)

All login logic lives here—this is where we'll handle the Facebook button:

import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import com.facebook.AccessToken;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.login.LoginResult;
import com.facebook.login.widget.LoginButton;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthCredential;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FacebookAuthProvider;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;

public class LoginFragment extends Fragment {

    private static final String TAG = "LoginFragment";
    private FirebaseAuth mAuth;
    private CallbackManager mCallbackManager;
    private TextView mLoginStatus;

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

    public static LoginFragment newInstance() {
        return new LoginFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAuth = FirebaseAuth.getInstance();
        mCallbackManager = CallbackManager.Factory.create();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_login, container, false);

        mLoginStatus = rootView.findViewById(R.id.login_status);
        LoginButton loginButton = rootView.findViewById(R.id.login_button);
        loginButton.setReadPermissions("email", "public_profile");

        // Register Facebook login callback
        loginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                Log.d(TAG, "facebook:onSuccess:" + loginResult);
                handleFacebookAccessToken(loginResult.getAccessToken());
            }

            @Override
            public void onCancel() {
                Log.d(TAG, "facebook:onCancel");
                mLoginStatus.setText("登录已取消");
            }

            @Override
            public void onError(FacebookException error) {
                Log.d(TAG, "facebook:onError", error);
                mLoginStatus.setText("登录出错");
            }
        });

        return rootView;
    }

    @Override
    public void onStart() {
        super.onStart();
        // Check if user is already signed in
        FirebaseUser currentUser = mAuth.getCurrentUser();
        updateUI(currentUser);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // Pass result to Facebook SDK
        mCallbackManager.onActivityResult(requestCode, resultCode, data);
    }

    private void handleFacebookAccessToken(AccessToken token) {
        Log.d(TAG, "handleFacebookAccessToken:" + token);

        AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            Log.d(TAG, "signInWithCredential:success");
                            FirebaseUser user = mAuth.getCurrentUser();
                            updateUI(user);
                            // Auto-switch to home fragment after login
                            ((MainActivity)getActivity()).switchToHomeFragment();
                        } else {
                            Log.w(TAG, "signInWithCredential:failure", task.getException());
                            Toast.makeText(getActivity(), "认证失败", Toast.LENGTH_SHORT).show();
                            updateUI(null);
                        }
                    }
                });
    }

    private void updateUI(FirebaseUser user) {
        if (user != null) {
            mLoginStatus.setText("已登录:" + user.getDisplayName());
        } else {
            mLoginStatus.setText("未登录");
        }
    }
}

3.2 首页Fragment(HomeFragment.java)

Display user info once logged in:

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.fragment.app.Fragment;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;

public class HomeFragment extends Fragment {

    private TextView mUserInfo;
    private FirebaseAuth mAuth;

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

    public static HomeFragment newInstance() {
        return new HomeFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mAuth = FirebaseAuth.getInstance();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_home, container, false);
        mUserInfo = rootView.findViewById(R.id.user_info);
        return rootView;
    }

    @Override
    public void onStart() {
        super.onStart();
        FirebaseUser currentUser = mAuth.getCurrentUser();
        updateUserInfo(currentUser);
    }

    private void updateUserInfo(FirebaseUser user) {
        if (user != null) {
            StringBuilder info = new StringBuilder();
            info.append("用户名:").append(user.getDisplayName()).append("\n");
            info.append("邮箱:").append(user.getEmail()).append("\n");
            mUserInfo.setText(info.toString());
        } else {
            mUserInfo.setText("请先登录");
        }
    }
}

4. 更新MainActivity.java

MainActivity now only manages the ViewPager and Fragment navigation:

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;

import com.google.android.material.tabs.TabLayout;

public class MainActivity extends AppCompatActivity {

    private SectionsPagerAdapter mSectionsPagerAdapter;
    private ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
        mViewPager = findViewById(R.id.container);
        mViewPager.setAdapter(mSectionsPagerAdapter);

        TabLayout tabLayout = findViewById(R.id.tabs);
        tabLayout.setupWithViewPager(mViewPager);
    }

    // Helper method to switch to home fragment after login
    public void switchToHomeFragment() {
        mViewPager.setCurrentItem(1);
    }

    /**
     * Adapter to manage our two fragments
     */
    public class SectionsPagerAdapter extends FragmentPagerAdapter {

        public SectionsPagerAdapter(FragmentManager fm) {
            super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 0:
                    return LoginFragment.newInstance();
                case 1:
                    return HomeFragment.newInstance();
                default:
                    return null;
            }
        }

        @Override
        public int getCount() {
            // Show 2 total pages
            return 2;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            switch (position) {
                case 0:
                    return "登录";
                case 1:
                    return "首页";
                default:
                    return null;
            }
        }
    }
}

关键注意事项

  • Control Ownership: Always reference Fragment-specific views inside the Fragment's onCreateView method using the inflated root view—never try to find them in the Activity.
  • Callback Handling: The Facebook onActivityResult must be handled in the Fragment that contains the login button, otherwise the result won't be passed correctly.
  • User State: Each Fragment can independently check the Firebase user state in onStart to update its UI dynamically.

测试验证

  1. Run the app—you'll see two slider tabs: "登录" and "首页".
  2. Tap the Facebook login button on the login tab, authorize the app, and you'll be auto-switched to the home tab with your user info displayed.
  3. Restart the app—you'll stay logged in, and the home tab will show your info immediately.

内容的提问来源于stack exchange,提问作者Milan Malovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:48:38