setPersistenceEnabled(true) 使应用程序崩溃

Posted

技术标签:

【中文标题】setPersistenceEnabled(true) 使应用程序崩溃【英文标题】:setPersistenceEnabled(true) crashes app 【发布时间】:2016-09-23 17:02:36 【问题描述】:

我正在创建我的第一个 Firebase 应用。它的要求之一是它在网络不可用时运行。 Firebase 指南指出:

启用磁盘持久性使我们的应用即使在应用重新启动后也能保持其所有状态。我们只需一行代码就可以启用磁盘持久性。 FirebaseDatabase.getInstance().setPersistenceEnabled(true); 启用磁盘持久性后,我们同步的数据和写入将在应用重新启动时持久保存到磁盘,并且我们的应用应该在离线情况下无缝运行。

另一个要求是使用 Google 登录。所以在我的MainActivity 中,我检查用户是否登录,如果没有,我启动SignInActivity。 (SignInActivity 来自 Firebase 示例。)SignInActivity 工作,用户登录,MainActivity 第二次启动。现在我的应用程序在代码行 FirebaseDatabase.getInstance().setPersistenceEnabled(true); 上崩溃并显示以下消息:

必须在使用 FirebaseDatabase 实例之前调用 setPersistenceEnabled()。

现在,如果我重新启动我的应用程序,用户已登录,SignInActivity 未启动,我的应用程序运行正常。

关于在用户登录后如何避免此崩溃的任何建议?

当我发布这个问题时,我收到了重新定位FirebaseDatabase.getInstance().setPersistenceEnabled(true); 的建议 到我的“应用程序类”。我得到了完全相同的结果……SignInActivity 开始、完成,然后我在setPersistenceEnabled 上崩溃了。

下面是我的 MainActivity onCreate:

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

    // Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
    // Crash here upon returning from SignInActivity.  
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);
    mFirebaseDbReference = FirebaseDatabase.getInstance().getReference();

    // Initialize Firebase Auth
    mFirebaseAuth = FirebaseAuth.getInstance();
    mFirebaseUser = mFirebaseAuth.getCurrentUser();
    if (mFirebaseUser == null) 
        // Not signed in, launch the Sign In activity
        Timber.tag("MainActivity").i("onCreate(): User not signed in, launching SignInActivity");
        startActivity(new Intent(this, SignInActivity.class));
        finish();

     else 
        mUsername = mFirebaseUser.getDisplayName();
        Timber.tag("MainActivity").i("onCreate(): User \"%s\" signed in.", mUsername);
        if (mFirebaseUser.getPhotoUrl() != null) 
            mPhotoUrl = mFirebaseUser.getPhotoUrl().toString();
        
     

【问题讨论】:

【参考方案1】:

FirebaseApp 由 ContentProvider 初始化,因此在调用 onCreate() 时它不会被初始化。

像这样获取您的 FirebaseDatabase:

public class Utils 
    private static FirebaseDatabase mDatabase;

    public static FirebaseDatabase getDatabase() 
       if (mDatabase == null) 
          mDatabase = FirebaseDatabase.getInstance();
          mDatabase.setPersistenceEnabled(true);
       
       return mDatabase;
    


然后从您想要的任何活动中调用Utils.getDatabase()

Read more in this article

【讨论】:

我想你是从这里拿来的...github.com/firebase/quickstart-android/issues/15 我现在得到这个:“服务 com.google.android.gms.chimera.GmsIntentOperationService 已泄露 ServiceConnection”【参考方案2】:

只需将 ExampleFragment.class 中的代码从 onCreateView 方法移动到 onCreate 方法:

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

    FirebaseDatabase.getInstance().setPersistenceEnabled(true);



@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) 

     ....

    // Inflate the layout for this fragment

    View view =  inflater.inflate(R.layout.fragment_home, container, false);

【讨论】:

【参考方案3】:

我通过在 Application 类中使用 setPersistenceEnabled(true) 修复了此异常。

public class MApplication extends Application 

    @Override
    public void onCreate() 
        super.onCreate();
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
    

在AndroidManifest.xml中,设置应用名称为MApplication:

<application
    android:name=".MApplication"
    ... />

【讨论】:

只是复制和过去 @EfrainSanjayAdhikary 是的,我花了很多时间才找到为什么我的数据没有存储在本地。 :-D 如果您仍然面临崩溃,请参阅此处:github.com/firebase/quickstart-android/issues/… 我就是这么想的。【参考方案4】:

对于 Kotlin 试试这个:

class DatabaseUtil 

    companion object 
        private val firebaseDatabase: FirebaseDatabase = FirebaseDatabase.getInstance()

        init 
            firebaseDatabase.setPersistenceEnabled(true)
        

        fun getDatabase() : FirebaseDatabase 
            return firebaseDatabase
        
    

【讨论】:

