Android 服务+;线程=内存泄漏

Android 服务+;线程=内存泄漏,android,multithreading,service,garbage-collection,Android,Multithreading,Service,Garbage Collection,这是我的代码(简化了一点): 服务 public class TaskService extends Service { // ----------------------------------------------------------------------- // // Constants // // ----------------------------------------------------------------------- private static final

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

服务

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);
}
 }
}


正如您所看到的,它非常简单。

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

调试内存泄漏通常是一项非常艰巨的工作,不仅因为正如我上面所说的,它有时并不直接取决于您所声明的内容,而且还因为找到解决方案可能不是一件小事。你能做的最好的事情就是你看起来已经在做的事情:DDMS+HPROF。我不知道你有多少知识,但尽管这不是一个通用的方法,但它帮助我在我的代码中找到内存泄漏


尽管这看起来很琐碎,但调试这类事情的最佳方法是逐步删除部分代码(总的来说,这些代码意味着使用其他类的实例),并查看HPROF报告如何更改。

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

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


如果这不是问题所在,那么我们看不到代码的对象(如RegistrationTask)就是罪魁祸首-但听起来您可能已经尝试删除了代码位。。。您可以随时发布更多代码。

我又犯了一个愚蠢的错误)我发出了一个web请求,该请求将始终返回一个错误(用于测试),但在我的服务的错误处理程序中,我忘记从线程列表中删除AD。现在一切正常。

我理解所有这些,但我希望有人能告诉我哪里出了问题。此外,这几乎是我所有的代码(这是一个小演示)。这里唯一缺少的是实际的web请求,它是通过Google Http客户端和活动代码完成的,我只编写了一行代码来向服务发送意图。您是否尝试过按照我的建议使用DDMS进行调试?什么对象导致内存泄漏?是的,我在第一篇文章中没有提到,但如果我删除线程启动代码,堆会增加,但在DDMS中显式调用GC会释放保留的内存。GC会在应用程序的整个生命周期中保持运行。事实上,这并不意味着你有内存泄漏。如果您遇到非常频繁的GC调用,这并不是证明您存在内存泄漏,而是表明您可能存在内存泄漏。如果您使用DDMS的直方图,您将很容易看到泄漏的潜在原因(假设您有泄漏)。我只是混淆了eclipse显式调用GC在一种情况下会释放内存,而在另一种情况下不会。我不知道eclipse是否强制GC清理内存,或者只是建议这样做。不,活动引用在任何地方都没有使用。是的,我已经尝试删除了一些部分。根据您的更新并查看您的活动代码,正在传递对您的活动的引用-在“TaskService.registerInstallation(this);”-“this”表示主活动;如果创建了活动的新实例,它也将传入…-您可能应该引用getApplicationContext()或使用“startService”。奇怪的是,您是从服务内部调用“startService”。更具体地说,该调用应该从活动中进行-因此您可以通过从服务启动服务来保持服务中的上下文引用…不,这是一个静态方法。所以没有服务呼叫。我不是指实例,我是指对象。对方法的静态调用很容易导致内存泄漏:
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();
    }
}
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;
}