如何防止 JobService 使用的 AsyncTask 中的上下文泄漏
Posted
技术标签:
【中文标题】如何防止 JobService 使用的 AsyncTask 中的上下文泄漏【英文标题】:How to prevent Context leak in AsyncTask used by JobService 【发布时间】:2018-02-08 09:27:48 【问题描述】:我需要做一些后台工作,这需要 JobService 中的上下文(我正在使用 Firebase JobDispatcher,因为我们支持 api 16+)我已经阅读了很多关于 JobService 和 AsyncTasks 的文章,但我如果您需要上下文,找不到任何关于如何组合它们的好文章。
我的工作服务
import com.firebase.jobdispatcher.JobParameters;
import com.firebase.jobdispatcher.JobService;
public class AsyncJobService extends JobService
@Override
public boolean onStartJob(JobParameters job)
new AsyncWork(this, job).execute();
return true;
@Override
public boolean onStopJob(JobParameters job)
return false;
我的异步任务
import android.os.AsyncTask;
import com.firebase.jobdispatcher.JobParameters;
import com.firebase.jobdispatcher.JobService;
class AsyncWork extends AsyncTask<Void, Void, Void>
private JobService jobService;
private JobParameters job;
AsyncWork(JobService jobService, JobParameters job)
this.jobService = jobService;
this.job = job;
@Override
protected Void doInBackground(Void... voids)
// some work that needs context
return null;
@Override
protected void onPostExecute(Void aVoid)
super.onPostExecute(aVoid);
// some work that needs context
jobService.jobFinished(job, false);
这会发出警告,表明 AsyncWork 类中的 jobService 属性正在泄漏上下文对象。我理解为什么如果你传递一个 Activity 或 Fragment 会出现这种情况,但这是一个 JobService,它应该存在,直到我调用 jobFinished()。我做错了什么还是可以忽略警告?
【问题讨论】:
你为什么首先使用AsyncTask
? AsyncTask
背后的要点是在后台工作完成后在主应用线程上做工作。 JobService
应该没有理由想要在主应用程序线程上做任何事情。因此,请使用普通的Thread
,或某种形式的Executor
。
JobService 被用于为我们的 appwidget 下载数据和图像并更新 appwidget。我一直认为出于某种原因需要在 UiThread 上使用新的 RemoteViews 更新 AppWidget。看来我的假设是不正确的,我确实不需要 AsyncTask。我明天要试试普通的Thread
。谢谢!
不,您可以从后台线程通过AppWidgetManager
更新应用小部件。您将需要一个Context
,但这可以是Application
单例,以免引入任何内存泄漏。 AsyncTask
通常是“即将推出”,但它只打算在主要 UI(活动、片段)中使用。
现在一切都变得更有意义了。名称RemoteViews
的“视图”部分总是欺骗我在工作线程上准备RemoteViews
并通过UiThread 上的AppWidgetManager
更新它。我才意识到这是一个错误,因为您质疑我使用AsyncTask
。我会使用你给出的建议。再次感谢!
【参考方案1】:
您不能忽略警告。因为AsyncWork
持有对Context
的引用,所以在任务完成之前无法对Context
进行GC:Context
内存泄漏。有两种解决方案:
-
使用长期存在的上下文,无论如何,应用程序上下文。
将异步任务的生命周期与它所引用的上下文的生命周期联系起来:在
onPause
中取消它
【讨论】:
1.我不能使用应用程序上下文,因为我需要调用 onPostExecuted() 中的 jobFinished() 函数来释放唤醒锁。 2. 据我所知,异步任务与 JobService 的生命周期相关,因为我在 JobService 的 onStartJob() 中返回 true,并在 onPostExecute() 结束时调用 jobFinished()。 JobService 中没有 onPause(),即使警告仍然存在,在 onStopJob() 中调用 cancel() 是否会 100% 防止泄漏? 在这种情况下,“泄漏”的意思是,Android 框架需要一个现在已过时的Activity
对象使用的内存,但由于 AsyncWork
持有一个引用,所以无法获取它。我不了解您的用例的详细信息,但这是您需要防止的。您还需要注意,一旦调用了onDestroy
,您作为上下文传递的Activity
将不再处于一致状态。
我注意到AsyncWork
没有返回任何内容。您是否考虑过将整个混乱放入IntentService
?
如果我的问题不清楚,我很抱歉。我了解Context
泄漏是如何从Activity
和Fragment
工作的,我的问题与从具有完全不同生命周期的JobService
调用AsyncTask
时的工作原理有关。 @CommonsWare 在 cmets 中指出,我首先不需要 AsyncTask
,这才是真正的问题所在。问题现已解决,非常感谢您的帮助!
什么。我完全遵从@CommonsWare。然而,AsyncTask
正是Executor
的使用。如果您将任务放在自制的Executor
上,并引用Activity
,您将遇到完全相同的问题。 ...我确实建议了IntentService
【参考方案2】:
要处理泄漏,您需要使用带有 JobService 对象的 WeakReference 类
class AsyncWork extends AsyncTask<Void, Void, Void>
private WeakReference<JobService> jobServiceWeakReference;
private JobParameters job;
AsyncWork(JobService jobService, JobParameters job)
this.jobServiceWeakReference = new WeakReference<>(jobService);
this.job = job;
@Override
protected Void doInBackground(Void... voids)
// some work that needs context
return null;
@Override
protected void onPostExecute(Void aVoid)
super.onPostExecute(aVoid);
// some work that needs context
jobServiceWeakReference.get().jobFinished(job, false);
【讨论】:
嗨,不应该从工作线程调用 jobFinished 吗?以上是关于如何防止 JobService 使用的 AsyncTask 中的上下文泄漏的主要内容,如果未能解决你的问题,请参考以下文章
当应用程序从后台删除时,如何停止 JobService Scheduled?