【参考方案5】:

确保 .setpersistenceenabled(true) 不会发生两次,而在您的情况下由 Google 登录时,必须在调用此方法的任何 firebase 实例解决我的问题之前调用第二次护理 setPersistenceEnabled(true)。

【讨论】:

【参考方案6】:

创建一个名为 Util.java 的类

并添加以下代码

public class Util 

        private static FirebaseDatabase mData;

        public static FirebaseDatabase getDatabase() 
            if (mData == null) 

                mData = FirebaseDatabase.getInstance();
                mData.setPersistenceEnabled(true);
            
            return mData;
        



现在将 FirebaseDatabase.getIntance() 替换为 Util.getDatabase() 每个活动中的每次。 只调用一次就会出错!

【讨论】:

【参考方案7】:

我不建议使用 Application 来存储数据,因为它就像在 CodePath 中编写的一样

在您的应用中的许多地方总是需要数据和信息。这可能是会话令牌、昂贵计算的结果等。使用应用程序实例可能很诱人,以避免在活动之间传递对象或将对象保存在持久存储中的开销。

但是,您永远不应该将可变实例数据存储在 Application 对象中,因为如果您假设您的数据将保留在那里,那么您的应用程序将不可避免地在某个时候崩溃并出现 NullPointerException。应用程序对象不能保证永远留在内存中,它会被杀死。与流行的看法相反,该应用程序不会从头开始重新启动。 Android 将创建一个新的 Application 对象并在用户之前所在的位置启动 Activity,从而给人一种应用程序从一开始就没有被杀死的错觉。

这就是我推荐使用这样的 Singleton 的原因:

public class DataBaseUtil 

private static FirebaseDatabase mDatabase;

public static FirebaseDatabase getDatabase() 
    if (mDatabase == null) 
        mDatabase = FirebaseDatabase.getInstance();
        mDatabase.setPersistenceEnabled(true);
    
    return mDatabase;

只需在你的代码中使用它然后喜欢

private FirebaseDatabase fdb = DataBaseUtil.getDatabase();

【讨论】:

【参考方案8】:

在清单中

 android:name=".AppName

创建扩展应用程序的 java 类

public class AppName extends Application 

@Override
public void onCreate() 
    super.onCreate();
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);


【讨论】:

【参考方案9】:

你可以使用:

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

在使用FirebaseDatabase.getInstance().getReference();之前

【讨论】:

在原始问题的代码中,setPersistenceEnabled 似乎已经在 FirebaseDatabase.getInstance().getReference() 之前被调用了......我误解了你的答案吗?【参考方案10】:

对我来说,为 Firebase 创建一个单独的类会更容易处理。这是因为 Firebase 有自己的实例,如果您在多个活动中使用它,如果您在另一个活动中再次调用 setPersistenceEnabled,它可能会崩溃。

另一个好处是,如果需要,您可以将上下文或参数传递给 FirebaseHandler 构造函数。或者,如果您在数据库中有固定位置,则可以在没有 .child("location") 样板的情况下轻松调用它们。

示例:

public class FirebaseHandler 

    // parameters
    private Context context;
    private String userKey;
    private DatabaseReference databaseReference;
    private static boolean isPersistenceEnabled = false;
    private static String fixedLocationA = "locationA";
    private static String fixedLocationB = "locationB";

    public FirebaseHandler(Context context, String userKey) 
        this.context = context;    // context can be used to call PreferenceManager etc.
        this.userKey = userKey;
        if (!isPersistenceEnabled) 
            FirebaseDatabase.getInstance().setPersistenceEnabled(true);
            isPersistenceEnabled = true;
        
        databaseReference = FirebaseDatabase.getInstance().getReference().child(userKey);
    

    public DatabaseReference getRefA() 
        return databaseReference.child(fixedLocationA);
    

    public DatabaseReference getRefB() 
        return databaseReference.child(fixedLocationB);
    

然后可以在任何活动中调用它,如下所示。

public class MyActivity extends AppCompatActivity 

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

        // get instance
        FirebaseHandler firebaseHandler = new FirebaseHander(this, "userKey");
        // to set value
        firebaseHandler.getRefA().setValue("value");

        // to set listener
        firebaseHandler.getRefB().addValueEventListener(new ValueEventListener() 
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) 
                // TODO here....

                // also, can remove listener if required
                if (certain condition) 
                    firebaseHandler.getRefB().removeEventListener(this);
                
            
        
    

【讨论】:

【参考方案11】:

错误信息描述了问题:

调用 setPersistenceEnabled() 必须在任何其他使用之前进行 FirebaseDatabase 实例。

文档中描述了此问题的修复方法:与 SDK 2.x 中一样,必须在对数据库进行其他调用之前启用磁盘持久性。

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

https://firebase.google.com/support/guides/firebase-android

【讨论】:

【参考方案12】:

