ANR实战案例 2 - 不同线程状态ANR示例

news2024/11/29 11:57:27

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


文章目录

  • 系列文章目录
  • 前言
  • 一、Blocked状态示例
    • 1.启动初始化阻塞案例
      • trace1.tx
    • 2.ConcurrentHashMap分段锁优化案例
  • 二、Waiting状态示例
    • 1.Retrofit创建内部互斥案例
  • 三、Native状态示例
    • 1.SIM卡状态获取
  • 四、Runable状态示例
  • 总结


前言

提示:这里可以添加本文要记录的大概内容:

monkey案例


提示:以下是本篇文章正文内容,下面案例可供参考

一、Blocked状态示例

1.启动初始化阻塞案例

trace1.tx

查看trace.txt中"main"线程堆栈:

"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 obj=0x75bc7c50 self=0x7a8d896a00
  | sysTid=12061 nice=0 cgrp=default sched=0/0 handle=0x7a91e23a98
  | state=S schedstat=( 401196933 229090000 643 ) utm=29 stm=10 core=4 HZ=100
  | stack=0x7ff9366000-0x7ff9368000 stackSize=8MB
  | held mutexes=
  at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:929)
  - waiting to lock <0x077d5864> (a java.lang.Object[]) held by thread 23//1.阻塞点,<0x077d5864> held by thread 23
  at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:886)
  at android.app.ContextImpl.getSystemService(ContextImpl.java:1524)
  at android.content.ContextWrapper.getSystemService(ContextWrapper.java:659)
  at android.view.TouchScreenHelper.updateDisplayInfo(TouchScreenHelper.java:323)
  at android.view.TouchScreenHelper.init(TouchScreenHelper.java:149)
  at android.view.TouchScreenHelper.<init>(TouchScreenHelper.java:87)
  at android.view.WindowManagerGlobal.getTouchScreenHelper(WindowManagerGlobal.java:173)
  - locked <0x02a54acd> (a java.lang.Class<android.view.WindowManagerGlobal>)
  at com.android.internal.policy.DecorView.<init>(DecorView.java:290)
  at com.android.internal.policy.PhoneWindow.generateDecor(PhoneWindow.java:2376)
  at com.android.internal.policy.PhoneWindow.installDecor(PhoneWindow.java:2730)
  at com.android.internal.policy.PhoneWindow.getDecorView(PhoneWindow.java:2085)
  at com.xxx.common.base.TransBaseActivity.toggleTranslucent(TransBaseActivity.java:37)
  at com.xxx.common.base.TransBaseActivity.onCreate(TransBaseActivity.java:20)
  at com.xxx.xxx.xx.ControllerActivity.onCreate(ControllerActivity.java:174)
  at android.app.Activity.performCreate(Activity.java:6734)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2681)
  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2793)
  at android.app.ActivityThread.-wrap12(ActivityThread.java:-1)
  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1527)
  at android.os.Handler.dispatchMessage(Handler.java:110)
  at android.os.Looper.loop(Looper.java:203)
  at android.app.ActivityThread.main(ActivityThread.java:6301)
  at java.lang.reflect.Method.invoke!(Native method)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1094)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:955)

由注释1处可知,当前主线程被线程23阻塞,找tid=23的线程堆栈:

