android - 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)
}
}
}
解决方案
推荐阅读
- python - Python 将相关记录附加到相关人员
- oauth - Oauth 令牌和信用
- python - 在具有唯一 ID 的数据帧上计算累积产品
- c# - 将字符串解析为日期时间格式
- python - 创建一个 Python 脚本,该脚本将读取 csv 文件并使用该输入从 finviz.com 网页抓取数据,然后将数据导出到 csv 文件中
- php - 如何在 phpunit 模拟中模拟请求方法?
- java - 从 int 转换为 char 数组。索引不正确?
- javascript - 从更新查询中读取 XLSX
- matlab - 八度打印到 cmd 提示符
- javascript - Firebase功能完成后如何修复在HTML5中检索数据?