根据this文章,我发现在onCreate()方法上调用Retrofit enqueue()可能会导致内存泄漏。
这是这篇文章的内容,
在主线程中调用Retrofit
public class MoviesActivity extends Activity {
private TextView mNoOfMoviesThisWeek;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_movies_activity);
mNoOfMoviesThisWeek = (TextView) findViewById(R.id.no_of_movies_text_view);
MoviesRepository repository = ((MoviesApp) getApplication()).getRepository();
repository.getMoviesThisWeek()
.enqueue(new Callback<List<Movie>>() {
@Override
public void onResponse(Call<List<Movie>> call,
Response<List<Movie>> response) {
int numberOfMovies = response.body().size();
mNoOfMoviesThisWeek.setText("No of movies this week: " + String.valueOf(numberOfMovies));
}
@Override
public void onFailure(Call<List<Movie>> call, Throwable t) {
// Oops.
}
});
}
}
现在,如果此网络呼叫在非常慢的连接上运行,并且在呼叫结束之前,活动被某种程度地旋转或破坏,那么整个活动实例将被泄漏。
我尝试在我的应用上执行相同的操作。我在onCreate()方法中调用了大内容(240个对象)usign enqueue()。然后,当内容加载时,我多次旋转设备,LeakCanary向我展示了Activity中的内存泄漏,如文章所述。
然后,我尝试了两种方法来避免内存泄漏:
第一个选项
使用静态内部类在后台线程上调用Retrofit execute()方法。
在后台线程中调用改造
private static class RetrofitCall extends AsyncTask<Void, Void, List<Show>> {
private WeakReference<TextView> numberOfShows;
public RetrofitCall(TextView numberOfShows) {
this.numberOfShows = new WeakReference<>(numberOfShows);
}
@Override
protected List<Show> doInBackground(Void... voids) {
List<Show> showList = new ArrayList<>();
if (!isCancelled()) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(TvMazeService.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
TvMazeService service = retrofit.create(TvMazeService.class);
try {
Response<List<Show>> response = service.getShows().execute();
if (response.isSuccessful()) {
showList = response.body();
}
return showList;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPostExecute(List<Show> shows) {
super.onPostExecute(shows);
TextView textView = numberOfShows.get();
if (textView != null) {
String number = String.valueOf(shows.size());
textView.setText(number);
}
}
}
然后我尝试再次使用LeakCanary来解决内存泄漏问题,并且碰巧内存泄漏消失了。
第二个选项
使用ViewModel。
正如您在文档中所看到的那样,在使用ViewModel时,我在ViewModel类中调用了Retrofit异步,并且在旋转屏幕(活动被破坏)时,无需再次加载数据,因为它保持了保存状态。
这种方法也不会造成内存泄漏,是谈论内存时最好的方法。
问题
1)然后,使用ViewModel调用Retrofit是最好的选择,它真的可以避免内存泄漏吗?
2)是否可以像MoviesActivity一样在onCreate()中使用enqueue()调用改造?
3)在这种方法中,拨打电话进行用户身份验证的最佳方法是哪种?
答案 0 :(得分:1)
1)以正确的方式使用ViewModel不会导致内存泄漏,这是一个不错的选择。您可以看到google's video explanation,以及本讲座中有关difference between MVP and MVVM的内容。第二讲对这个话题给出了很好的解释。
2)在onCreate()中调用改造enqueue()是一个问题,它会导致内存泄漏。问题在于,第一次启动活动会称为改造,然后在旋转设备时,所有活动都会被销毁并重新创建。如果在数据加载完成之前旋转设备,则再次调用onCreate()时将第二次调用改造,如果继续执行10次,则改造将被调用10次,然后停止旋转设备。调用的结果将开始出现,bzzz :(结果将显示10次,因为您调用了10次。这意味着大量内存泄漏。如果实施此方法并使用LeakCanary,您将看到泄漏。
3)最佳方法是什么?
您可以在另一个question中关注我的讨论。在我们讨论同一主题但正在将用户登录到服务器的地方。
答案 1 :(得分:0)
如果在enqueue()
中调用onCreate()
会导致内存泄漏的原因是,排队的调用将保留对您的活动实例的引用,因为传递给它的回调实例(匿名类)是持有对封闭类实例的引用。只要您在onDestroy()
之前将其取消,就不会有问题。