关闭并重新打开应用程序时Android崩溃

Posted

技术标签:

【中文标题】关闭并重新打开应用程序时Android崩溃【英文标题】:Android crash when app is closed and reopened 【发布时间】:2011-07-16 04:28:48 【问题描述】:

我有一个非常简单的 android 应用程序,它只显示一个空白屏幕。当我通过按 HOME 按钮关闭应用程序,然后尝试再次打开应用程序时,它崩溃并且我得到“强制关闭”按钮。在 Eclipse 中,我收到此错误,“ActivityManager:警告:活动未启动,因为当前活动正在为用户保留。”。如何修复此崩溃?

public class HelloAndroid extends Activity 

@Override
public void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(   WindowManager.LayoutParams.FLAG_FULLSCREEN,   
                            WindowManager.LayoutParams.FLAG_FULLSCREEN); 

    setContentView(new Panel(this));


class Panel extends SurfaceView implements SurfaceHolder.Callback 

    private TutorialThread _thread;

    public Panel(Context context) 
        super(context);

        // register our interest in hearing about changes to our surface
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        _thread = new TutorialThread(holder, this);

        setFocusable(true);
    

    @Override
    public void onDraw(Canvas canvas) 

        // Clear the background
        canvas.drawColor(Color.WHITE);
    

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
        // resize canvas here
    

    @Override
    public void surfaceCreated(SurfaceHolder holder) 
        _thread.setRunning(true);
        _thread.start();
    

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) 
        // simply copied from sample application LunarLander:
        // we have to tell thread to shut down & wait for it to finish, or else
        // it might touch the Surface after we return and explode
        boolean retry = true;
        _thread.setRunning(false);
        while (retry) 
            try 
                _thread.join();
                retry = false;
             catch (InterruptedException e) 
                // we will try it again and again...
            
        
    


class TutorialThread extends Thread 
    private SurfaceHolder _surfaceHolder;
    private Panel _panel;
    private boolean _run = false;

    public TutorialThread(SurfaceHolder surfaceHolder, Panel panel) 
        _surfaceHolder = surfaceHolder;
        _panel = panel;
    

    public void setRunning(boolean run) 
        _run = run;
    

    @Override
    public void run() 
        Canvas c;
        while (_run) 
            c = null;
            try 
                c = _surfaceHolder.lockCanvas(null);
                synchronized (_surfaceHolder) 
                    _panel.onDraw(c);
                
             finally 
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) 
                    _surfaceHolder.unlockCanvasAndPost(c);
                
            
        
    

添加 LogCat

