android-fragments 在关闭活动时,无法在onSaveInstanceState之后执行此操作

ruoxqz4g  于 2022-11-14  发布在  Android
关注(0)|答案(1)|浏览(129)

我知道这个错误是很常见的,但我找不到一个解决我的问题的方式太多的地方这个错误是提出了。
我正在开发一个应用程序来存储和排序电视节目。我有一个主要的活动与一些片段,与一个HomeFragment这是主页,与一个'添加节目'按钮,和下面的recyclerView与我所有的节目。
当点击“添加节目”按钮时,我启动了一个新的活动,以填写一个表格,然后用提供的信息创建节目。这里没有问题,它应该工作。现在我试图通过在上面提到的recyclerView中点击它们来添加编辑节目的可能性。这也带来了与“添加节目”按钮相同的活动,但这次是节目信息。
问题似乎就是从这个页面出现的。在表单Activity中,我有一个按钮,可以在其中为节目选择一个图像。编辑节目时,如果我更改图像,我不会收到错误,但如果我更改其他内容,例如名称,而不更改此图像,当单击确认按钮时,节目被正确编辑,但应用程序崩溃,并出现java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState错误。
错误似乎来自于片段部分,事务无法提交(我搜索了一段时间,所以我开始理解为什么它不工作,但不能确定是代码的哪一部分导致了这种情况)。

class HomeFragment(private val context: MainActivity) : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val view = inflater.inflate(R.layout.fragment_home, container, false)

        view.findViewById<Button>(R.id.button_add_show).setOnClickListener{
            startActivity(Intent(context, AddShowActivity::class.java))
        }
        
        val verticalRecyclerView = view.findViewById<RecyclerView>(R.id.vertical_recycler_view)
        verticalRecyclerView.adapter = ShowAdapter(context, showList, R.layout.item_show)

        return view
    }
}

这里是MainActivity的加载部分:

private fun loadFragment(fragment: Fragment){
   // Load repository
   val repo = ShowRepository()

    // Update shows list
    repo.updateData{
        // Inject fragment into fragment_container
        val transaction = supportFragmentManager.beginTransaction()
        transaction.replace(R.id.fragment_container, fragment)
        transaction.addToBackStack(null)
        transaction.commit()
    }
}

下面是我的AddShowActivity的代码,它呈现要填充的表单:

class AddShowActivity : AppCompatActivity() {
    private var fileImage: Uri? = null
    private lateinit var uploadedImage: ImageView
    private lateinit var editTextName: EditText
    private lateinit var editTextNote: EditText
    private lateinit var editTextDescription: EditText
    private lateinit var editTextReview: EditText
    private lateinit var datePicker: DatePicker
    private var currentShow: ShowModel? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_add_show)
        setupComponents()
        setupButtons()
        
        // Get show when editing
        if(intent.extras != null){
            val position = intent.extras!!.getInt("position")
            currentShow = showList[position]
        }

        initializeComponents()

    }

    private fun setupComponents() {
        editTextName = findViewById(R.id.name_input)
        editTextNote = findViewById(R.id.note_input)
        editTextDescription = findViewById(R.id.description_input)
        editTextReview = findViewById(R.id.review_input)
        uploadedImage = findViewById(R.id.preview_image)
        datePicker = findViewById(R.id.watch_date_input)
    }

    private fun setupButtons(){
        val pickupImageButton = findViewById<Button>(R.id.upload_image_button)
        pickupImageButton.setOnClickListener{
            pickupImage()
        }

        val confirmButton = findViewById<Button>(R.id.confirm_button)
        confirmButton.setOnClickListener{
            sendForm()
            val toastText = when(currentShow){
                null -> "Show added"
                else -> "Show edited"
            }
            Toast.makeText(this, toastText, Toast.LENGTH_SHORT).show()
            this.finish()
        }
    }

    @SuppressLint("NewApi")
    private fun initializeComponents() {
        if(currentShow != null){
            editTextName.setText(currentShow!!.name)
            editTextNote.setText(currentShow!!.note.toString())
            editTextDescription.setText(currentShow!!.description)
            editTextReview.setText(currentShow!!.review)
            Glide.with(this).load(Uri.parse(currentShow!!.imageUrl)).into(uploadedImage)
        }
    }

    private fun sendForm(){
        val repo = ShowRepository()
        if(fileImage == null)createShow(repo)
        else{
            if(currentShow != null)repo.deleteImage(currentShow!!)
            repo.uploadImage(fileImage!!){
                createShow(repo)
            }
        }
    }

    private fun createShow(repo: ShowRepository){
        val showName = editTextName.text.toString()
        val showNote = parseInt(editTextNote.text.toString())
        val description = editTextDescription.text.toString()
        val review = editTextReview.text.toString()
        val showWatchDate = getWatchDate(datePicker)
        val downloadImageUrl = downloadImageUri.toString()

        val show = ShowModel(UUID.randomUUID().toString(), showName, showWatchDate, showNote, downloadImageUrl, description, review)
        if(currentShow != null){
            show.id = currentShow!!.id
            repo.updateShow(show)
        }
        else repo.insertShow(show)
    }

    private fun getWatchDate(datePicker: DatePicker): String {
        var day = datePicker.dayOfMonth.toString()
        if(day.toInt() < 10)day = "0$day"
        var month = (datePicker.month + 1).toString()
        if(month.toInt() < 10)month = "0$month"
        val year = datePicker.year.toString()
        return "$day-$month-$year"
    }

    private fun pickupImage(){
        val intent = Intent()
        intent.type = "image/"
        intent.action = Intent.ACTION_GET_CONTENT
        startActivityForResult(Intent.createChooser(intent, "Select Picture"), 47)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if(resultCode == Activity.RESULT_OK && requestCode == 47){
            if(data == null || data.data == null)return
            fileImage = data.data
            uploadedImage.setImageURI(fileImage)
        }
    }
}

