系列文章目录
提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 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等调用频率没那么高的方法中。