"RxCachedThreadScheduler-1" daemon prio=5 tid=23 Blocked
  | group="main" sCount=1 dsCount=0 obj=0x12f61af0 self=0x7a8211fa00
  | sysTid=12096 nice=0 cgrp=default sched=0/0 handle=0x7a68c66450
  | state=S schedstat=( 164816616 182495075 396 ) utm=13 stm=2 core=7 HZ=100
  | stack=0x7a68b64000-0x7a68b66000 stackSize=1037KB
  | held mutexes=
  at android.view.WindowManagerGlobal.getInstance(WindowManagerGlobal.java:182)
  - waiting to lock <0x02a54acd> (a java.lang.Class<android.view.WindowManagerGlobal>) held by thread 1
  at android.view.WindowManagerImpl.<init>(WindowManagerImpl.java:58)
  at android.view.WindowManagerImpl.<init>(WindowManagerImpl.java:65)
  at android.app.SystemServiceRegistry$50.createService(SystemServiceRegistry.java:599)
  at android.app.SystemServiceRegistry$50.createService(SystemServiceRegistry.java:598)
  at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:933)
  - locked <0x077d5864> (a java.lang.Object[])//2.当前线程持有锁<0x077d5864>
  at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:886)
  at android.app.ContextImpl.getSystemService(ContextImpl.java:1524)
  at android.content.ContextWrapper.getSystemService(ContextWrapper.java:659)
  at me.jessyan.autosize.utils.ScreenUtils.getScreenSize(ScreenUtils.java:62)
  at me.jessyan.autosize.AutoSizeConfig.init(AutoSizeConfig.java:247)
  at me.jessyan.autosize.AutoSizeConfig.init(AutoSizeConfig.java:205)
  at me.jessyan.autosize.AutoSize.checkAndInit(AutoSize.java:84)
  at com.xxx.common.base.XxxApplicationInitor.initAutoSize(XxxApplicationInitor.java:404)
  at com.xxx.common.base.XxxApplicationInitor.thirdSDKInit(XxxApplicationInitor.java:414)
  at com.xxx.common.base.XxxApplicationInitor.preInit(XxxApplicationInitor.java:438)
  at com.xxx.common.base.XxxApplicationInitor.access$800(XxxApplicationInitor.java:109)
  at com.xxx.common.base.XxxApplicationInitor$10.subscribe(XxxApplicationInitor.java:231)
  at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40)
  at io.reactivex.Observable.subscribe(Observable.java:12284)
  at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
  at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578)
  at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
  at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
  at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
  at java.lang.Thread.run(Thread.java:761)

查看thread-23可知,当前使用RxJava创建的子线程正在进行AutoSize库的初始化,WindowManagerGlobal一直持有<0x077d5864>锁,导致主线程TransBaseActivity.toggleTranslucent方法执行时候获取不到<0x077d5864>这个锁。
ps:为了提升启动速度,业务中将AutoSize库的初始化放到了子线程中异步初始化。

结合业务代码查看:

protected void toggleTranslucent() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = getWindow();
            window.setNavigationBarColor(Color.BLACK);
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
                    | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
            if (SkinAttribute.bgColor1 == getResources().getColor(R.color.bgColor1_c)) {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            } else {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            }
            window.setStatusBarColor(Color.TRANSPARENT);
        }
    }

分析:TransBaseActivity为ControllerActivity的父类,toggleTranslucent方法中获取WindowManager进行隐藏状态栏操作,也验证了thread-23中WindowManagerGlobal一直持有<0x077d5864>锁,导致主线程WindowManager阻塞的逻辑。
修改方案:将AutoSize库的初始化时机提前。

有些机型生成的trace.txt没有直接指出持有<0x077d5864>锁的线程,这时候可以在trace.txt中搜索0x077d5864可以发现搜索<0x077d5864>锁指向的线程堆栈,同样能找到持有当前锁的线程。
ps:搜索<0x077d5864>锁的方式,可能会出现多个线程waiting to lock <0x077d5864>(等待获取锁),需要找出真正持有<0x077d5864>的线程,即02注释处locked <0x077d5864>。

2.ConcurrentHashMap分段锁优化案例

在这里插入图片描述
由trace文件可知,主线程被线程74锁持有的锁阻塞,如果是本地monkey,直接搜索查看持有0x031 d6bd2锁的线程堆栈,很容易解决问题,
但由于Google Play后台抓取trace日志,目前线程获取并不完善(只获取靠前的20个线程),故此处拿不到线程74的堆栈,所以常规方法走不通。
此处只能通过主线程堆栈顺藤摸瓜,切入分析代码阻塞在deleteMusicCache方法,查看分析该段代码发现,业务同学是为了保证该类中HashMap操作不出问题而加的方法锁,所以此处可以去除synchronized方法锁,把原有HashMap改为ConcurrentHashMap,同样能解决同步问题,而避免其它地方调用该方法时候阻塞,可参考如下示例。
修改前:

