Android:旋转时崩溃,从横向到纵向

7
我正在研究Android,并创建了一个简单的Twitter应用程序来测试我所学到的知识。但我遇到了一个我不理解的问题。我创建了一个线程来获取推文,没有问题,但是随后添加了一个ProgressDialog来显示该线程正在运行。这样做导致了非常不寻常的行为。如果用户从垂直方向旋转到水平方向,程序表现正常,没有问题,但如果他然后再次旋转回来,程序会崩溃并显示窗口泄漏的错误消息。
这个问题只在水平到竖直布局切换时发生。我知道如果你尝试在对话框框运行时旋转会有问题,但是我已经确保对话框框完成运行并关闭后旋转,它仍然会崩溃。以下是我的代码:
package com.onesmartpuppy.puppytweet;
import java.util.ArrayList;
import com.github.droidfu.widgets.WebImageView;
import winterwell.jtwitter.Twitter;
import winterwell.jtwitter.Twitter.Status;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.TextView;

public class PuppyTweet extends ListActivity implements OnClickListener {

    private ProgressDialog m_ProgressDialog = null;
    private ArrayList<Status> messages = null;
    private TweetAdapter m_adapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        messages = new ArrayList<Status>();
        this.m_adapter = new TweetAdapter(this, R.layout.row, messages);
        setListAdapter(this.m_adapter);

        setupButtons();

        m_ProgressDialog = ProgressDialog.show(this,    
             "Please wait...", "Retrieving tweets ...", true);

        Runnable findTweets = new Runnable(){
            @Override
            public void run() {

             try {
                messages = new ArrayList<Status>();

                Twitter ourTwitter = new Twitter("*****", "******");
                messages = (ArrayList<Status>) ourTwitter.getFriendsTimeline();

                Thread.sleep(1000);
                Log.i("ARRAY", ""+ messages.size());
             } catch (Exception e) {
                Log.e("BACKGROUND_PROC", e.getMessage());
             }
              handler.sendEmptyMessage(0);   
             }
         };

        Thread thread =  new Thread(null, findTweets, "FindTweets");
        thread.start();
    }



    private void setupButtons()
    {
     Button refreshButton = (Button) findViewById(R.id.refresh_button);
     refreshButton.setOnClickListener(this);
     Button settingsButton = (Button) findViewById(R.id.settings_button);
     settingsButton.setOnClickListener(this);
     Button tweetButton = (Button) findViewById(R.id.update_button);
     tweetButton.setOnClickListener(this);
    }



 @Override
 public void onClick(View v) {
  switch (v.getId())
  {
  case R.id.settings_button:
   break;
  case R.id.update_button:
   break;
  case R.id.refresh_button:
   break;
  default:
   break;
  }
 }

 private Handler handler = new Handler() {

         @Override
         public void handleMessage(Message msg) {
             if(messages != null && messages.size() > 0){
              m_adapter.clear();
                 m_adapter.notifyDataSetChanged();
                 for(int i=0;i<messages.size();i++)
                  m_adapter.add(messages.get(i));
             }
             m_ProgressDialog.dismiss();
             m_adapter.notifyDataSetChanged();
         }
  };


  private class TweetAdapter extends ArrayAdapter<Status> {

      private ArrayList<Status> items;

      public TweetAdapter(Context context, int textViewResourceId, ArrayList<Status> items) {
              super(context, textViewResourceId, items);
              this.items = items;
      }

      @Override
      public View getView(int position, View convertView, ViewGroup parent) {
              View v = convertView;
              if (v == null) {
                  LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                  v = vi.inflate(R.layout.row2, null);
              }
              Status o = (Status) items.get(position);

              if (o != null) {
                WebImageView img = (WebImageView) v.findViewById(R.id.webimage);                   
                      TextView tt = (TextView) v.findViewById(R.id.toptext);
                      TextView bt = (TextView) v.findViewById(R.id.bottomtext);
                      if (tt != null) {
                            tt.setText("Name: " + o.user.name);                            }
                      if(bt != null){
                            bt.setText("Status: " + o.getText());
                      }
                      if (img !=null) {
                       img.setImageUrl(o.getUser().profileImageUrl.toString());
                          img.loadImage();
                      }
              }
              return v;
      }


  }
}

