目錄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