private Map<String, XxxExtraInfo> mXxExtraInfoMap = new HashMap<>();//mXxExtraInfoMap为全局HashMap
private synchronized XxxFile deleteMusicCache(String ID) {
	XxxExtraInfo xxExtraInfo = mXxExtraInfoMap.remove(musicID);
	...省略代码行...
}

public synchronized List<XxxFile> getUnSyncXx() {
        List<XxFile> resultList = new ArrayList<>();
        List<XxExtraInfo> items = new ArrayList<>(mXxExtraInfoMap.values());
        ...省略代码行...
        return resultList;
}

修改后:

private Map<String, XxxExtraInfo> mXxExtraInfoMap = new ConcurrentHashMap<>();//mXxExtraInfoMap为全局HashMap
private XxxFile deleteMusicCache(String ID) {
	XxxExtraInfo xxExtraInfo = mXxExtraInfoMap.remove(musicID);
	...省略代码行...
}

public List<XxxFile> getUnSyncXx() {
        List<XxFile> resultList = new ArrayList<>();
        List<XxExtraInfo> items = new ArrayList<>(mXxExtraInfoMap.values());
        ...省略代码行...
        return resultList;
}

去除读取Map的方法锁,将原本HashMap改为ConcurrentHashMap分段锁,减小锁的范围。修改验证后,此处ANR得到解决。
在这里插入图片描述
在这里插入图片描述

二、Waiting状态示例

1.Retrofit创建内部互斥案例

主线程堆栈如下(trace.txt):

"main" prio=5 tid=1 Waiting
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x71e0e448 self=0xae22d000
  | sysTid=4118 nice=0 cgrp=default sched=1073741825/1 handle=0xb289d4a4
  | state=S schedstat=( 747119393 44517458 785 ) utm=54 stm=20 core=1 HZ=100
  | stack=0xbe05b000-0xbe05d000 stackSize=8MB
  | held mutexes=
  at b.a.b.c.a.l.v(:1)
  - waiting on <0x08fd2b2f> (a java.lang.Class<b.a.b.c.a.l>)
  at b.a.b.c.f.b.e(:6)
  at b.a.b.c.a.k.c(:60)
  at b.a.b.c.a.k.a(:6)
  at b.a.b.c.a.k.<clinit>(:1)
  at com.xxx.storage.cache.g1.V(:2)
  at com.xxx.storage.cache.q1.a(:20)
  at com.xxx.biz.remote.j.i(:113)
  at com.xxx.biz.remote.j.f(:6)
  at com.xxx.xxx.xx.service.XxService$m.a(:5)
  at com.xxx.xxx.xx.service.XxService$m.onChanged(:1)
  at com.jeremyliao.liveeventbus.LiveEventBus$ObserverWrapper.onChanged(LiveEventBus.java:3)
  at androidx.lifecycle.LiveData.considerNotify(LiveData.java:6)
  at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:8)
  at androidx.lifecycle.LiveData.setValue(LiveData.java:4)
  at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:1)
  at com.jeremyliao.liveeventbus.LiveEventBus$LiveEvent.postInternal(LiveEventBus.java:1)
  at com.jeremyliao.liveeventbus.LiveEventBus$LiveEvent.access$1800(LiveEventBus.java:1)
  at com.jeremyliao.liveeventbus.LiveEventBus$LiveEvent$PostValueTask.run(LiveEventBus.java:1)
  at android.os.Handler.handleCallback(Handler.java:790)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:164)
  at android.app.ActivityThread.main(ActivityThread.java:6524)
  at java.lang.reflect.Method.invoke(Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:451)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:888)

由堆栈可知,主线程处于Waiting状态,首先搜索waiting on后面的锁<0x08fd2b2f>,然而并未找到持有该锁的其它线程。

- waiting on <0x08fd2b2f> (a java.lang.Class<b.a.b.c.a.l>)

继续搜索b.a.b.c.a.l类,在另外一个RxCachedThreadScheduler-2线程发现:

