zoukankan      html  css  js  c++  java
  • 关于java.lang.IllegalArgumentException: View not attached to window manager 错误的分析

    今天遇到一个很奇特的问题,当用户设置了PIN码,在锁屏界面正常解锁PIN码后,进入Launcher时显示com.android.phone 已停止运行。一开始猜想会不会是解锁PIN码的时候处理导致了Phone进程报错,通过log分析找到了问题的大概原因:

    [plain] view plaincopy
     
    1. AndroidRuntime: FATAL EXCEPTION: main  
    2. AndroidRuntime: java.lang.IllegalArgumentException: View not attached to window manager  
    3. AndroidRuntime:     at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:385)  
    4. AndroidRuntime:     at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:287)  
    5. AndroidRuntime:     at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:79)  
    6. AndroidRuntime:     at android.app.Dialog.dismissDialog(Dialog.java:323)  
    7. AndroidRuntime:     at android.app.Dialog.dismiss(Dialog.java:306)  
    8. AndroidRuntime:     at com.android.stk.StkDialogActivity$4.onClick(StkDialogActivity.java:188)  
    9. AndroidRuntime:     at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:169)  
    10. AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:99)  
    11. AndroidRuntime:     at android.os.Looper.loop(Looper.java:153)  
    12. AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5299)  
    13. AndroidRuntime:     at java.lang.reflect.Method.invokeNative(Native Method)  
    14. AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:511)  
    15. AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)  
    16. AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)  
    17. AndroidRuntime:     at dalvik.system.NativeStart.main(Native Method)  
    转载请务必注明出处:http://blog.csdn.net/yihongyuelan
     
           通过分析PIN码的解锁流程知道,并不是PIN码解释时的dialog导致的问题。PIN码解锁流程另用文档说明。
    仔细查看log后发现有:
    [plain] view plaincopy
     
    1. AndroidRuntime:     at com.android.stk.StkDialogActivity$4.onClick(StkDialogActivity.java:188)  

           这是开机识别到SIM卡之后弹出的STK对话框。那为什么会报com.android.phone已停止运行呢?

           经过以上分析,我们可以大致猜测是因为STK引起的问题,既然是STK的问题那为什么会报phone的错误呢?如果是phone的错误那么从log中应该可以检索到phone相关的问题,为什么没有呢?那么我们接下来一一解答这些问题。

    (1). 为什么报错com.android.phone已停止运行?

           通过查看STK的源码(单卡MTK的代码在/mediatec/packages/app/stk1),在其AndroidManifest.xml可以发现:
    android:sharedUserId="android.uid.phone"
    也就是STK和Phon在一个进程中,因此在报错的表现上来讲就会是com.andorid.phone。

    (2). 为什么会报View not attached to window manager错误?

           这个错误的意思是说我们所操作的View没有被纳入window manager的管理。
           我们知道所有的窗口创建和管理都是依附于window manager的,因此Dialog的创建也不例外。Dialog的创建流程通过查看源码可以知道,在Dialog的构造函数中,创建了一个Window对象,但我们知道Window对象并不是用于显示的,真正用于显示的是View对象。因此通过Dialog的show方法构造了一个mDecor的View对象,并最终通过WindowManager的addView()方法显示Dialog。
           通过查看log信息我们可以看到com.android.stk.StkDialogActivity$4.onClick(StkDialogActivity.java:188)
    查看对应的StkDialogActivity代码后发现,在188行处代码为dialog.dismiss();
           在网络上搜索后发现,多数情况下出现这种错误,都是在dismiss Dialog时,发现创建该Dialog的Activity存在而导致的。
    比如在界面上显示一个Dialog,当任务处理结束后再Dismiss Dialog。如果在Dialog显示期间,该Activity因为某种原因被杀掉且又重新启动了,那么当任务结束时,Dismiss Dialog的时候WindowManager检查,就会发现该Dialog所属的Activity已经不存在了(重新启动了一次,是一个新的Activity),所以会报IllegalArgumentException: View not attached to window manager.
    通过以上分析我们可以知道在STK Dialog在执行dismiss方法时,发现启动它的Activity已经不见了,被杀掉了(现在这个是重新启动的),所以才报错出现异常。

    (3). 为什么STKDialogActivity会被"杀掉"?

           通过跟踪查看异常log我们可以看到StkDialogActivity的生命周期打印log如下:
    [plain] view plaincopy
     
    1. onCreate  
    2. onResume - mbSendResp[false], sim id: 0  
    3. ... ...省略部分  
    4. onSaveInstanceState  
    5. onPause, sim id: 0  
    6. ... ...省略部分  
    7. onDestroy-  
    8. onCreate  
    9. ... ...省略部分  
    10. onRestoreInstanceState - [com.android.internal.telephony.cat.TextMessage@41fe7d80]  
    11. onResume - mbSendResp[false], sim id: 0  
    到这里后就出现了异常错误……
           通过log我们可以很清楚的看到该Activity启动了两次。在log中我们也看到了onSaveInstanceState方法。
    对于onSaveInstanceState方法,在Android SDK里面有这样的描述:
    Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key)
    也就说当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按BACK键的时候。
           注意这里的容易二字,当前Activity并没有被销毁,只是系统觉得它有可能会被销毁因此会执行该方法。在该方法中我们可以保存Activity中的各种数据信息,如果该Activity真的被杀掉而又重新启动后,可以使用onRestoreInstanceState方法在重新启动该Activity时,还原我们之前保存的数据信息。onSaveInstanceState方法的调用遵循一个重要原则,即当系统“未经你许可”销毁了你的Activity时,onSaveInstanceState就会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然如果你自己不保存,那就没法恢复了)。
           因此通过以上分析我们可以看到STKDialogActivity的确被杀掉后再次启动了,但为什么会被杀掉,通过log并没有找到答案。

    (4). 在STKDialogActivity被杀掉时,Dialog存在么?

           可能大家会有疑问,Dialog都没有看到,就出现错误了,怎么能确定该Dialog当时一定是显示的呢?继续在log中搜索我们可以发现:
    [plain] view plaincopy
     
    1. WindowManager: Activity com.android.stk.StkDialogActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{41fea228 V.E..... R.....ID 0,0-1026,433} that was originally added here  
    2. WindowManager: android.view.WindowLeaked: Activity com.android.stk.StkDialogActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView{41fea228 V.E..... R.....ID 0,0-1026,433} that was originally added here  
    3. WindowManager:  at android.view.ViewRootImpl.<init>(ViewRootImpl.java:409)  
    4. WindowManager:  at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:218)  
    5. WindowManager:  at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)  
    6. WindowManager:  at android.app.Dialog.show(Dialog.java:281)  
    7. WindowManager:  at android.app.AlertDialog$Builder.show(AlertDialog.java:951)  
    8. WindowManager:  at com.android.stk.StkDialogActivity.onCreate(StkDialogActivity.java:192)  
    9. WindowManager:  at android.app.Activity.performCreate(Activity.java:5122)  
    10. WindowManager:  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1081)  
    11. WindowManager:  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2270)  
    12. WindowManager:  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2358)  
    13. WindowManager:  at android.app.ActivityThread.access$600(ActivityThread.java:156)  
    14. WindowManager:  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1340)  
    15. WindowManager:  at android.os.Handler.dispatchMessage(Handler.java:99)  
    16. WindowManager:  at android.os.Looper.loop(Looper.java:153)  
    17. WindowManager:  at android.app.ActivityThread.main(ActivityThread.java:5299)  
    18. WindowManager:  at java.lang.reflect.Method.invokeNative(Native Method)  
    19. WindowManager:  at java.lang.reflect.Method.invoke(Method.java:511)  
    20. WindowManager:  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)  
    21. WindowManager:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)  
    22. WindowManager:  at dalvik.system.NativeStart.main(Native Method)  
           这里的WindowManager报错是什么意思呢?这段log是WindowManager抛出的error错误,当我们的Dialog还没有dismiss时,如果此时该Activity被销毁了,那么就会出现以上错误,提示窗口泄漏(leaked window)。这里自己也做了一个实验,写一个demo,在Activity的onCreate方法中显示一个Dialog,然后直接调用finish方法。代码大致如下:
    [java] view plaincopy
     
    1. protected void onCreate(Bundle savedInstanceState) {  
    2.     super.onCreate(savedInstanceState);  
    3.     setContentView(R.layout.activity_main);  
    4.     AlertDialog.Builder info = new Builder(this);  
    5.     info.setTitle("Dialog").setPositiveButton("OK", null).setMessage("This is a Dialog");;  
    6.     info.show();   
    7.     finish();  
    8. }  
           直接将此程序run到模拟器或者真机上,查看log我们就能看到leaked window的报错信息。因此这也证明了前面我们的假设,即StkActivity在被销毁时,其所依附的Dialog是存在的。

    (5). 如何解决这个问题呢?

           通过以上分析之后我们知道了问题出现的原因,那么如何解决呢?可以通过以下两个方面来解决:

    1. 使用Activity自带的Dialog控制方法

           在Activity中需要使用对话框,可以使用Activity自带的回调,比如onCreateDialog(),showDialog(),dimissDialog(),removeDialog()等等。毕竟这些都是Activity自带的方法,所以用起来更方便,也不用显示创建和操控Dialog对象,一切都由框架操控,相对来说比较安全。

    2. 限制Dialog的生命周期

           让创建的Dialog对象的存活周期跟Activity的生命周期一致,也就是说Dialog的生命周期被限定在Activity的onCreate()和onDestroy()方法之间。

    原文:http://blog.csdn.net/yihongyuelan/article/details/9829313

  • 相关阅读:
    [LeetCode]2. Add Two Numbers链表相加
    Integration between Dynamics 365 and Dynamics 365 Finance and Operation
    向视图列添加自定义图标和提示信息 -- PowerApps / Dynamics365
    Update the Power Apps portals solution
    Migrate portal configuration
    Use variable to setup related components visible
    Loyalty management on Retail of Dynamic 365
    Modern Fluent UI controls in Power Apps
    Change screen size and orientation of a canvas app in Power App
    Communication Plan for Power Platform
  • 原文地址:https://www.cnblogs.com/veins/p/3860576.html
Copyright © 2011-2022 走看看