C.L.Wang
任职于春雨医生
修复内存泄露的经验总结
By C.L.Wang
使用v7.7.0预发版本
Commit: ba18201192f98cd9a63da30a1748eb35c89db36a
使用LeakCanary
版本:1.3.1
GitHub: https://github.com/square/leakcanary
误区:
(1)引入LeakCanary,项目的运行速度未受到较大影响
(2)使用LeakCanary检测到的错误都是存在的,不存在误报
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
}
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}
代码
// 位置: WelcomeActivity, @华仔
public class WelcomeActivity extends CYDoctorNetworkActivity40 {
StartPageInfoManager.getInstance().getRemoteData(WelcomeActivity.this, null);
}
// 位置: StartPageInfoManager
public class StartPageInfoManager extends DataManager<StartPageAdInfo> {
@Override
public void getRemoteData(final Context context, OnGetRemoteDataListener listener) {
getScheduler(context)
}
}
// 位置: DataManager @东哥
public abstract class DataManager<T> {
protected WebOperationScheduler getScheduler(Context context) {
if (sScheduler == null) {
// 修改context.getApplicationContext()
sScheduler = new WebOperationScheduler(context);
}
return sScheduler;
}
}
// 位置: WebOperationScheduler
public class WebOperationScheduler {
public WebOperationScheduler(Context context) {
this(context, 0);
}
public WebOperationScheduler(Context context, int timeout) {
super();
mContext = new WeakReference<Context>(context.getApplicationContext());
// !!!内存泄露
if (context instanceof FragmentActivity)
mFragmentActivity = (FragmentActivity) context;
mAsyncTaskIds = new ArrayList<Integer>();
}
}
Leak
代码
// 位置: UploadImageGridViewFragment @蛋蛋
public class UploadImageGridViewFragment {
protected ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService.setActivity(getActivity());
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
}
// 位置: UploadImageForStartAskService @蛋蛋
public class UploadImageForStartAskService extends Service {
// !!! 内存泄露
public void setActivity(Activity activity) {
this.mActivity = activity;
}
// 修改: 防止内存泄露, 添加位置, 根据逻辑判断, 或者重构代码
// public void removeReferences() {
// mActivity = null;
// mHandler = null;
// }
}
Leak
代码
// 位置: MainActivity600 @国信
public class MainActivity600 extends MainActivity2 {
// !!! 内存泄露
// 计时器TimerTask和Timer配合使用时, 造成内存泄露
private Timer mTimer = new Timer();
// 修改
protected void onDestroy() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
}
Leak
代码
// 位置: Initializer @东哥
public class Initializer {
public static void init(final Activity activity) {
initPicasso(activity);
}
private static void initPicasso(final Context context) {
// !!!内存泄露
// 修改context.getApplicationContext()
Picasso picasso = new Picasso.Builder(context)...
}
}
Leak
代码,这个泄露关系非常复杂,有多处不合理
// MainActivity 包含 NewsTabFragment 包含 NewsNormalFragment
// 位置: NewsNormalFragment @东哥
public class NewsNormalFragment extends RemoteDataList2Fragment {
@Override
protected G7BaseAdapter getListAdapter() {
// 注意, 此处使用的Activity, 表面NewsNormalViewHolder的引用是Activity.
// 应该修改G7BaseAdapter的Context引用
G7BaseAdapter adapter = new G7BaseAdapter(getActivity());
adapter.setHolderForObject(NewsNormalItem.class, NewsNormalViewHolder.class);
return adapter;
}
}
// 位置: NewsNormalViewHolder @东哥
public class NewsNormalViewHolder extends G7ViewHolder<NewsNormalItem> {
protected CYAutoScrollViewPager mVPHots; // 泄露位置
@Override
public void setData(Context context, NewsNormalItem data) {
if (data.mHots != null) {
// 注意此处使用的Activity
HotNewsAdapter adapter = new HotNewsAdapter(context);
...
for (int i = 0; i < data.mHots.size(); ++i) {
// !!!内存泄露, View持有Activity
View dot = new View(context);
...
mLLDots.addView(dot);
}
}
}
// @吴林
public void setCommandReceiver(String actionPrefix){
// !!!内存泄露, 持有View -> View持有Activity
LocalBroadcastManager.getInstance(getContext()).registerReceiver(new CommandReceiver(),filter);
}
class CommandReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
...
}
}
}
Leak
大家想听什么样的技术讲座
By C.L.Wang
Talk about how to find and solve memory leak in the app.