以下是应用崩溃时的LogCat:

02-10 16:01:05.237: INFO/ActivityManager(63): Config changed: { scale=1.0 imsi=310/260 loc=ld_US touch=3 keys=2/1/2 nav=3/1 orien=2 layout=18}
02-10 16:01:05.344: WARN/UsageStats(63): Something wrong here, didn't expect com.onesmartpuppy.puppytweet to be resumed
02-10 16:01:05.384: INFO/WindowManager(63): Setting rotation to 0, animFlags=0
02-10 16:01:05.394: INFO/ActivityManager(63): Config changed: { scale=1.0 imsi=310/260 loc=ld_US touch=3 keys=2/1/2 nav=3/1 orien=1 layout=18}
02-10 16:01:05.504: WARN/UsageStats(63): Something wrong here, didn't expect com.onesmartpuppy.puppytweet to be resumed
02-10 16:01:05.704: ERROR/WindowManager(227): Activity com.onesmartpuppy.puppytweet.PuppyTweet has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@43af9490 that was originally added here
02-10 16:01:05.704: ERROR/WindowManager(227): android.view.WindowLeaked: Activity com.onesmartpuppy.puppytweet.PuppyTweet has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@43af9490 that was originally added here
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.view.ViewRoot.<init>(ViewRoot.java:227)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.view.Window$LocalWindowManager.addView(Window.java:424)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.Dialog.show(Dialog.java:239)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ProgressDialog.show(ProgressDialog.java:107)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ProgressDialog.show(ProgressDialog.java:90)
02-10 16:01:05.704: ERROR/WindowManager(227):     at com.onesmartpuppy.puppytweet.PuppyTweet.onCreate(PuppyTweet.java:39)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2417)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2470)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3573)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ActivityThread.access$2300(ActivityThread.java:119)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1825)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.os.Looper.loop(Looper.java:123)
02-10 16:01:05.704: ERROR/WindowManager(227):     at android.app.ActivityThread.main(ActivityThread.java:4310)
02-10 16:01:05.704: ERROR/WindowManager(227):     at java.lang.reflect.Method.invokeNative(Native Method)
02-10 16:01:05.704: ERROR/WindowManager(227):     at java.lang.reflect.Method.invoke(Method.java:521)
02-10 16:01:05.704: ERROR/WindowManager(227):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
02-10 16:01:05.704: ERROR/WindowManager(227):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
02-10 16:01:05.704: ERROR/WindowManager(227):     at dalvik.system.NativeStart.main(Native Method)
02-10 16:01:06.344: INFO/global(227): Default buffer size used in BufferedReader constructor. It would be better to be explicit if an 8k-char buffer is required.
02-10 16:01:06.624: DEBUG/dalvikvm(227): GC freed 6082 objects / 479568 bytes in 77ms
02-10 16:01:07.374: DEBUG/dalvikvm(227): GC freed 11982 objects / 709808 bytes in 83ms
02-10 16:01:07.394: INFO/global(227): Default buffer size used in BufferedReader constructor. It would be better to be explicit if an 8k-char buffer is required.
02-10 16:01:07.564: DEBUG/dalvikvm(227): GC freed 95 objects / 79880 bytes in 68ms
02-10 16:01:07.704: DEBUG/dalvikvm(227): GC freed 8 objects / 38992 bytes in 65ms
02-10 16:01:08.174: DEBUG/dalvikvm(63): GC freed 5407 objects / 257208 bytes in 174ms
02-10 16:01:08.194: INFO/ARRAY(227): 20
02-10 16:01:08.204: DEBUG/AndroidRuntime(227): Shutting down VM
02-10 16:01:08.204: WARN/dalvikvm(227): threadid=3: thread exiting with uncaught exception (group=0x4001b188)
02-10 16:01:08.204: ERROR/AndroidRuntime(227): Uncaught handler: thread main exiting due to uncaught exception
02-10 16:01:08.214: ERROR/AndroidRuntime(227): java.lang.IllegalArgumentException: View not attached to window manager
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:200)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.view.Window$LocalWindowManager.removeView(Window.java:432)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.app.Dialog.dismissDialog(Dialog.java:280)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.app.Dialog.access$000(Dialog.java:73)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.app.Dialog$1.run(Dialog.java:109)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.app.Dialog.dismiss(Dialog.java:264)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at com.onesmartpuppy.puppytweet.PuppyTweet$1.handleMessage(PuppyTweet.java:104)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.os.Looper.loop(Looper.java:123)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at android.app.ActivityThread.main(ActivityThread.java:4310)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at java.lang.reflect.Method.invokeNative(Native Method)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at java.lang.reflect.Method.invoke(Method.java:521)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
02-10 16:01:08.214: ERROR/AndroidRuntime(227):     at dalvik.system.NativeStart.main(Native Method)
02-10 16:01:08.244: INFO/Process(63): Sending signal. PID: 227 SIG: 3
02-10 16:01:08.244: INFO/dalvikvm(227): threadid=7: reacting to signal 3
02-10 16:01:08.274: ERROR/dalvikvm(227): Unable to open stack trace file '/data/anr/traces.txt': Permission denied
02-10 16:01:08.754: DEBUG/dalvikvm(227): GC freed 5986 objects / 372656 bytes in 316ms
02-10 16:01:09.829: INFO/ARRAY(227): 20
02-10 16:01:09.955: INFO/Process(227): Sending signal. PID: 227 SIG: 9
02-10 16:01:09.974: INFO/ActivityManager(63): Process com.onesmartpuppy.puppytweet (pid 227) has died.
02-10 16:01:10.014: INFO/WindowManager(63): WIN DEATH: Window{43b653e0 com.onesmartpuppy.puppytweet/com.onesmartpuppy.puppytweet.PuppyTweet paused=false}
02-10 16:01:10.014: INFO/WindowManager(63): WIN DEATH: Window{43c32b48 Please wait... paused=false}
02-10 16:01:10.164: WARN/UsageStats(63): Unexpected resume of com.android.launcher while already resumed in com.onesmartpuppy.puppytweet
02-10 16:01:10.314: WARN/InputManagerService(63): Got RemoteException sending setActive(false) notification to pid 227 uid 10023

2
利用Android的LogCat功能,在代码中留下Log类的调试语句,打开Android上的LogCat透视窗口并发布堆栈跟踪。它将显示窗口泄漏发生在哪一行,以及可能修复它的方法。 - Anthony Forloney
处理onDestroy和onPause事件以终止该线程可能是值得的。虽然这可能不是问题的根本原因。 - Patrick Kafka
我添加了从logcat中获取的内容。 - Kelend
1个回答

9
在日志中出现的WindowLeaked异常通常发生在您有某种异步任务,该任务在启动它的活动被销毁后才完成。
当您旋转屏幕时,活动将被重新创建。根据Android文档,您应该在onPause方法中保存任何实例状态,包括确保停止任何异步任务。您可以在onCreate方法中使用保存的捆绑包进行重新创建。

2
我在onPause方法中添加了另一个dismiss()来处理对话框,已经修复了所有问题。谢谢! - Kelend
感谢您让我更好地理解这个问题以及为什么解决方案有效。这个问题已经存在很长时间了!现在,由于我在PreferenceActivity的onPause()中解除自定义AlertDialog,所以不会再出现WindowLeak问题。只有在显示对话框时手机旋转才会发生这种情况。 - Sean A.O. Harney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接