类原生系统使用uni-app input组件的focus导致安卓UI线程问题导致崩溃

类原生系统使用uni-app input组件的focus导致安卓UI线程问题导致崩溃

开发环境 版本号 项目创建方式
Windows 22631.2861 HBuilderX

示例代码:

<template>  
  <view class="search-page">  
    <!-- 搜索栏 -->  
    <view class="search-top">  
      <view class="search-box">  
        <view class="icon">  
          <image src="/static/images/search.png" />  
        </view>  
        <input  
          placeholder="输入商品名或店铺名"  
          class="input"  
          focus  
          :value="searchKeyword"  
          @input="getSearchContent"  
          @confirm="toSearchListPage"  
        >  
      </view>  
      <view  
        class="search-cancel"  
        @tap="backPreviousPage"  
      >  
        取消  
      </view>  
    </view>  

    <!-- 搜索历史 -->  
    <view  
      v-if="recentSearchHistory && recentSearchHistory.length > 0"  
      class="search-con"  
    >  
      <view class="search-tit">  
        <view class="text">  
          搜索历史  
        </view>  
        <view  
          class="del"  
          @tap="clearSearchHistory"  
        >  
          <image src="/static/images/del.png" />  
        </view>  
      </view>  
      <view class="search-history">  
        <block  
          v-for="(item, index) in recentSearchHistory"  
          :key="index"  
        >  
          <view  
            class="item"  
            :data-keyword="item"  
            @tap="clickKeywordSearch"  
          >  
            {{ item }}  
          </view>  
        </block>  
      </view>  
    </view>  

    <!-- 热门搜索 -->  
    <view  
      v-if="hotSearchList && hotSearchList.length > 0"  
      class="search-con"  
    >  
      <view class="search-tit">  
        <view class="text">  
          热门搜索  
          <view class="hot-icon">  
            <image src="/static/images/search-hot.png" />  
          </view>  
        </view>  
      </view>  
      <view class="search-hot">  
        <block  
          v-for="(item, index) in hotSearchList"  
          :key="index"  
        >  
          <view  
            class="item"  
            :data-keyword="item.content"  
            @tap="clickKeywordSearch"  
          >  
            {{ item.content }}  
          </view>  
        </block>  
      </view>  
    </view>  
  </view>  
</template>  

<script setup>  
import { reactive } from 'vue'  

const Data = reactive({  
  searchKeyword: '', // 关键词  
  // 请求的参数  
  pageQuery: {  
    shopId: 0  
  },  
  recentSearchHistory: [], // 搜索历史列表  
  hotSearchList: [] // 热门搜索列表  
})  
const { searchKeyword, recentSearchHistory, hotSearchList } = toRefs(Data)  

onLoad((options) => {  
  Data.searchKeyword = options.keyword  
})  

onShow(() => {  
  getHotSearchList()  
  getRecentSearchHistory()  
})  

/**  
 * 获取热搜列表  
 */  
const getHotSearchList = () => {  
  const params = {  
    url: '/mall4cloud_multishop/ua/app/hot_search/list',  
    method: 'GET',  
    data: Data.pageQuery  
  }  
  http.request(params).then((res) => {  
    Data.hotSearchList = res  
  })  
}  

/**  
 * 搜索店铺/商品(跳转到搜索列表页)  
 */  
const toSearchListPage = () => {  
  // 判断搜索框是否为空  
  if (!Data.searchKeyword || !Data.searchKeyword.trim()) {  
    uni.showToast({  
      title: '请输入关键字',  
      icon: 'none'  
    })  
    return  
  }  
  let recentSearchHistory = uni.getStorageSync('cloudRecentSearchHistory') || []  
  // 过滤掉重复的搜索关键词  
  recentSearchHistory = recentSearchHistory.filter(item => item !== Data.searchKeyword)  
  recentSearchHistory.unshift(Data.searchKeyword)  
  if (recentSearchHistory.length > 10) {  
    recentSearchHistory.pop()  
  }  
  // 将搜索内容放到缓存中  
  uni.setStorageSync('cloudRecentSearchHistory', recentSearchHistory)  
  // 跳转到搜索列表页  
  uni.redirectTo({  
    url: '/pages/search-list/search-list?keyword=' + Data.searchKeyword  
  })  
}  

/**  
 * 获取搜索历史  
 */  
const getRecentSearchHistory = () => {  
  Data.recentSearchHistory = uni.getStorageSync('cloudRecentSearchHistory')  
}  

