目录html
背景:java
卡死/黑屏log信息,缘由分析:linux
背景:
一个盘点扫描的APP,能够离线在线操做,运行平台为PDA,客户在使用过程当中反馈通过屡次扫描后会出现屏幕卡死/黑屏的状况,可是PDA系统能够正常使用,因而可知是个人APP致使的。客户一次盘点会盘点近千个物品,也就是说会扫描近千次,扫描过程当中会有人声提示音播放。收到了客户的反馈后,拿了设备回来本身测,经过几次合的连续扫描后,复现出了这个问题,每回合都是扫到237次就出现黑屏或者报错。拿到了出现问题的log信息,可是一看logcat,很难肯定缘由,不是常规的代码崩溃。如下看下我拿到的logcat信息android
卡死/黑屏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之类的控件,因此这个多是由其余报错影响致使。sql
这个时候,咱们就重点看第二个报错信息Could not read input channel file descriptors from parcel数据库
经过百度获得了一些信息,可能致使出现这个报错的几个缘由app
RemoteView中添加的图片太大了,超过40K会报这个异常(PS:排除,项目中没有用到这个)
Intent传递的数据太大了超过1M也会报这个错误(PS:排除)
FileDescripter太多并且没有关闭,looper太多没有quit。
试试在AndroidManefest.xml中对当前Activity配置 (PS:项目中是强制竖屏)
configchange=“orientation|keyboardHidden”强制在Activity横竖屏切换的时候不从新onCreate。
经过排除最终定位到了FileDescripter太多并且没有关闭这个可疑的缘由,这个是什么东西,我也不知道,只能再百度下。socket
能够参考https://www.jianshu.com/p/e3830e7be8b2ide
能够知道这个是句柄泄露照成的,一个进程拥有的最大句柄数通常是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