"RxCachedThreadScheduler-2" daemon prio=5 tid=28 Waiting
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x13202c00 self=0x937fb000
  | sysTid=4174 nice=0 cgrp=default sched=0/0 handle=0x9377f970
  | state=S schedstat=( 1247162012 424038387 440 ) utm=117 stm=7 core=0 HZ=100
  | stack=0x9367d000-0x9367f000 stackSize=1038KB
  | held mutexes=
  at b.a.b.c.a.l.j(:4)
  - waiting on <0x0d0f3735> (a java.lang.Class<b.a.b.c.a.k>)
  at b.a.b.c.a.l.<clinit>(:1)
  at b.a.b.c.a.l.b(:1)
  at com.xxx.common.base.r.s(:6)
  at com.xxx.common.base.r.j(:23)
  at com.xxx.common.base.r.d(:1)
  at com.xxx.common.base.k.a(:1)
  at io.reactivex.internal.operators.observable.w0.subscribeActual(:3)
  at io.reactivex.m.subscribe(:14)
  at io.reactivex.internal.operators.observable.g6$a.run(:1)
  at io.reactivex.u.run(:2)
  at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:2)
  at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:1)
  at java.util.concurrent.FutureTask.run(FutureTask.java:266)
  at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
  at java.lang.Thread.run(Thread.java:764)

发现该线程同样在执行b.a.b.c.a.l类中方法时锁住了,mapping文件查看b.a.b.c.a.l类:

com.xxx.xx.network.api.NetworkService -> b.a.b.c.a.l:

继而进一步结合业务代码查看NetworkService 中执行逻辑:

public class NetworkService {
	private static Api api = getRetrofit().create(Api.class);
	private static ApiSync apiSync = getRetrofit().create(ApiSync.class);

	public static void resetWithBaseurl(String baseurl) {
        ...
        api = getRetrofit(baseurl).create(Api.class);
        apiSync = getRetrofit(baseurl).create(ApiSync.class);
    }
}

该类调用resetWithBaseurl静态方法创建Retrofit时候,类内部静态变量同时创建Retrofit(),导致Retrofit创建时候阻塞。修改后调用如下:

public class NetworkService {
	private static Api api;
	private static ApiSync apiSync;

	public static void resetWithBaseurl(String baseurl) {
        ...
        api = getRetrofit(baseurl).create(Api.class);
        apiSync = getRetrofit(baseurl).create(ApiSync.class);
    }
}

在具体方法调用时候,再去创建Retrofit,避免创建时锁住主线程。

三、Native状态示例

1.SIM卡状态获取

"main" tid=1 Native
  #00  pc 0x000000000009aec0  /apex/com.android.runtime/lib/bionic/libc.so (__ioctl+8)
  #01  pc 0x0000000000068f2f  /apex/com.android.runtime/lib/bionic/libc.so (ioctl+26)
  #02  pc 0x0000000000039bc3  /system/lib/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+238)
  #03  pc 0x000000000003a805  /system/lib/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)+32)
  #04  pc 0x000000000003a5d3  /system/lib/libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+126)
  #05  pc 0x00000000000352d7  /system/lib/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+98)
  #06  pc 0x00000000000c9167  /system/lib/libandroid_runtime.so (android_os_BinderProxy_transact(_JNIEnv*, _jobject*, int, _jobject*, _jobject*, int)+82)
  at android.os.BinderProxy.transactNative (Native method)
  at android.os.BinderProxy.transact (BinderProxy.java:545)
  at com.android.internal.telephony.ISub$Stub$Proxy.getSimStateForSlotIndex (ISub.java:2356)
  at android.telephony.SubscriptionManager.getSimStateForSlotIndex (SubscriptionManager.java:2307)
  at android.telephony.TelephonyManager.getSimStateIncludingLoaded (TelephonyManager.java:3521)
  at android.telephony.TelephonyManager.getSimState (TelephonyManager.java:3495)
  at com.xxx.util.Utils.simCardState (Utils.java:957)
  at com.xxx.util.CommonRequestHelper.getAdByPlacementID (CommonRequestHelper.java:66)
  at com.xxx.util.CommonRequestHelper.getAdByPlacementID (CommonRequestHelper.java:54)
  at com.xxx.biz.adc.request.platform.bp.BPAd.load (BPAd.java:164)
  at com.xxx.biz.adc.process.caching.AdCachingProcess.loadAd (AdCachingProcess.java:185)
  at com.xxx.biz.adc.process.caching.WaterfallAdCachingProcess.stateChanged (WaterfallAdCachingProcess.java:46)
  at com.xxx.biz.adc.process.caching.AdCachingProcess.onError (AdCachingProcess.java:153)
  at com.xxx.biz.adc.request.UniformAd.lambda$notifyAdError$1 (UniformAd.java:260)
  at com.xxx.biz.adc.request.-$$Lambda$UniformAd$hjEYnpArgK_Cafxu5CAiz-xetks.run (lambda)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:223)
  at android.app.ActivityThread.main (ActivityThread.java:7974)
  at java.lang.reflect.Method.invoke (Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:635)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:947)

