【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
目录
背景:
卡死/黑屏log信息,原因分析:
背景:
一个盘点扫描的APP,可以离线在线操作,运行平台为PDA,客户在使用过程中反馈经过多次扫描后会出现屏幕卡死/黑屏的情况,但是PDA系统可以正常使用,由此可见是我的APP导致的。客户一次盘点会盘点近千个物品,也就是说会扫描近千次,扫描过程中会有人声提示音播放。收到了客户的反馈后,拿了设备回来自己测,通过几回合的连续扫描后,复现出了这个问题,每回合都是扫到237次就出现黑屏或者报错。拿到了出现问题的log信息,但是一看logcat,很难确定原因,不是常规的代码崩溃。以下看下我拿到的logcat信息
卡死/黑屏log信息,原因分析:
08-25 18:18:58.028 17875-17875/com.pda.wph E/Surface: dequeueBuffer failed (Invalid argument)
08-25 18:18:58.038 17875-17875/com.pda.wph E/ViewRootImpl: Could not lock surface
java.lang.IllegalArgumentException
at android.view.Surface.nativeLockCanvas(Native Method)
at android.view.Surface.lockCanvas(Surface.java:236)
at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2495)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:2469)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2313)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1946)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1078)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5887)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
at android.view.Choreographer.doCallbacks(Choreographer.java:562)
at android.view.Choreographer.doFrame(Choreographer.java:532)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5136)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
08-26 11:05:56.994 3741-3741/com.pda.wph E/InputChannel-JNI: Error 24 dup channel fd 1011.
08-26 11:05:57.004 3741-3741/com.pda.wph D/AndroidRuntime: Shutting down VM
08-26 11:05:57.004 3741-3741/com.pda.wph W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x41b5a898)
08-26 11:05:57.004 3741-3741/com.pda.wph E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Could not read input channel file descriptors from parcel.
at android.view.InputChannel.nativeReadFromParcel(Native Method)
at android.view.InputChannel.readFromParcel(InputChannel.java:148)
at android.view.IWindowSession$Stub$Proxy.addToDisplay(IWindowSession.java:690)
at android.view.ViewRootImpl.setView(ViewRootImpl.java:595)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:269)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2857)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2269)
at android.app.ActivityThread.access$600(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1259)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5136)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
从上面的logcat日志看 很难定位到问题发生的所在位置,所以也是耗费我大量的时间去网上搜寻答案。但是基本可以排除掉的是第一部分的logcat原因,我的APP中并没有使用过surfaceview之类的控件,所以这个可能是由其他报错影响导致。
这个时候,我们就重点看第二个报错信息Could not read input channel file descriptors from parcel
通过百度得到了一些信息,可能导致出现这个报错的几个原因
RemoteView中添加的图片太大了,超过40K会报这个异常(PS:排除,项目中没有用到这个)
Intent传递的数据太大了超过1M也会报这个错误(PS:排除)
FileDescripter太多而且没有关闭,looper太多没有quit。
试试在AndroidManefest.xml中对当前Activity配置 (PS:项目中是强制竖屏)
configchange=“orientation|keyboardHidden”强制在Activity横竖屏切换的时候不重新onCreate。
通过排除最终定位到了FileDescripter太多而且没有关闭这个可疑的原因,这个是什么东西,我也不知道,只能再百度下。
可以参考https://www.jianshu.com/p/e3830e7be8b2
可以知道这个是句柄泄露照成的,一个进程拥有的最大句柄数一般是1024,一旦超过这个值,该进程就会出现句柄泄露,进而引发一些报错情况。
在linux中一个文件、一个串口、一个socket、一个线程都可以是一个文件,而一个文件会占用一个句柄,linux中一个进程默认的句柄最大数值是1024,当超过这个数值,linux就会对当前的进程进行kill,而kill的对象可以是任意对象,所以会造成各种异常原因的崩溃。
这句资料参考至:http://www.cnblogs.com/dongweiq/p/9494033.html
好了 又要排除原因了,
项目中没有用到socket,排除;
项目中有sqlite数据库备份还原操作,涉及到文件流操作,检查后都有close,排除;
项目中没有较多以及导致死循环的线程,排除;
排除后又陷入了迷茫当中,到底还有什么会导致句柄泄露。
经过一段时间的分析后,关于文件的操作还有一个地方,播放人声提示音,这里播放提示音是使用SoundPool去实现的,在一次扫描中会播放两个提示音,经过237次的扫描,也就是播放了484的提示音,SoundPool的load方法生成一个AssetFileDescriptor对象,可能是没有关闭进而导致句柄超过了1024泄露了,
参考至https://www.cnblogs.com/l2rf/p/6051169.html。
所以 针对SoundPool做下修改
以下是我自己修改后的工具类
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.SoundPool;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
*
* 声音播放类
* Created by ZYB on 2017/7/21 0021.
*/
public class SoundPoolUtil {
private static SoundPoolUtil soundPoolUtil;
private static SoundPool soundPool;
private static List<AssetFileDescriptor> FileDescriptors ;
private SoundPoolUtil(){
FileDescriptors = new ArrayList<>();
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 100);
}
/**
* 单例模式
* @return
*/
public static SoundPoolUtil newInstance()
{
if (soundPoolUtil == null) {
soundPoolUtil = new SoundPoolUtil();
}
//清除soundpool资源,防止加载超过300次后没声音
if (soundPool != null)
{
soundPool.release();
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 100);
}
return soundPoolUtil;
}
/**
* 播放声音
* @param resid 资源id
* @param context 上下文对象
*/
public void playSoundPool(int resid, Context context)
{
try {
AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
final int soundid = soundPool.load(afd,1);
FileDescriptors.add(afd);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
soundPool.play(soundid,1,1,0,0,1f);
Log.w("sound_play", soundid +"");
soundPool.unload(soundid);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
//释放FD资源,防止出现句柄超过1024泄露
public void releaseFileDescriptor()
{
for (AssetFileDescriptor item : FileDescriptors)
{
try {
//释放FileDescriptor句柄
item.close();
item = null;
} catch (IOException e) {
e.printStackTrace();
}
}
//清空列表
FileDescriptors.clear();
}
}
在使用SoundPool的Activity中的onDestory方法调用SoundPool.newInstance().releaseFileDescriptor()。
修改后经过一轮测试,连续扫描1000多次也不会出现屏幕卡死/黑屏的问题了,问题解决。
多亏以下这些博客 我才解决了这个繁琐的问题:
https://www.cnblogs.com/l2rf/p/6051169.html
http://www.cnblogs.com/dongweiq/p/9494033.html
https://www.jianshu.com/p/e3830e7be8b2
————————————————
版权声明:本文为CSDN博主「乙-second」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_33617079/article/details/82316606
来源:oschina
链接:https://my.oschina.net/u/4000302/blog/3152810