服务+线程=内存泄漏

时间:2014-02-04 12:13:24

标签: android multithreading service garbage-collection

这是我的代码(简化了一点):

服务

public class TaskService extends Service {
// -----------------------------------------------------------------------
//
// Constants
//
// -----------------------------------------------------------------------
private static final String EXTRA_TASK = "EXTRA_TASK";

private static final String TASK_REGISTER_INSTALLATION = "TASK_REGISTER_INSTALLATION";

private static Handler sHandler = new Handler();

// -----------------------------------------------------------------------
//
// Statics
//
// -----------------------------------------------------------------------
public static void registerInstallation(Context context) {
    Intent intent = new Intent(context, TaskService.class);
    intent.putExtra(EXTRA_TASK, TASK_REGISTER_INSTALLATION);
    context.startService(intent);
}

// -----------------------------------------------------------------------
//
// Fields
//
// -----------------------------------------------------------------------
private List<BaseTask> mTasks = new ArrayList<BaseTask>();

// -----------------------------------------------------------------------
//
// Methods
//
// -----------------------------------------------------------------------
@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (intent != null)
        handleIntent(intent);
    return Service.START_NOT_STICKY;
}

private void handleIntent(Intent intent) {
    String taskType = intent.getStringExtra(EXTRA_TASK);
    if (taskType.equalsIgnoreCase(TASK_REGISTER_INSTALLATION)) {
        RegistrationTask task = new RegistrationTask("",
                "", "", "", "");
        task.setTaskListener(sHandler, mRegistrationListener);
        mTasks.add(task);
        task.start();
    }
}

@Override
public void onDestroy() {
    for (BaseTask task : mTasks)
        task.interrupt();
    mTasks.clear();
    Log.d(TaskService.class.getSimpleName(), "onDestroy");
    super.onDestroy();
}

private TaskListener<String, String, String> mRegistrationListener = new TaskListener<String, String, String>() {
    @Override
    public void onResult(String result, BaseTask task) {
        mTasks.remove(task);
    }

    @Override
    public void onProgress(String progress, BaseTask task) {
        mTasks.remove(task);
    }

    @Override
    public void onError(String error, BaseTask task) {
        Toast.makeText(TaskService.this, error, Toast.LENGTH_SHORT).show();
    }
};

public static interface TaskListener<ResultType, ProgressType, ErrorType> {
    public void onError(ErrorType error, BaseTask task);

    public void onProgress(ProgressType progress, BaseTask task);

    public void onResult(ResultType result, BaseTask task);
}
 }

public class BaseTask<ResultType, ProgressType, ErrorType> extends Thread {
protected Handler mHandler;
protected TaskListener<ResultType, ProgressType, ErrorType> mTaskListener;
protected HttpRequestFactory mRequestFactory;

public BaseTask() {
    try {
        ApacheHttpTransport.Builder builder = new ApacheHttpTransport.Builder();

        mRequestFactory = builder.doNotValidateCertificate().build()
                .createRequestFactory();
    } catch (GeneralSecurityException e) {
        e.printStackTrace();
    }
}

public void setTaskListener(Handler handler,
        TaskListener<ResultType, ProgressType, ErrorType> listener) {
    mHandler = handler;
    mTaskListener = listener;
}

protected void onError(final ErrorType error) {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mTaskListener.onError(error, BaseTask.this);
        }
    });
}

protected void onProgress(final ProgressType progress) {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mTaskListener.onProgress(progress, BaseTask.this);
        }
    });
}

protected void onResult(final ResultType result) {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mTaskListener.onResult(result, BaseTask.this);
        }
    });
}

  }

public class RegistrationTask extends BaseTask<String, String, String> {

public RegistrationTask(...) {
    super();
}

@Override
public void run() {
    try {
                //Simple web request executed here
    } catch (HttpResponseException e) {
        e.printStackTrace();
        onError(e.getContent());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

我还有Activity每次调用Intent方法发送onCreate以启动任务。问题是,经过几次重启后,我得到大约300rb泄漏,即使我从Eclipse调用GC,我也没有被释放。我做错了什么?

更新 活动代码:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    TaskService.registerInstallation(this);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

}

你可以看到它非常简单。

3 个答案:

答案 0 :(得分:0)

这个答案不会给你一个明确的解决方案,不是因为我不愿意,而是因为它不可能(甚至更难,不仅仅是查看你的代码,而是非常了解你的代码)。但根据我的经验,我可以告诉你,由于直接引用的对象不会发生那种内存泄漏 - 你声明的对象(并继续引用另一个类/对象)依赖于许多其他类等等,并且可能您看到由于对任何实例的错误处理而导致内存泄漏,同时引用其他实例。

调试内存泄漏通常是一项非常艰苦的工作,不仅仅是因为正如我上面所述,它有时并不直接依赖于您声明的内容,而且因为找到解决方案可能并非易事。您可以做的最好的事情就是您似乎正在做的事情:DDMS + HPROF。我不知道你有多少知识,但是虽然它不是一种通用的方法,this link帮助我找到了代码中的内存泄漏。

虽然看起来微不足道,但调试这类事情的最佳方法是逐步删除代码的一部分(总体而言,意味着使用其他类的实例)并查看HPROF报告的更改方式。

答案 1 :(得分:0)

您的服务不会随着您的活动而破坏。最有可能的是,您在服务中生成的每个线程中保留对Activity的引用,这意味着它不会被销毁。

如果您需要引用您的活动,您应该使用WeakReference:

How to use WeakReference in Java and Android development?

如果这不是问题,那么像我们看不到代码的RegistrationTask这样的对象就是罪魁祸首 - 但听起来你可能已经尝试删除代码位......你总是可以发布更多的代码。 / p>

答案 2 :(得分:0)

再一次,我刚刚犯了一个愚蠢的错误)我做了一个Web请求,总是会返回一个错误(对于测试),但在我的服务的错误处理程序中,我忘了从Threads列表中删除Thead。现在一切都按照我的预期运作。