首页 > 解决方案 > WebView 选择文件和 Activity 重新创建

问题描述

有一个应用程序,其中用户入职流程实现为网页,并显示在嵌入式 WebView 中。其中一个流程步骤是文件上传。

它是通过<input/>在网页上使用标签来实现的,并通过WebChromeClient.onShowFileChooser(..., filePathCallback: ValueCallback<Array<Uri>>?, ...)最终触发的覆盖来进一步处理它,该触发startActivityForResult(...)应该由平台 File-Chooser-Activity 提供(对于不同的供应商/版本可能不同)。

和往常一样,一切正常,直到活动被破坏:)

问题是当 File-Chooser-Activity 在应用程序的 Activity 之上启动时,最后一个可能会被销毁(由于缺乏资源、旋转等而被系统销毁)。然后我们拥有的 WebViewfilePathCallback也将被销毁。这意味着应用程序在filePathCallback获取所选文件的 URI 时没有有效的时间(在 上onActivityResult)。

你们是如何解决这个问题的?

我已经创建了一个简单的项目,它代表了孤立的问题,请随时查看: https ://github.com/allco/WebViewFileChooseIssue

这是一些视频:

http://www.youtube.com/watch?v=9mnd0lT9lZI

案例 #2 演示了问题,即使选择完成,应用程序仍然显示“未选择文件”。

这是带有项目密钥文件的剪辑片段:

class MainActivity : AppCompatActivity() {

    companion object {
        const val REQ_CODE_CHOOSER = 1
    }

    val unencodedHtml = "<input type=file>"
    var webViewFileChooseCallback: ValueCallback<Array<Uri>>? = null

    private val webClient = object : WebViewClient() {
        ...
    }



    private val chromeClient = object : WebChromeClient() {
        ...
        override fun onShowFileChooser(
            webView: WebView,
            filePathCallback: ValueCallback<Array<Uri>>?,
            fileChooserParams: FileChooserParams?
        ): Boolean {
            if (filePathCallback == null) return false
            webViewFileChooseCallback = filePathCallback
            startFileChooserActivity("*/*")
            return true
        }
    }

    fun startFileChooserActivity(mimeType: String) {
        val intent = Intent(Intent.ACTION_GET_CONTENT)
        intent.type = mimeType
        intent.addCategory(Intent.CATEGORY_OPENABLE)

        // special intent for Samsung file manager
        val sIntent = Intent("com.sec.android.app.myfiles.PICK_DATA")
        // if you want any file type, you can skip next line
        sIntent.putExtra("CONTENT_TYPE", mimeType)
        sIntent.addCategory(Intent.CATEGORY_DEFAULT)

        val chooserIntent: Intent
        if (packageManager.resolveActivity(sIntent, 0) != null) {
            // it is device with Samsung file manager
            chooserIntent = Intent.createChooser(sIntent, "Open file")
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(intent))
        } else {
            chooserIntent = Intent.createChooser(intent, "Open file")
        }

        try {
            startActivityForResult(chooserIntent, REQ_CODE_CHOOSER)
        } catch (ex: android.content.ActivityNotFoundException) {
            Toast.makeText(applicationContext, "No suitable File Manager was found.", Toast.LENGTH_SHORT).show()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val webView = findViewById<WebView>(R.id.webView)
        webView.webChromeClient = chromeClient
        webView.webViewClient = webClient

        if (savedInstanceState == null) {
            val encodedHtml = Base64.encodeToString(unencodedHtml.toByteArray(), Base64.NO_PADDING)
            webView.loadData(encodedHtml, "text/html", "base64")
        } else {
            webView.restoreState(savedInstanceState)
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        findViewById<WebView>(R.id.webView).saveState(outState)
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        when {
            requestCode == REQ_CODE_CHOOSER && resultCode == Activity.RESULT_OK && data != null -> {
                val uri = when {
                    data.dataString != null -> arrayOf(Uri.parse(data.dataString))
                    data.clipData != null -> (0 until data.clipData!!.itemCount)
                        .mapNotNull { data.clipData?.getItemAt(it)?.uri }
                        .toTypedArray()
                    else -> null
                }
                webViewFileChooseCallback?.onReceiveValue(uri)
            }
            else -> super.onActivityResult(requestCode, resultCode, data)
        }
    }
}

标签: androidwebviewfilechooser

解决方案


推荐阅读