03-15 15:36:05.579: INFO/AndroidRuntime(4441): NOTE: attach of thread 'Binder Thread #2' failed
03-15 15:36:05.719: DEBUG/AndroidRuntime(4449): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
03-15 15:36:05.719: DEBUG/AndroidRuntime(4449): CheckJNI is OFF
03-15 15:36:05.719: DEBUG/dalvikvm(4449): creating instr width table
03-15 15:36:05.759: DEBUG/AndroidRuntime(4449): --- registering native functions ---
03-15 15:36:05.969: INFO/ActivityManager(1294): Starting activity: Intent  act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 cmp=com.example.helloandroid/.HelloAndroid 
03-15 15:36:05.979: DEBUG/Launcher(1371): onPause+
03-15 15:36:05.979: DEBUG/Launcher.DragController(1371): +endDrag: false
03-15 15:36:05.979: DEBUG/Launcher.DragController(1371): mDragging == false
03-15 15:36:05.979: DEBUG/Launcher.DragController(1371): -endDrag: false
03-15 15:36:05.979: DEBUG/Launcher(1371): onPause-
03-15 15:36:05.999: DEBUG/AndroidRuntime(4428): Shutting down VM
03-15 15:36:05.999: DEBUG/AndroidRuntime(4449): Shutting down VM
03-15 15:36:05.999: WARN/dalvikvm(4428): threadid=1: thread exiting with uncaught exception (group=0x4001d7e0)
03-15 15:36:06.009: DEBUG/dalvikvm(4449): Debugger has detached; object registry had 1 entries
03-15 15:36:06.009: INFO/AndroidRuntime(4449): NOTE: attach of thread 'Binder Thread #3' failed
03-15 15:36:06.029: ERROR/AndroidRuntime(4428): FATAL EXCEPTION: main
03-15 15:36:06.029: ERROR/AndroidRuntime(4428): java.lang.IllegalThreadStateException: Thread already started.
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at java.lang.Thread.start(Thread.java:1322)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at com.example.helloandroid.HelloAndroid$Panel.surfaceCreated(HelloAndroid.java:55)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.SurfaceView.updateWindow(SurfaceView.java:538)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:206)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.View.dispatchWindowVisibilityChanged(View.java:3888)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:725)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:725)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewRoot.performTraversals(ViewRoot.java:748)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1737)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.os.Handler.dispatchMessage(Handler.java:99)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.os.Looper.loop(Looper.java:123)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at android.app.ActivityThread.main(ActivityThread.java:4627)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at java.lang.reflect.Method.invokeNative(Native Method)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at java.lang.reflect.Method.invoke(Method.java:521)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
03-15 15:36:06.029: ERROR/AndroidRuntime(4428):     at dalvik.system.NativeStart.main(Native Method)
03-15 15:36:06.039: WARN/ActivityManager(1294):   Force finishing activity com.example.helloandroid/.HelloAndroid
03-15 15:36:06.541: WARN/ActivityManager(1294): Activity pause timeout for HistoryRecord450300c0 com.example.helloandroid/.HelloAndroid
03-15 15:36:06.549: DEBUG/Launcher(1371): onResume+
03-15 15:36:06.549: DEBUG/Launcher.DragController(1371): +endDrag: false
03-15 15:36:06.549: DEBUG/Launcher.DragController(1371): mDragging == false
03-15 15:36:06.549: DEBUG/Launcher.DragController(1371): -endDrag: false
03-15 15:36:06.549: DEBUG/Launcher(1371): onResume-
03-15 15:36:08.645: ERROR/KINETO(1370): KLOG0C3- xmk_QueryOSQueue SDL Queue empty : WAIT_FOREVER 

【问题讨论】:

是的,错误是由您的Thread 引起的。看看我下面的答案。 LunarLander 和 JetBoy 在被 HOME 中断并重新启动后都崩溃了! SurfaceView / Activity 生命周期在这里讨论:source.android.com/devices/graphics/architecture.html#activity。 Grafika 中的工作代码示例链接自***.com/questions/21567641/… 【参考方案1】:

我已经回答了这样的问题here。

您遇到的错误可能是由您的Thread 引起的(虽然没有看到完整的 Logcat,但很难判断)。您每次创建表面时都会启动它,这将使您的应用程序崩溃,因为您不能两次调用Thread.start()。请查看我上面的链接,以更深入地描述该问题以及您应该如何解决它。

由于我的解释不够,我将发布整个解决方案:

在你的Runnable/Thread

private Object mPauseLock = new Object();  
private boolean mPaused;

// Constructor stuff.      

// This should be after your drawing/update code inside your thread's run() code.
synchronized (mPauseLock) 
    while (mPaused) 
        try 
            mPauseLock.wait();
         catch (InterruptedException e) 
        
    


// Two methods for your Runnable/Thread class to manage the thread properly.
public void onPause() 
    synchronized (mPauseLock) 
        mPaused = true;
    


public void onResume() 
    synchronized (mPauseLock) 
        mPaused = false;
        mPauseLock.notifyAll();
    

在您的SurfaceView 班级中:

private boolean mGameIsRunning;

@Override
public void surfaceCreated(SurfaceHolder holder) 
    // Your own start method.
    start();


public void start() 
    if (!mGameIsRunning) 
        thread.start();
        mGameIsRunning = true;
     else 
        thread.onResume();
    

【讨论】:

