为什么我的Android应用程序在使用后退按钮导航并从互联网加载数据时跳过帧? (导航组件)

问题描述 投票:0回答:1

所以我有一个底部导航设置,其中包含5个带有Navigation Components的片段。当应用启动时,将数据加载到回收器视图中时,globalFragment会跳过90多个帧。当我转到另一个片段并按返回按钮(将我带到globalFragment)时,它再次跳过帧(> 100),但是当我转到我的最后一个片段(即首选项片段)并按回去时,它的应用程序挂起并跳过2500+框架。

我尝试过的

从适配器中删除的数据绑定似乎解决了应用程序启动时跳过初始帧的问题,但是当我从设置片段中导航回来时,应用程序仍然跳过了2500帧

我删除了首选项片段,现在该应用程序未挂起,但返回到全局片段时仍会跳过帧

我从适配器中删除了单击侦听器,并从onBind中删除了其他不必要的逻辑,但问题仍然存在

该项目是开源的,因此您可以在此处查看https://github.com/destructo570/CovidTracker-kotlin

这是我的Logcat

2020-05-20 13:19:09.658 26583-26583/com.destructo.covidtracker 
W/to.covidtracke: Accessing hidden method 
Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V 
(greylist, reflection, allowed)
2020-05-20 13:19:09.675 26583-26583/com.destructo.covidtracker 
D/NetworkSecurityConfig: No Network Security Config specified, using platform 
default
2020-05-20 13:19:09.678 26583-26583/com.destructo.covidtracker 
W/to.covidtracke: Accessing hidden method Ldalvik/system/CloseGuard;- 
>get()Ldalvik/system/CloseGuard; (greylist,core-platform-api, reflection, 
allowed)
2020-05-20 13:19:09.678 26583-26583/com.destructo.covidtracker 
W/to.covidtracke: Accessing hidden method Ldalvik/system/CloseGuard;- 
>open(Ljava/lang/String;)V (greylist,core-platform-api, reflection, allowed)
2020-05-20 13:19:09.678 26583-26583/com.destructo.covidtracker 
W/to.covidtracke: Accessing hidden method Ldalvik/system/CloseGuard;- 
>warnIfOpen()V (greylist,core-platform-api, reflection, allowed)
2020-05-20 13:19:10.163 26583-26583/com.destructo.covidtracker 

I/Choreographer: Skipped 41 frames!  The application may be doing too much 
work on its main thread.
2020-05-20 13:19:10.168 26583-26583/com.destructo.covidtracker W/Looper: Slow 
Looper main: doFrame is 686ms late because of 27 msg, msg 1 took 188ms 
(seq=38 running=106ms runnable=6ms late=134ms 
h=android.view.Choreographer$FrameHandler 
c=android.view.Choreographer$FrameDisplayEventReceiver), msg 24 took 451ms 
(seq=61 running=425ms runnable=2ms late=220ms h=android.os.Handler 
c=kotlinx.coroutines.DispatchedContinuation)
2020-05-20 13:19:13.541 26583-26583/com.destructo.covidtracker W/Looper: Slow 
Looper main: Long Msg: seq=116 plan=13:19:12.044  late=1ms wall=1495ms 
running=1411ms runnable=5ms h=android.view.Choreographer$FrameHandler 
c=android.view.Choreographer$FrameDisplayEventReceiver
2020-05-20 13:19:13.543 26583-26583/com.destructo.covidtracker 