这里是ShowRepository,它处理与Firebase数据库的通信:

class ShowRepository {

    object Singleton{
        // Link to bucket
        private val BUCKET_URL: String = "gs://tv-memories.appspot.com"

        // Storage connexion
        val storageReference = FirebaseStorage.getInstance().getReferenceFromUrl(BUCKET_URL)

        // Database connexion
        val databaseRef = FirebaseDatabase.getInstance().getReference("shows")

        // List containing all shows
        val showList = arrayListOf<ShowModel>()

        // Contains current image link
        var downloadImageUri: Uri? = null
    }

    fun updateData(callback: () -> Unit){
        // Absorb data from databaseRef
        databaseRef.addValueEventListener(object : ValueEventListener {
            override fun onDataChange(snapshot: DataSnapshot) {
                // Remove old shows
                showList.clear()

                // Get list
                for(ds in snapshot.children){
                    //Build show object
                    val show = ds.getValue(ShowModel::class.java)

                    // Verify show isn't null
                    if(show != null){
                        // Add show to the list
                        showList.add(show)
                    }
                }
                // Activate callback
                callback()
            }

            override fun onCancelled(p0: DatabaseError) {

            }

        })
    }

    // Upload files on storage
    fun uploadImage(file: Uri, callback: () -> Unit){
        val fileName = UUID.randomUUID().toString() + ".jpg"
        val ref = storageReference.child(fileName)
        val uploadTask = ref.putFile(file)

        uploadTask.continueWithTask(Continuation<UploadTask.TaskSnapshot, Task<Uri>>{ task ->
            if(!task.isSuccessful){
                task.exception?.let{throw it}
            }

            return@Continuation ref.downloadUrl
        }).addOnCompleteListener{ task ->
            if(task.isSuccessful){
                downloadImageUri = task.result
                callback()
            }
        }
    }

    fun deleteImage(show: ShowModel){
        val photoRef: StorageReference = FirebaseStorage.getInstance().getReferenceFromUrl(show.imageUrl)
        photoRef.delete()
    }

    fun updateShow(show: ShowModel) = databaseRef.child(show.id).setValue(show)

    fun insertShow(show: ShowModel) = databaseRef.child(show.id).setValue(show)

    fun deleteShow(show: ShowModel){
        databaseRef.child(show.id).removeValue()
        deleteImage(show)
    }
}

以及错误的完整追溯:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: fr.steph.showmemories, PID: 18296
    java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
        at androidx.fragment.app.FragmentManager.checkStateLoss(FragmentManager.java:1844)
        at androidx.fragment.app.FragmentManager.enqueueAction(FragmentManager.java:1884)
        at androidx.fragment.app.BackStackRecord.commitInternal(BackStackRecord.java:329)
        at androidx.fragment.app.BackStackRecord.commit(BackStackRecord.java:294)
        at fr.steph.showmemories.MainActivity$loadFragment$1.invoke(MainActivity.kt:49)
        at fr.steph.showmemories.MainActivity$loadFragment$1.invoke(MainActivity.kt:44)
        at fr.steph.showmemories.ShowRepository$updateData$1.onDataChange(ShowRepository.kt:61)
        at com.google.firebase.database.core.ValueEventRegistration.fireEvent(ValueEventRegistration.java:75)
        at com.google.firebase.database.core.view.DataEvent.fire(DataEvent.java:63)
        at com.google.firebase.database.core.view.EventRaiser$1.run(EventRaiser.java:55)
        at android.os.Handler.handleCallback(Handler.java:873)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7078)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
mzaanser

mzaanser1#

我终于找到了解决问题的方法,即在我的loadFragment()方法中用transaction.commitAllowingStateLoss()替换transaction.commit(),以防状态已经保存。
然后我得到

private fun loadFragment(fragment: Fragment){
        // Load repository
        val repo = ShowRepository()

        // Update shows list
        repo.updateData{
            // Inject fragment into fragment_container
            val transaction = supportFragmentManager.beginTransaction()
            transaction.replace(R.id.fragment_container, fragment)
            transaction.addToBackStack(null)
            if(supportFragmentManager.isStateSaved)transaction.commitAllowingStateLoss()
            else transaction.commit()
        }
    }

相关问题