谈谈Android内存优化
(一) adapter中保留对Activity的引用
if (mAdapter == null) {
mAdapter = new ChannelDetailAdapter(this, mDataList, mPageGridView);
mPageGridView.setAdapter(mAdapter);
} else {
mAdapter.setDataList(mDataList);
mPageGridView.notifyDataSetChanged(false);
mPageGridView.setSelectionPositionNotFocus(0);//重置 position
}
public class ChannelDetailAdapter extends BaseGridViewAdapter<AlbumModel> {
private Context mContext;
public ChannelDetailAdapter(Context mContext, List<AlbumModel> mDataList, PageGridView mPageGridView) {
super(mContext, mDataList, mPageGridView);
this.mContext = mContext;
}
}
(二) listener回调引起的泄露
(1)Activity中添加了回调监听,而没有把listener给干掉
this.mDataErrorView.setErrorListener(this);
(2) 使用Volley的回调导致的内存泄露
public Request(int method, String url, Response.ErrorListener listener) {
mMethod = method;
mUrl = url;
mIdentifier = createIdentifier(method, url);
mErrorListener = listener;
setRetryPolicy(new DefaultRetryPolicy());
mDefaultTrafficStatsTag = findDefaultTrafficStatsTag(url);
}
再看看Request的子类StringRequest的构造方法
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
/**
* 网络请求优化,取消请求
* @param tag
*/
public void cancelRequest(String tag){
try {
mRequestQueue.cancelAll(tag);
}catch (Exception e){
Logger.e("LeSportsApplication","tag =="+ tag + "的请求取消失败");
}
}
/**
* Cancels all requests in this queue with the given tag. Tag must be non-null
* and equality is by identity.
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}
最终调用的request的cancel方法,其实就是把mCaceled方法设为false,最终会触发请求将不会分发, 最后回调也就无效了
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.setTag(null);
request.cancel();
}
}
}
}
public void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
}
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
// If we finish marking off of the main thread, we need to
// actually do it on the main thread to ensure correct ordering.
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
} else {
long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;
if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {
VolleyLog.d("%d ms: %s", requestTime, this.toString());
}
}
mErrorListener = null;
}
重新Request子类的finish方法,Volley是没有重写这个方法,你要改下源码就好
public void finish(final String tag) {
super.finish(tag);
mListener = null;
}
总结:取消请求最终调用的方法是request的finish方法,只要在这个方法中把mErrorListener和mListener2个回调置为空,那么泄露问题就解决了。
(三) 使用Handler导致的泄露
(1) 用static 修饰handler,并用WeakRefrerence修饰Activity
private static class MsgHandler extends Handler {
private WeakReference<Activity> mActivity;
MsgHandler(Activity activity) {
mActivity = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
Activity activity = mActivity.get();
if (activity != null) {
activity.handleMessage(msg);
}
}
}
private Handler mHandler = new MsgHandler(this);
(2) 在onDestroy的方法中,把handler中运行的线程和消息全都
remove掉
@Override
public void onDestroy() {
super.onDestroy();
if(mHandler != null){
mHandler.removeCallbacks(mPreloadingRunnable);
mHandler.removeMessages(DERECTLY_SEARCH);
}
Logger.e(TAG, "onDestroy");
}
(四) 在单例类中持有Activity的引用
由于单例的生命周期是和Application保持一致的,当Activity被销毁的时候,而这个单例类并没有回收掉,也就是说Activity由于该单例类还持有对它的引用,导致Activity不能被回收掉。
public SingleInstance(Context contex){
mContext = context.getApplicationContext();
}
(五) 养成良好的编码习惯
虽然说Java内存回收线程会自动帮我们回收不需要的内存,但是这种回收的方式还是比较迟钝的。最好遵循谁用谁释放的原则,这样会更有利于内存的回收。我们new出一个对象的时候,一定要考虑在Activity或者fragment退出时把这些对象置为空,使这些对象的生命周期和Activity和Fragment的生命周期保持一致,这样就可以保证在Activity退出的时候,内存得到充分释放。
最近刚刚完成了一个TV项目,我们在开发中,程序经常会莫名的OOM,用MAT就发现,程序中有许多的内存泄露问题。解决了一部分的内存泄露问题,但是还是会经常OOM。网上找了一篇比较不错的内存优化博客,然后按照该文章实践,果然内存泄露导致的OOM问题,终于解决了。