I/Choreographer: Skipped 87 frames!  The application may be doing too much 
work on its main thread.
2020-05-20 13:19:13.543 26583-26639/com.destructo.covidtracker 
I/OpenGLRenderer: Davey! duration=1498ms; Flags=0, 
IntendedVsync=645628150832172, Vsync=645628150832172, 
OldestInputEvent=9223372036854775807, NewestInputEvent=0, 
HandleInputStart=645628151894908, AnimationStart=645628152051210, 
PerformTraversalsStart=645628152055168, DrawStart=645629621464439, 
SyncQueued=645629644963710, SyncStart=645629645792251, 
IssueDrawCommandsStart=645629646424022, SwapBuffers=645629648726366, 
FrameCompleted=645629649800324, DequeueBufferDuration=188000, 
QueueBufferDuration=749000, 
2020-05-20 13:19:13.545 26583-26583/com.destructo.covidtracker W/Looper: Slow 
Looper main: doFrame is 1465ms late because of 13 msg, msg 1 took 1495ms 
(seq=116 running=1411ms runnable=5ms late=1ms 
h=android.view.Choreographer$FrameHandler 
c=android.view.Choreographer$FrameDisplayEventReceiver)
2020-05-20 13:19:16.918 26583-26632/com.destructo.covidtracker 
I/to.covidtracke: ProcessProfilingInfo new_methods=6942 is saved 
saved_to_disk=1 resolve_classes_delay=8000
2020-05-20 13:19:40.514 26583-26583/com.destructo.covidtracker 
D/ViewRootImpl: [TouchInput][ViewRootImpl] KeyEvent { action=ACTION_DOWN, 
keyCode=KEYCODE_BACK, scanCode=0, metaState=0, flags=0x48, repeatCount=0, 
eventTime=645656609, downTime=645656609, deviceId=-1, source=0x101, 
displayId=-1 }
2020-05-20 13:19:40.650 26583-26583/com.destructo.covidtracker 
D/ViewRootImpl: [TouchInput][ViewRootImpl] KeyEvent { action=ACTION_UP, 
keyCode=KEYCODE_BACK, scanCode=0, metaState=0, flags=0x48, repeatCount=0, 
eventTime=645656753, downTime=645656609, deviceId=-1, source=0x101, 
displayId=-1 }
2020-05-20 13:19:41.155 26583-26601/com.destructo.covidtracker 
I/to.covidtracke: Background concurrent copying GC freed 197245(8256KB) 
AllocSpace objects, 4(72KB) LOS objects, 49% free, 22MB/45MB, paused 77us 
total 121.932ms
2020-05-20 13:20:11.708 26583-26583/com.destructo.covidtracker W/Looper: Slow 
Looper main: Long Msg: seq=2203 plan=13:19:40.690  late=0ms wall=31018ms 
running=29292ms runnable=126ms h=android.view.Choreographer$FrameHandler 
c=android.view.Choreographer$FrameDisplayEventReceiver
2020-05-20 13:20:11.709 26583-26583/com.destructo.covidtracker W/Looper: Slow 
Looper main: MotionEvent is 10361ms late (event_seq=4, action=ACTION_DOWN) 
because of 1 msg, msg 1 took 31018ms (seq=2203 running=29292ms runnable=126ms 
h=android.view.Choreographer$FrameHandler 
c=android.view.Choreographer$FrameDisplayEventReceiver)
2020-05-20 13:20:11.709 26583-26583/com.destructo.covidtracker 
W/InputEventReceiver: App Input: 10361ms before dispatchInputEvent 
(MotionEvent: event_seq=4, seq=1959042, action=ACTION_DOWN)
2020-05-20 13:20:11.711 26583-26639/com.destructo.covidtracker 
I/OpenGLRenderer: Davey! duration=31020ms; Flags=1, 
IntendedVsync=645656796004117, Vsync=645656796004117, 
OldestInputEvent=9223372036854775807, NewestInputEvent=0, 
HandleInputStart=645656796923387, AnimationStart=645656796961876, 
PerformTraversalsStart=645656797074585, DrawStart=645687793910146, 
SyncQueued=645687812587698, SyncStart=645687813233010, 
IssueDrawCommandsStart=645687813928896, SwapBuffers=645687815157698, 
FrameCompleted=645687817258583, DequeueBufferDuration=189000, 
QueueBufferDuration=1646000, 
2020-05-20 13:20:11.714 26583-26583/com.destructo.covidtracker 
W/InputEventReceiver: App Input: 8571ms before dispatchInputEvent 
(MotionEvent: event_seq=5, seq=1959127, action=ACTION_CANCEL)
2020-05-20 13:20:11.715 26583-26583/com.destructo.covidtracker 