/**  
 * 清空搜索历史  
 */  
const clearSearchHistory = () => {  
  uni.removeStorageSync('cloudRecentSearchHistory')  
  getRecentSearchHistory()  
}  

/**  
 * 点击历史搜索/热门搜索关键词跳转到搜索列表页  
 */  
const clickKeywordSearch = (e) => {  
  Data.searchKeyword = e.currentTarget.dataset.keyword  
  toSearchListPage()  
}  

/**  
 * 获取搜索框字符串  
 */  
const getSearchContent = (e) => {  
  Data.searchKeyword = e.detail.value  
}  

/**  
 * 返回上一页  
 */  
const backPreviousPage = () => {  
  // #ifndef H5  
  uni.navigateBack()  
  // #endif  

  // #ifdef H5  
  history.back()  
  // #endif  
}  

</script>  

<style lang="scss" scoped>  
@use "search-page";  
</style>

操作步骤:

  • 我的项目中任何一个页面有focus都会在第二次进入时发生崩溃,但新建项目不会。但是我项目太复杂我没有时间去排查

预期结果:

  • 聚焦后没有异常

实际结果:

  • 聚焦后应用崩溃

bug描述:

<input  
  class="in"  
  type="text"  
  focus  
  v-model="name"  
  maxlength="33"  
/>

进入页面后会返回第二次进入立即聚焦输入框从而导致软件崩溃

FATAL EXCEPTION: Timer-1
Process: uni.app.UNI5098048, PID: 28164
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. Expected: main Calling: Timer-1
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:11213)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:2551)
at android.view.ViewRootImpl.notifyInsetsChanged(ViewRootImpl.java:2513)
at android.view.ViewRootInsetsControllerHost.notifyInsetsChanged(ViewRootInsetsControllerHost.java:56)
at android.view.InsetsController.notifyVisibilityChanged(InsetsController.java:1884)
at android.view.InsetsController.onAnimationStateChanged(InsetsController.java:1845)
at android.view.InsetsController.controlAnimationUncheckedInner(InsetsController.java:1563)
at android.view.InsetsController.controlAnimationUnchecked(InsetsController.java:1357)
at android.view.InsetsController.applyAnimation(InsetsController.java:2048)
at android.view.InsetsController.applyAnimation(InsetsController.java:2022)
at android.view.InsetsController.show(InsetsController.java:1177)
at android.view.inputmethod.InputMethodManager.showSoftInput(InputMethodManager.java:2426)
at android.view.inputmethod.InputMethodManager.showSoftInput(InputMethodManager.java:2376)
at android.view.inputmethod.InputMethodManager.toggleSoftInput(InputMethodManager.java:3191)
at io.dcloud.common.adapter.util.DeviceInfo$3.run(Unknown Source:6)
at java.util.TimerThread.mainLoop(Timer.java:634)
at java.util.TimerThread.run(Timer.java:577) 

更多关于类原生系统使用uni-app input组件的focus导致安卓UI线程问题导致崩溃的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于类原生系统使用uni-app input组件的focus导致安卓UI线程问题导致崩溃的实战教程也可以访问 https://www.itying.com/category-93-b0.html


这是一个典型的Android UI线程冲突问题。从错误堆栈可以看出,Timer-1线程试图操作UI组件,违反了Android的UI线程规则。

问题出现在focus属性的使用上。当页面第二次进入时,uni-app内部可能通过定时器触发输入框聚焦,但这个定时器运行在非UI线程,导致崩溃。

解决方案:

  1. 移除focus属性,改为手动触发聚焦:
<input
  ref="searchInput"
  placeholder="输入商品名或店铺名"
  class="input"
  :value="searchKeyword"
  @input="getSearchContent"
  @confirm="toSearchListPage"
>
  1. onReadyonShow中使用$nextTick延迟聚焦
onShow(() => {
  getHotSearchList()
  getRecentSearchHistory()
  
  // 延迟聚焦输入框
  setTimeout(() => {
    if (this.$refs.searchInput) {
      this.$refs.searchInput.focus()
    }
  }, 100)
})
  1. 或者使用uni-app的聚焦方法
onShow(() => {
  getHotSearchList()
  getRecentSearchHistory()
  
  setTimeout(() => {
    const input = uni.createSelectorQuery().select('.input')
    input.focus().exec()
  }, 100)
})
回到顶部