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