为何onWindowFocusChanged未触发?clipDataOnResume在onActivityResult偶现空值
问题描述
我尝试用ZXing二维码结合摄像头实现网络共享剪贴板内容,原本用onWindowFocusChanged事件获取剪贴板内容大部分情况正常,但clipDataOnResume成员偶尔会在onActivityResult中为空,此时会弹出Toast提示"clipDataOnResume is null at onActivityResult"。请问该如何避免这个问题?
使用的依赖
implementation("com.google.zxing:core:3.4.0") implementation("com.journeyapps:zxing-android-embedded:4.1.0")
MainActivity 代码
class MainActivity : AppCompatActivity() { var clipDataOnResume: ClipData? = null private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets -> val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars()) v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom) insets } binding.btnScan.setOnClickListener { val scanner = IntentIntegrator(this) scanner.setOrientationLocked(true) scanner.setPrompt("Scan a barcode.") scanner.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE) scanner.setBarcodeImageEnabled(true) scanner.initiateScan() } } fun sendContentTask(url: String, clipboardContent: String): Int { // Send clipboard content over network } fun getContentTask(url: String): String { // Get clipboard content over network } public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data) if (result != null) { if (result.getContents() == null) { Toast.makeText(this, "Cancelled from fragment", Toast.LENGTH_LONG).show() } else { if (result.getContents().endsWith("post")) { val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager var clipContent = "" if (manager.hasPrimaryClip()) { val clipData = manager.primaryClip val item = clipData!!.getItemAt(0) clipContent = item.text.toString() } else if (clipDataOnResume != null && clipDataOnResume!!.itemCount > 0) { val item = clipDataOnResume!!.getItemAt(0) clipContent = item.text.toString() } if (clipContent == ""){ Toast.makeText(this, "clipDataOnResume is null at onActivityResult", Toast.LENGTH_LONG).show() } else { val url = result.getContents(); Toast.makeText(this, "Location: " + url, Toast.LENGTH_LONG).show() runBlocking { var result: Deferred<Unit> = async() { withContext(Dispatchers.IO) { val msg = sendContentTask(url, clipContent) } } result.await() } } } else if (result.getContents().endsWith("get")) { val url = result.getContents() Toast.makeText(this, "Location: " + url, Toast.LENGTH_LONG).show() runBlocking { var msg = "" var result: Deferred<Unit> = async() { withContext(Dispatchers.IO) { msg = getContentTask(url) } } result.await() setClipboardContent(msg) } } } } } override fun onWindowFocusChanged(hasFocus: Boolean) { super.onWindowFocusChanged(hasFocus) if (hasFocus) { val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager clipDataOnResume = manager.primaryClip if (clipDataOnResume == null) { Toast.makeText(this, "clipDataOnResume is null at focus", Toast.LENGTH_LONG).show() } } } }
解决方案
1. 换用onResume保存剪贴板数据
onWindowFocusChanged触发时机不稳定(系统弹窗、多窗口切换都会干扰),改用onResume方法——Activity回到前台时一定会执行这个生命周期方法,能稳定获取剪贴板内容:
override fun onResume() { super.onResume() val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager clipDataOnResume = manager.primaryClip }
2. 点击扫描按钮时立即缓存剪贴板
在启动扫码Activity前,直接获取当前剪贴板内容缓存,确保数据是最新且可靠的:
binding.btnScan.setOnClickListener { // 点击时立即缓存剪贴板内容 val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager clipDataOnResume = manager.primaryClip val scanner = IntentIntegrator(this) scanner.setOrientationLocked(true) scanner.setPrompt("Scan a barcode.") scanner.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE) scanner.setBarcodeImageEnabled(true) scanner.initiateScan() }
3. 增加多层空值兜底逻辑
在onActivityResult中,即使缓存为空,也再次尝试获取剪贴板,避免直接报错:
if (result.getContents().endsWith("post")) { val manager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager var clipContent = "" // 优先直接获取当前剪贴板 if (manager.hasPrimaryClip() && manager.primaryClip?.itemCount ?: 0 > 0) { val item = manager.primaryClip!!.getItemAt(0) clipContent = item.text.toString() ?: "" } // 缓存内容兜底 else if (clipDataOnResume != null && clipDataOnResume!!.itemCount > 0) { val item = clipDataOnResume!!.getItemAt(0) clipContent = item.text.toString() ?: "" } // 最后再尝试一次获取 else { val latestClip = manager.primaryClip if (latestClip?.itemCount ?:0 >0) { clipContent = latestClip!!.getItemAt(0).text.toString() ?: "" } } if (clipContent.isEmpty()){ Toast.makeText(this, "无法获取剪贴板内容", Toast.LENGTH_LONG).show() } else { val url = result.getContents(); Toast.makeText(this, "地址: " + url, Toast.LENGTH_LONG).show() // 替换runBlocking,用lifecycleScope避免阻塞主线程 lifecycleScope.launch { withContext(Dispatchers.IO) { sendContentTask(url, clipContent) } } } }
4. 替换runBlocking避免主线程阻塞
runBlocking会卡死UI线程,改用lifecycleScope启动协程,先添加依赖(如果没有的话):
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
然后直接用lifecycleScope替换原有的runBlocking代码,保证界面流畅。
内容的提问来源于stack exchange,提问作者Papp Zoltán
相关产品推荐
相关产品推荐