I/Choreographer: Skipped 1860 frames!  The application may be doing too much 
work on its main thread.
2020-05-20 13:20:11.774 26583-26583/com.destructo.covidtracker W/Looper: Slow 
Looper main: doFrame is 31009ms late because of 20 msg, msg 1 took 31018ms 
(seq=2203 running=29292ms runnable=126ms 
h=android.view.Choreographer$FrameHandler
c=android.view.Choreographer$FrameDisplayEventReceiver)
2020-05-20 13:20:11.776 26583-26639/com.destructo.covidtracker 
I/OpenGLRenderer: Davey! duration=31068ms; Flags=0, 
IntendedVsync=645656812653619, Vsync=645687812652379, 
OldestInputEvent=9223372036854775807, NewestInputEvent=0, 
HandleInputStart=645687822730146, AnimationStart=645687822796552, 
PerformTraversalsStart=645687865692802, DrawStart=645687866102750, 
SyncQueued=645687878454625, SyncStart=645687879030510, 
IssueDrawCommandsStart=645687880157177, SwapBuffers=645687881475146, 
FrameCompleted=645687882061812, DequeueBufferDuration=192000, 
QueueBufferDuration=255000, 

我的适配器

class GlobalCountryAdapter (private val onClickListener: GlobalClickListener):
ListAdapter<GlobalCountryStatistics, GlobalCountryAdapter.ViewHolder>(
    GlobalCountryDiffCallback()
) {


override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
    return ViewHolder.from(
        parent
    )
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    val countryData = getItem(position)
    holder.bind(countryData)
    holder.itemView.setOnClickListener{
        onClickListener.onCLick(countryData)
    }
}


class ViewHolder private constructor(val binding: DataListItemViewBinding) : 
RecyclerView.ViewHolder(binding.root) {
    fun bind(countryData: GlobalCountryStatistics) {
        if (countryData.cases_today != null && countryData.cases_today > 0 ){
            binding.increaseIcon.visibility = View.VISIBLE
            binding.newCasesTxt.visibility = View.VISIBLE
        }else{
            binding.increaseIcon.visibility = View.GONE
            binding.newCasesTxt.visibility = View.GONE
        }
        binding.globalCountryData = countryData
        binding.executePendingBindings()
    }

    companion object {
        fun from(parent: ViewGroup): ViewHolder {
            var layoutInflater = LayoutInflater.from(parent.context)
            val binding = DataListItemViewBinding.inflate(layoutInflater, parent, false)
            return ViewHolder(
                binding
            )
        }
    }

}


class GlobalClickListener(val clickListener: (country:GlobalCountryStatistics) -> Unit){
    fun onCLick(country:GlobalCountryStatistics) = clickListener(country)
}

}

class GlobalCountryDiffCallback : DiffUtil.ItemCallback<GlobalCountryStatistics>() {
override fun areItemsTheSame(oldItem: GlobalCountryStatistics, newItem: GlobalCountryStatistics): 
Boolean {
    return oldItem.country_name == newItem.country_name
}

override fun areContentsTheSame(oldItem: GlobalCountryStatistics,
 newItem: GlobalCountryStatistics): Boolean {
    return oldItem == newItem
}

}

GlobalFragment

