0

大家好,

我正在开发一个针对 API 7 的 android 应用程序,此时我使用需要重新启动的活动。假设我的活动如下所示:

public class AllocActivity extends Activity implements OnClickListener{

    Button but;
    private Handler hand = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_alloc);

        but = (Button) findViewById(R.id.button);
        but.setText("RELOAD");
        but.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0){
                Intent intent = getIntent();
                startActivity(intent);
                finish();
            }
        });  
    }

    @Override
    protected void onDestroy(){
        super.onDestroy();
        System.gc();
    }

    /****** THREADS AND RUNNABLES ******/

    final Runnable fullAnim = new Thread(new Runnable(){
        @Override
        public void run(){
            try{
                hand.post(anim1);
                Thread.sleep(2000);
                hand.post(anim2);
                Thread.sleep(1000);
                // and so on
            }catch(InterruptedException ie){ie.printStackTrace();}
        }
    });

    final Runnable anim1 = new Runnable() {
        @Override
        public void run(){
            // non-static method findViewById
            ImageView sky = (ImageView)findViewById(R.id.sky);
        }
    };
}

问题是 gc 似乎没有释放 fullAnim 线程,因此每次重新启动时堆都会增长约 100K - 直到它变慢并崩溃。将 fullAnim 声明为静态确实解决了这个问题 - 但是当我使用非静态引用时,这对我来说不起作用。

所以在这一点上,我有点迷失了——我希望你能告诉我下一步该去哪里。是否有什么我可能做错了,或者是否有一个工具可以用来管理线程以在重新启动后丢弃和释放堆。

亲切的问候

更新

感谢所有回答的人 - 帮助很大。使用 TimerTask 最终成功了。我做了以下更改:

/****** THREADS AND RUNNABLES ******/

final TimerTask fullAnim = new TimerTask(){
    @Override
    public void run(){
        try{
            hand.post(anim1);
            Thread.sleep(2000);
            hand.post(anim2);
            Thread.sleep(1000);
            // and so on
        }catch(InterruptedException ie){ie.printStackTrace();}
    }
};

由于活动长度超过 6k loc,这是一个相当不错的解决方案,不会面临更大的影响。赞!

我不使用计时器来安排任务 - 不知道它是否是不好的做法,但动画是这样调用的:

Thread t = new Thread(fullAnim);
t.start();
4

4 回答 4

3
  • 跑步Thread永远不会被垃圾收集。
  • 如果您Activity停止或被破坏,线程不会自动停止。它可以永远运行。
  • 每个非静态内部类都保留对封闭实例的引用。例如hand.post(anim1);,在该内部类中工作,因为它具有对 的隐式引用AllocActivity.this

因此,您实际上要做的是将对您的引用保持Activity比它应该存在的时间更长的时间,即直到 after onDestroy

如果您不再需要线程,请确保手动停止线程。

于 2013-08-24T07:41:19.787 回答
2

与任何 Java VM 一样,堆内存将自动增长到最大大小。但是,位图是在 VM 外部分配的,因此您不会在统计信息中轻松“看到”它们。你能做的最好的事情是确保你不使用大位图,或者使用 http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html缩小它们

在 Eclipse 中,您可以在 Android 1.6 或更高版本上生成堆转储,并且可以使用 Eclipse MAT 分析转储。

通常,您无法控制真实设备上的最大堆大小,除非您使用自定义硬件或固件。

developer.android.com 上应该有一篇关于在 1.6 上转储堆的文章,但我找不到。:(

编辑另外,我不得不提一下,您可以通过使用为应用程序请求更多内存

 android:largeHeap="true"

在清单中。但这是非常不明智的,因为大多数应用程序不需要这个。

于 2013-08-24T07:15:56.470 回答
1

因为final 变量对于 GC 的优先级较低。因此,您需要在 onPause() 方法中显式释放可运行对象,因为不能确保 onDestory() 将在 finish() 调用之后立即调用。

@Override
    protected void onPause(){
        super.onPause();
        //cancel timer to stop animations
       if(t!=null){
        t.cancel();
       }

        System.gc();
    }

更新

使用计时器来实现这一点

boolean isFirstAnim=true;
Timer t = new Timer();
        t.schedule(new TimerTask() {

            @Override
            public void run() {
                          if(isFirstAnim){
                              // play your first animation at every
                          }else{
                              // play your second animation at every
                          }                 
            }
        }, 0, 3000);
于 2013-08-24T07:15:57.957 回答
1

当应用程序的所有活动完成时会发生什么?

“当你调用完成()这并不意味着 Activity 实例被垃圾收集。你告诉 Android 你想关闭 Activity(不再显示它)。它仍然存在,直到 Android 决定终止进程(并因此终止 DVM)或实例被垃圾收集。”

您需要实现自己的停止方法来停止正在运行的线程,您可以在 onDestroy 中对其进行调用

参考这个停止一个可运行的

或者,您可以在 asynctask 中执行操作并使用 onProgressUpdate() 在 UI 线程上发布进度,并结合使用 cancel(true) 和检查 doInBackground() 是否已调用取消来停止任务。

于 2013-08-24T08:09:52.107 回答