广告接口请求,自营广告信息获取,每次接口请求获取一次sim卡状态过于频繁,导致binder资源不足。
解决:去除sim参数,使用别的唯一标识判断。

Utils.simCardState

/**
 * 获取SIM卡状态
 * * SIM的状态信息:
 * *  SIM_STATE_UNKNOWN            未知状态 0
 * *  SIM_STATE_ABSENT             没插SIM卡 1
 * *  SIM_STATE_PIN_REQUIRED       锁定状态,需要用户的PIN码解锁 2
 * *  SIM_STATE_PUK_REQUIRED       锁定状态,需要用户的PUK码解锁 3
 * *  SIM_STATE_NETWORK_LOCKED     锁定状态,需要网络的PIN码解锁 4
 * *  SIM_STATE_READY              就绪状态 5
 */
@SuppressLint("WrongConstant")
public static int simCardState(TelephonyManager telephonyManager) {
    int simState = 1;//没有SIM卡
    if(telephonyManager == null){
        return simState;
    }
    try {
        int a = TelephonyManager.SIM_STATE_READY;
        simState = telephonyManager.getSimState();
    } catch (Exception e) {
        Log.e("Utils", "simCardState: ", e);
    }
    return simState;
}

四、Runable状态示例

"main" tid=1 Runnable
  at com.airbnb.lottie.LottieTask.removeListener (LottieTask.java)
  at com.airbnb.lottie.LottieAnimationView.cancelLoaderTask (LottieAnimationView.java:486)
  at com.airbnb.lottie.LottieAnimationView.setImageResource (LottieAnimationView.java:225)
  at com.xxx.ui.play.XxxPlayCoverFragment.showPlayBtnPauseState (XxxPlayCoverFragment.java:2557)
  at com.xxx.ui.play.XxxPlayCoverFragment.access$3300 (XxxPlayCoverFragment.java:189)
  at com.xxx.ui.play.XxxPlayCoverFragment$26.onTrackProgress (XxxPlayCoverFragment.java:2727)
  at com.xxx.xxx.service.XxxService$PlayerEngineListenerImpl.onTrackProgress (XxxService.java:1440)
  at com.xxx.biz.media.XxxPlayerEngine$MediaPlayerListener.onProgress (XxxPlayerEngine.java:417)
  at com.xxx.biz.media.XxxMediaPlayer$ProgressThread$2.subscribe (XxxMediaPlayer.java:1295)
  at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual (ObservableCreate.java:40)
  at io.reactivex.Observable.subscribe (Observable.java:12284)
  at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run (ObservableSubscribeOn.java:96)
  at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run (HandlerScheduler.java:124)
  at android.os.Handler.handleCallback (Handler.java:938)
  at android.os.Handler.dispatchMessage (Handler.java:99)
  at android.os.Looper.loop (Looper.java:268)
  at android.app.ActivityThread.main (ActivityThread.java:8016)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:627)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:997)

XxxPlayCoverFragment$26.onTrackProgress

private PlayerEngineListener mPlayerEngineListener = new PlayerEngineListener() {
	public boolean onTrackStart(Item item) {
	 ...
	}
	
   public void onTrackPrepared(boolean isPlayAfterPrepare) {	
    ...
   }

   public void onTrackResume() {
	...
   }        

   public void onTrackProgress(int seconds) {
	...
	showPlayBtnPauseState();
   }
}