class GlobalFragment : Fragment() {

private val mglobalViewModel: GlobalViewModel by lazy {
    ViewModelProvider(this).get(GlobalViewModel::class.java)
}

override fun onCreateView(
    inflater: LayoutInflater, container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {

    val binding = FragmentGlobalBinding.inflate(inflater)

    binding.setLifecycleOwner(this)

    binding.globalViewModel = mglobalViewModel


    mglobalViewModel.globalCountryStats.observe(viewLifecycleOwner, Observer {

        val adap = GlobalCountryAdapter(GlobalCountryAdapter.GlobalClickListener {
            mglobalViewModel.navigationToCountryDetail(it)
        })
        adap.submitList(it)
        binding.countryRecycler.adapter = adap

    })

    mglobalViewModel.navigateToCountryDetail.observe(viewLifecycleOwner, Observer {
        if (null != it){
            this.findNavController().navigate(
                GlobalFragmentDirections.actionGlobalFragmentToCountryDetailsFragment2(it))
            mglobalViewModel.doneNavigationToCountryDetail() }
    })

    mglobalViewModel.globalStats.observe(viewLifecycleOwner, Observer { globalSummary ->
        if (null != globalSummary){
            binding.include.globalMoreButton.setOnClickListener{
                this.findNavController().navigate(
                    GlobalFragmentDirections.actionGlobalFragmentToGlobalSummaryDetailsFragment(
                        globalSummary
                    )
                )
            }
        }
    })

    return binding.root
}

}

用于回收者视图的BindingAdapter

@BindingAdapter("countryList")
fun bindCountryRecyclerView(recyclerView: RecyclerView, mdata: List<GlobalCountryStatistics>?) {

val adapter = recyclerView.adapter as FinalAdapter
mdata?.let {
    adapter.submitList(mdata)
}
}

ViewModel

class GlobalViewModel : ViewModel() {

private var _globalStats = MutableLiveData<GlobalCoronaStatistics>()
val globalStats: LiveData<GlobalCoronaStatistics>
    get() = _globalStats

private var _globalCountryStats = MutableLiveData<List<GlobalCountryStatistics>>()
val globalCountryStats:LiveData<List<GlobalCountryStatistics>>
    get() = _globalCountryStats

private val _navigateToCountryDetail = MutableLiveData<GlobalCountryStatistics>()
val navigateToCountryDetail: LiveData<GlobalCountryStatistics>
    get() = _navigateToCountryDetail

private var globalViewModelJob = Job()

private val uiScope = CoroutineScope(globalViewModelJob + Dispatchers.Main)


init {
    getGlobalStatistics()
    getCountryStatsList()
}

private fun getGlobalStatistics() {
    uiScope.launch {
        var getGlobalDataDef = GlobalApi.retrofitService.getGlobalDataAsync()
        try {

            val globalData = getGlobalDataDef.await()
            _globalStats.value = globalData
        } catch (e: Exception) {

            Log.e("GlobalViewModel", "FAILED NETWORK\n" + e.message)
        }
    }
}

private fun getCountryStatsList(){
    uiScope.launch {

        var getCountryDeferred = GlobalApi.retrofitService.getGlobalCountryDataAsync()
        try {
            val globalCountry = getCountryDeferred.await()
            _globalCountryStats.value = globalCountry
        }catch (e:Exception){
            Log.e("GlobalViewModel","FAILED NETWORK\n" + e.message)
        }
    }
}

fun navigationToCountryDetail(selectedCountry:GlobalCountryStatistics){
    _navigateToCountryDetail.value = selectedCountry
}
fun doneNavigationToCountryDetail(){
    _navigateToCountryDetail.value = null
}

override fun onCleared() {
    super.onCleared()
    globalViewModelJob.cancel()
}

}

GlobalApiService

private const val BASE_URL = "https://disease.sh/v2/"

private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()

private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()

interface GlobalApiService {

@GET("all")
fun getGlobalDataAsync(): Deferred<GlobalCoronaStatistics>


@GET("countries?sort=cases")
fun getGlobalCountryDataAsync(): Deferred<List<GlobalCountryStatistics>>

@GET("gov/india")
fun getIndiaDataAsync(): Deferred<IndiaStatistics>
}

object GlobalApi {
val retrofitService: GlobalApiService by lazy { 
retrofit.create(GlobalApiService::class.java) }
}
android android-fragments android-recyclerview android-databinding android-architecture-navigation
1个回答
0
投票

也许GlobalApi.retrofitService方法未在后台线程中运行。您将Dispatchers.Main设置为uiScope,因此launch {}中的所有内容都有可能在主线程上运行。您可能会注意到,在上面的代码中,您仍然可以在_globalStats.value = globalData内部调用launch,如果在后台线程上运行,则不允许这样做。试试这个:

uiScope.launch {
    try {
        val globalData = withContext(Dispatchers.IO) {
             GlobalApi.retrofitService.getGlobalDataAsync() 
        }
        _globalStats.value = globalData
    } catch (e: Exception) {

        Log.e("GlobalViewModel", "FAILED NETWORK\n" + e.message)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.