Android Studio中ViewPager+Fragment集成Firebase+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
onCreateViewmethod using the inflated root view—never try to find them in the Activity. - Callback Handling: The Facebook
onActivityResultmust 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
onStartto update its UI dynamically.
测试验证
- Run the app—you'll see two slider tabs: "登录" and "首页".
- 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.
- Restart the app—you'll stay logged in, and the home tab will show your info immediately.
内容的提问来源于stack exchange,提问作者Milan Malovic