如果您不喜欢静态字段,这对我有用:

if (FirebaseApp.getApps(context).isEmpty()) 
     FirebaseApp.initializeApp(context);
     FirebaseDatabase.getInstance().setPersistenceEnabled(true);

这可能是由于一个进程初始化了两次 firebase 或 Multidex 应用程序。欲了解更多信息,请参阅:https://github.com/firebase/quickstart-android/issues/15

【讨论】:

【参考方案13】:

我有点晚了,但今天我遇到了这个问题,我通过添加解决了

static 
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);

到我的活动

【讨论】:

最简单最有效的答案! 最简单的解决方案 非常好的和简单的解决方案,但请详细说明这是如何解决问题的。我是否需要为我在其中使用 firebase 的每个活动添加此代码?【参考方案14】:

我遇到了同样的问题。我将代码更改如下。

之前(导致崩溃)

    var rootRef = FIRDatabase.database().reference()

    override func viewDidLoad() 
       super.viewDidLoad()
       FIRDatabase.database().persistenceEnabled = true
    

之后(已解决的崩溃)

var rootRef:FIRDatabaseReference!
override func viewDidLoad() 
 super.viewDidLoad()
   FIRDatabase.database().persistenceEnabled = true
   rootRef = FIRDatabase.database().reference()

【讨论】:

【参考方案15】:

通过使 Firebase 引用像这样的静态类字段来解决它:

public class MainActivity extends AppCompatActivity

private static FirebaseDatabase fbDatabase;

@Override
    protected void onCreate(Bundle savedInstanceState) 

if(fbDatabase == null) 
            fbDatabase = FirebaseDatabase.getInstance();
            fbDatabase.setPersistenceEnabled(true);
        

在其他活动中创建新的 Firebase 引用(没有 setPersistenceEnabled(true))也没有问题。

【讨论】:

【参考方案16】:

我也遇到了一些问题,但这是我的应用程序的临时解决方案。

Create BaseActivity 扩展 AppcompatActivity 并覆盖 onCreate,将setPersistenceEnabled 放在那里。

public class BaseActivity extends AppCompatActivity 

    private static String TAG = "BaseActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        try
            FirebaseDatabase.getInstance().setPersistenceEnabled(true);
            Log.d(TAG,FirebaseDatabase.getInstance().toString());
        catch (Exception e)
            Log.w(TAG,"SetPresistenceEnabled:Fail"+FirebaseDatabase.getInstance().toString());
            e.printStackTrace();
        
    

并更改 MainActivity 以扩展 BaseActivity

public class MainActivity extends BaseActivity

编辑: 关注@imakeApps 回答

public class BaseActivity extends AppCompatActivity 

    private static String TAG = "BaseActivity";

    static boolean isInitialized = false;

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

        try
            if(!isInitialized)
                FirebaseDatabase.getInstance().setPersistenceEnabled(true);
                isInitialized = true;
            else 
                Log.d(TAG,"Already Initialized");
            
        catch (Exception e)
            e.printStackTrace();
        
    

【讨论】:

感谢 Chip ... setPersistenceEnabled 存在的唯一地方目前在我的 MainActivity 中,它没有在我的 SignInActivity 中调用。 Chip ...您建议用 try/catch 包围 setPersistenceEnable,然后记录错误并继续。您是否验证过数据实际上是否在本地持久化? @Loren 我的应用程序仅在我关闭并再次打开时崩溃,我认为它仍在离线工作,因为我尝试启用飞行模式并且我的数据仍然存在于我的应用程序中。【参考方案17】:

我遇到了类似的问题,使用静态变量似乎为我解决了这个问题。所以起初我的代码看起来像这样

@Override
protected void onCreate(Bundle savedInstanceState) 
    //..code

    FirebaseDatabase.getInstance().setPersistenceEnabled(true);
    FirebaseDatabase database = FirebaseDatabase.getInstance();

    //..code

现在看起来更像

static boolean calledAlready = false;

@Override
protected void onCreate(Bundle savedInstanceState) 
    //..code

    if (!calledAlready)
    
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        calledAlready = true;
    

    FirebaseDatabase database = FirebaseDatabase.getInstance();

    //..code

希望对你有帮助!

【讨论】:

这对我有用。 Mind calledAlready 必须是静态的才能工作。

以上是关于setPersistenceEnabled(true) 使应用程序崩溃的主要内容,如果未能解决你的问题,请参考以下文章

Firebase 离线功能和 addListenerForSingleValueEvent

带有 RecyclerView 的片段:java.lang.IllegalArgumentException:报废或附加的视图可能不会被回收。 isScrap:false isAttached:tru

更快地加载firebase图像数据

注销后清除 Firebase 持久性

将Firebase Persistence Enabled设置为true后,不检索新数据?

如果没有更新,firebase在线数据库如何工作?