Viktor 我无法让您的代码与问题中的线程一起工作。在 surfaceDestroyed 上调用关闭线程并在用户每次离开此活动时运行,包括按下 Home 按钮时。 @Lumis:你不应该在surfaceDestroyed 中关闭它,因为它不需要(我们已经在onPause 中暂停它。你应该在onResume 中处理Thread,@ 987654332@ 和 onDestroy.【参考方案2】:

测试了以下解决方案。代码检查线程状态,如果终止则创建一个新的。 没有崩溃,我能看到的唯一问题是游戏状态没有保存,所以基本上从 HOME 键返回,重新开始游戏。 p.s.记得从 Lunarview 视图传递上下文并设置为 mContextLunarView。 希望这可以帮助。这些论坛真棒。继续努力吧。

public void surfaceCreated(SurfaceHolder holder) 
    // start the thread here so that we don't busy-wait in run()
    // waiting for the surface to be created    

    if(thread.getState() == Thread.State.TERMINATED) 
        //LunarView Thread state TERMINATED..make new...under CheckCreateThread

        thread = new LunarThread(holder, mContextLunarView, new Handler() 
            @Override
            public void handleMessage(Message m) 
                mStatusText.setVisibility(m.getData().getInt("viz"));
                mStatusText.setText(m.getData().getString("text"));
            
        );     
       

    thread.setRunning(true);
    thread.start();

【讨论】:

【参考方案3】:

这是一个简单的解决方案,在某些情况下可能是可以接受的,例如背景动画屏幕和不需要恢复状态的活动 - 表面视图活动需要在暂停时完成。

protected void onPause()   
 super.onPause(); 
 finish();
 

更好的解决方案是将线程的创建从构造函数移到surfaceCretaed上,如下所示:

@Override
public void surfaceCreated(SurfaceHolder holder) 
  _thread = new TutorialThread(holder, this);
  _thread.setRunning(true);
  _thread.start(); 

然后在线程循环中创建一个暂停标志:

if(!pause)
  _panel.onDraw(c); 

最后在onPause和onRestore为activity设置了暂停标志:

   protected void onResume() 
            super.onResume();
            pause = false;    
        

        protected void onPause()     
            super.onPause();   
            pause = true;   
         

当用户单击 Home 按钮时,将调用 surfaceDestroyed,这将关闭当前线程“_thread”。当他返回应用程序时,surfaceCreated 会将引用“_thread”分配给新线程,而旧的线程对象将被垃圾收集器移除。

【讨论】:

这不是正确的处理方式。 如果您知道为什么不编辑问题中的代码以使其正常工作?否则,这都是理论上的......当我想出一个工作代码时,我会发布它。 @Lumis:查看我的答案和里面的链接。有一个解决方案,您必须使用synchronized 和锁来正确管理Thread 我认为由于问题中发布的示例来自Android SDK游戏MoonLander和JetBoy,因此会被很多人使用,我们应该编写一个工作代码/解决方案并将其发布在这里这样其他人就不必重新发明***,并且可以从一个有希望的好例子中学习。 @Lumis:然后等一下,我会发布整个解决方案。【参考方案4】:

当你想通过点击返回按钮退出时使用这个...

boolean doubleBackToExitPressedOnce = false;
@Override
public void onBackPressed() 
    if (doubleBackToExitPressedOnce) 

        //**completely exit the app like this**//
      
        System.exit(0);

        return;
    

    this.doubleBackToExitPressedOnce = true;
    Toast.makeText(this, "Tap again to exit", Toast.LENGTH_SHORT).show();

    new Handler().postDelayed(() -> doubleBackToExitPressedOnce=false, 2000);

【讨论】:

以上是关于关闭并重新打开应用程序时Android崩溃的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中打开一个关闭的 NSWindow 会导致应用程序崩溃

关闭和重新打开应用程序后,Android 画布游戏循环中断 onResume()

iOS Wikiitude SDK 在退出/重新打开应用时崩溃

重新打开应用程序时出现 Moengage 问题

Windows 10 崩溃查询

即使在 ios 等 android 应用程序崩溃后重新加载应用程序