showPlayBtnPauseState

    private void showPlayBtnPauseState() {
        if (binding == null || binding.layoutBottomOperation.playpagePlay == null) {
            return;
        }
        isPreparing = false;
        binding.layoutBottomOperation.playpagePlay.clearAnimation();
        binding.layoutBottomOperation.playpagePlay.setImageResource(R.drawable.full_screen_play_ing);
    }

onTrackProgress为播放进度更新回调,调用背景切换过于频繁,可修改到onTrackStart等调用频率没那么高的方法中。


总结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/528242.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

互联网营销之何谓真需求-想知道如何挖掘真需求看这篇就对了

互联网营销思维是以爆品为核心的迭代思维&#xff0c;本文结合“生日蛋糕”、“方便面”、“蜜雪冰城”几个小例子&#xff0c;以及我们具体的工作&#xff0c;展开聊聊什么是“真需求”。 1. 互联网营销和传统营销的区别 1.1 传统的营销思维&#xff1a; “定位4P&#xff0…

亚马逊云科技:使用Inf2实例运行大语言模型GPT-J-6B基础设施

在2019年的亚马逊云科技re:Invent上&#xff0c;亚马逊云科技发布了Inferentia芯片和Inf1实例这两个基础设施。Inferentia是一种高性能机器学习推理芯片&#xff0c;由亚马逊云科技定制设计&#xff0c;其目的是提供具有成本效益的大规模低延迟预测。时隔四年&#xff0c;2023年…

金融行业软件测试面试必备:答案详解与干货技巧

大家好&#xff0c;今天我要和大家分享的是我多年从事金融行业软件测试的心得体会。由于金融行业涉及到的数据量非常大&#xff0c;系统功能也十分复杂&#xff0c;因此在招聘软件测试人员时&#xff0c;往往会提出一些具有挑战性的问题。 作为一个资深面试官&#xff0c;我也…

Android aidl及binder基础知识巩固

作者&#xff1a;义华 1、什么是binder binder是android framework提供的&#xff0c;用于跨进程方法调用的机制&#xff0c;具有安全高效等特点。 我们知道&#xff0c;在 Android 系统中&#xff0c;每个应用程序都运行在一个独立的进程中&#xff0c;各个进程之间需要进行…

Logstash-grok表达式常用模式与正则使用与测试

Logstash 常用字符解释常用模式使用方式 使用正则表达式使用方式 测试用例 常用字符解释 \ 表示匹配 \s* 匹配空格&#xff08;可多个&#xff09; \w 匹配字符&#xff08;可多个&#xff09;常用模式 %{HOSTNAME}&#xff0c;匹配请求的主机名 %{TIMESTAMP_ISO8601:time…

探索智能化:TOOM解析未来稿件校验系统的技术进展与应用展望

在信息时代&#xff0c;随着大数据、人工智能和自然语言处理等技术的快速发展&#xff0c;稿件校验系统正朝着智能化的方向迈进。智能化的稿件校验系统能够更准确、高效地检测虚假信息、抄袭行为以及提升文章质量。本文将探讨智能化稿件校验系统的技术进展与应用展望&#xff0…

NC与单一窗口数据对接丨外贸软件

在国际贸易通关过程中&#xff0c;所涉及相关部门的信息管理&#xff0c;主要是以数字化流程系统为主&#xff0c;让每个部门业务的申请、办理、回复采用电子化和互联网化。由于每个环节部分的数据壁垒未打通&#xff0c;数据无法协同共享&#xff0c;导致在口岸通关的过程中&a…

Swoole定时器实现毫秒级任务调度

简介 Timer 毫秒精度的定时器&#xff0c;底层基于 epoll_wait 和 setitimer 实现&#xff0c;数据结构使用最小堆&#xff0c;可支持添加大量定时器&#xff0c;使用最小堆数据结构实现的定时器&#xff0c;类似 JavaScript 的 setInterval&#xff0c;Swoole 定时器的添加和…

I2C通信协议原理和MPU6050

一、串口通讯 只能在两个设备之间进行 若要三台设备两两通信&#xff0c;则每个设备得需要两组窗口&#xff0c;为3组相互独立的窗口通讯 为解决这个问题&#xff1a;设计了总线通讯&#xff0c;有多种&#xff0c;I2C为其中一种 二、I2C通信 &#xff08;1&#…

(java)异常 (详解)

