【问题标题】:Android Read/Write large file when activity is destoyedActivity被销毁时Android读/写大文件
【发布时间】:2020-12-25 21:53:11
【问题描述】:

当活动或片段被销毁时,我想编写一个 1-10MB 的大文件。因为我想在用户关闭应用程序时存储文件,所以我必须在 onPause() 方法中执行此操作。我使用 DataOutputStream 因为它有编写不同类型的方法:Integer、Float、ByteArray 等等。

假设我需要编写一个大文件,这需要 2-3 秒。 onPause() 方法中的代码在主线程上执行。假设我使用 Thread.sleep(6000) 将线程冻结 6 秒以模拟线程工作。如果用户尝试返回应用程序,应用程序在这 6 秒内没有响应,因为所有工作都在主线程中完成。

如果我启动一个在单独的线程上运行任务的 AsyncTask 并在那里写入文件,那么冻结 UI 的问题就解决了。但据我所知,如果任务耗时6s,并且应用程序在线程完成工作之前就被销毁,内存泄漏问题很大!

所以我的问题是当用户即将关闭应用程序并防止内存泄漏时,如何在单独的线程上编写一个大文件??? 以下是在主线程上编写所有内容的示例代码:

override fun onStop() {
    // code is run in the main thread
    saveFile(context!!, "test", "testFile.txt")
}

fun saveFile(context: Context, fileDirectory: String, fileName: String) {

        // path to /data/data/yourAppName/app_data/imageDir
        val directory: File = context.getDir(fileDirectory, Context.MODE_PRIVATE)

        val file = File(directory, fileName)
        val fileOutputStream: FileOutputStream
        try {

            fileOutputStream = FileOutputStream(file)
            val dataOutputStream = DataOutputStream(fileOutputStream)

            dataOutputStream.writeInt(11)                       // write integer
            dataOutputStream.writeFloat(1.8f)                   // write float
            dataOutputStream.write(byteArrayOf(1, 33, 124, 41)) // write byte array

            dataOutputStream.flush()
            dataOutputStream.close()
            fileOutputStream.flush()
            fileOutputStream.close()

        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
    }

因为我需要上下文来生成文件,并且如果活动在线程完成工作之前被破坏,这将导致内存泄漏。如果我将上下文包装在 WeakReference 中,是否可以保护它免受内存泄漏?即使活动/片段被破坏,线程也会完成它的工作吗??

下面是使用 AsyncTask 的示例,并将上下文包装在 WeakReference 中!

class SaveFile(context: Context, var fileDirectory: String, var fileName: String) : AsyncTask<Void, Void, Int>() {

            private var contextWrapper: WeakReference<ContextWrapper>

            init {
                val contextWrapper = ContextWrapper(context)
                this.contextWrapper = WeakReference(contextWrapper)
            }

            override fun doInBackground(vararg params: Void): Int {
                val directory: File = contextWrapper.get()!!.getDir(fileDirectory, Context.MODE_PRIVATE)

                val file = File(directory, fileName)
                val fileOutputStream: FileOutputStream
                try {

                    fileOutputStream = FileOutputStream(file)
                    val dataOutputStream = DataOutputStream(fileOutputStream)

                    dataOutputStream.writeInt(11)                     // write integer
                    dataOutputStream.writeFloat(1.8f)                 // write float
                    dataOutputStream.write(byteArrayOf(1, 33, 124, 41)) // write byte array

                    dataOutputStream.flush()
                    dataOutputStream.close()
                    fileOutputStream.flush()
                    fileOutputStream.close()

                } catch (e: java.lang.Exception) {
                    e.printStackTrace()
                }
                
                return 0
            } 
        }

【问题讨论】:

  • 这是您使用服务的地方。在onPauseonDestroy 上启动服务。
  • 所以我应该使用 IntentService 向用户显示他的工作正在被保存的消息。
  • 通知可能会更好。
  • 使用线程而不是 AsyncTask。但是为什么有些内存泄​​漏会困扰你呢?
  • @blackapps 内存泄漏应始终被视为严重问题。

标签: android file kotlin


【解决方案1】:

如果你想确保你没有持有Activity 或其他任何东西,你可以使用applicationContext(你可以从任何Context 获得)。但是多持有Activity 几秒钟并不是真正的泄漏 - 无论如何都不能保证你甚至会在那个时间跨度内获得垃圾收集事件!这是您需要小心的长期参考。

您的WeakReference 想法确实通过在没有其他东西持有它时释放Context 来防止内存泄漏 - 问题是您将它视为它总是返回上下文,通过说

contextWrapper.get()!!

如果您的弱引用完成其工作并释放上下文,此调用将返回 null,并且您的非 null 断言 (!!) 失败并导致您的应用崩溃。因此,您正在将潜在的内存使用问题换成更大的问题!

如果你曾经做过这种事情,你需要处理带有弱引用的对象被垃圾回收的情况 - 这就是你对弱引用所做的事情,所以你不能保证您可以访问它。

所以你想要这样的东西

val directory: File? = contextWrapper.get()?.getDir(fileDirectory, Context.MODE_PRIVATE)
if (directory == null) return
...

如何处理“无上下文”路径取决于您,这里我只是放弃文件内容。如果这不是一个选项,并且您需要 Context 以便您可以做一些重要的事情,那么您不能只持有一个弱引用。

我知道您已经解决了一些关于服务的问题,但了解这一点很重要!

【讨论】:

  • 谢谢!是的,你是绝对正确的,我完全忘记了上下文有可能为空。我现在遇到了服务问题,您可以查看我的最新问题,以防您过去遇到同样的问题并且您设法解决了它。
  • 老实说,永远不要使用 !! - 上下文 可以 为空,这就是为什么它是可空的 Context? 类型,空安全系统和 linter 会警告你你需要处理它。使用!! 会打破这一点,并告诉系统您知道得更好 - 在极少数情况下您会这样做,但作为一项规则,您应该完全避免使用它!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多