目录 1. 异常的概念 1. 算术异常 2.空指针异常 3.数组越界异常 4.在编译时就发现了异常 2.异常的体系结构 总结&#xff1a; 3.异常的分类 4.异常的处理 1 .防御式编程 2.异常的抛出 3 .异常的捕获 3.1 .异常声明throws throw和throws的区别&#xff1f; …

【笔试强训选择题】Day13.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 文章目录…

昨天的测试岗面试,仅仅4个问题,轻松让面试者破防了

目录 引言 你看&#xff0c;一不小心&#xff0c;就要被虐&#xff01;&#xff01; 自动化测试到底该如何学&#xff1f; 一、Python编程学习内容 二、WEB自动化测试学习内容 三、APP自动化测试学习内容 四、Postman接口测试工具学习内容 五、接口自动化测试学习内容 …

微服务框架【Nacos配置管理-Feign远程调用-Gateway服务网关】

一、Nacos配置管理 1.统一配置管理 在Nacos中添加配置信息 填写配置信息 点击发布 完成配置的统一管理 配置获取的步骤&#xff1a; 项目启动->读取本地配置文件application.yml->创建spring容器->加载bean 但是现在多了一个nacos中的配置文件&#xff0c;我们…

IntelliJ IDEA 统一设置编码为utf-8编码 及 SpringBoot 打 jar 包运行 在windows 平台控制台和日志 乱码解决

文章目录 一、背景二、知识准备三、程序运行源代码历经处理阶段四、问题描述五、解决方法1.修改项目编码格式统一为UTF-82.将项目中的.idea文件夹中的encodings.xml文件中的编码格式改为uft-83.File->Settings->Build,Execution,Deployment -> Compiler -> Java Co…

架构师日记-从数据库发展历程到数据结构设计探析 | 京东云技术团队

作者&#xff1a;京东零售 刘慧卿 一、数据库发展史 起初&#xff0c;数据的管理方式是文件系统&#xff0c;数据存储在文件中&#xff0c;数据管理和维护都由程序员完成。后来发展出树形结构和网状结构的数据库&#xff0c;但都存在着难以扩展和维护的问题。直到七十年代&am…

分布式补充知识 02.AOP的重要注解@annotation ,使用添加缓存和清空缓存

01.在项目中创建一个包annotation包&#xff1a; 在创建新的java.class文件时候&#xff0c;选择annotation 写一个自定义的注解&#xff0c;名字叫做RequiredCache package com.cy.annotation;package com.cy.annotation;import java.lang.annotation.ElementType; import j…

企业远程工作安全及简化

员工远程面临哪些挑战 大多数企业已将远程工作模式作为其新常态&#xff0c;这使得保护远程端点成为比以往更高的优先级。然而&#xff0c;在寻求远程工作支持的安全性时&#xff0c;企业有时会忽视用户体验。过于严格的远程工作解决方案没有考虑到经常在工作场所和家庭的安全…

执行SQL响应比较慢,你有哪些排查思路?

如果执行SQL响应比较慢&#xff0c;我觉得可能有以下4个原因&#xff1a; 第1个原因&#xff1a;没有索引或者 导致索引失效。第2个原因&#xff1a;单表数据量数据过多&#xff0c;导致查询瓶颈第3个原因&#xff1a;网络原因或者机器负载过高。第4个原因&#xff1a;热点数据…

基于Canal实现Mysql数据实时同步到Elasticsearch(Docker版)

1、Canal简介 Canal主要用途是对MySQL数据库增量日志进行解析&#xff0c;提供增量数据的订阅和消费&#xff0c;简单说就是可以对MySQL的增量数据进行实时同步&#xff0c;支持同步到MySQL、Elasticsearch、HBase等数据存储中去。 Canal会模拟MySQL主库和从库的交互协议&#…

SpringMVC常用注解用法

Spring MVC是基于Servlet API构建的原始Web框架。 MVC是Model View Controller的缩写即视图模型控制器&#xff0c;是一种思想&#xff0c;而Spring MVC是对该思想的具体实现。关于SpringMVC的学习我们需要掌握用户和程序的连接、获取参数以及返回数据三大部分。而这三大功能的…