上周做比分更新的时候,老是遭遇leakcanary在AIDL数据回调接口中报内存泄漏,看着好心烦
既然是内存泄漏,就补补Java内存管理的基础知识
Java的内存管理就是对对象的创建和释放,一般创建我们就这直接new, 由Java的垃圾回收器负责回收无用对象占据的内存资源
垃圾回收的机制 引用计数 引用计数是一种简单但速度很慢的垃圾回收技术,对每个对象都含有一个引用计数,当有引用连接到对象时,引用计数加1。当引用离开作用域或被置为null时,引用计数-1。垃圾回收器会检测程序所有对象的引用计数的变化,当引用计数为0时,证明该对象没有被引用,这时候垃圾回收器回收对象占据的内存资源,将不连续的内存空间,整理成连续的内存空间,但是引用计数不能解决循环引用,所以这种方式只是用于说明垃圾回收器的机制 (ps:还有好多技术)http://www.importnew.com/1993.html
###
内部类 非静态类内部会默认引用外部类,所以非handler如果是非静态内部就引用外部的context造成内部内存泄漏。
四种引用方式 强引用:如果一个对象具有强引用,那就 类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。 软引用:如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它; 弱引用:弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。 虚引用:如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。 可以看四种应用方式被回收的概率从无到大。
我的解决方式 有很多种UI模板Fragment定义一个抽象类,在抽象类中绑定服务,解除绑定,子类实现更新UI操作 创建一个继承Handler的静态内部类,静态SoftReference,静态UiUpdateHandler 这样保证与外部没有直接引用关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 public abstract class ScoreFragment extends Fragment { protected ICategoryUpdate mCategoryUpdate; protected static SoftReference<ScoreFragment> mReference; protected static UiUpdateHandler mUpdateUI; protected int refreshTime = 5000; protected ScoreGame mScoreGame; protected boolean isNoScore; protected Context mContext; protected String shareXML; protected static final String SCORE_GAME = "SCORE_GAME"; protected static final String CATEGORY_NEED = "CATEGORY_NEED"; /*************************************生命周期的方法********************************/ @Override public void onAttach(Context context) { super.onAttach(context); mContext = context; isNoScore = SharedPrefsUtil.getValue(mContext, APIConstant.SHOW_SCORE, false); shareXML = SharedPrefsUtil.getValue(mContext, APIConstant.HOME_SHARE, APIConstant.HOME_SHARE, null); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { mScoreGame = getArguments().getParcelable(SCORE_GAME); if (shareXML != null){ HomePage homePage = GsonInstance.getGsonInstance().fromJson(shareXML, HomePage.class); List<HomeCate> item = homePage.getCate().getItem(); for (HomeCate homeCate : item) { if (homeCate.getCateid().equals(mScoreGame.getGame().getCateid())){ refreshTime = homeCate.getRefreshtime2() * 1000; break; } } } mReference = new SoftReference<>((ScoreFragment) this); mUpdateUI = new UiUpdateHandler(mReference, mScoreGame.getGame().getId()+""); } } /** * 绑定服务 */ @Override public void onResume() { super.onResume(); bindToService(); } /** * 解除绑定 */ @Override public void onPause() { super.onPause(); try { if (mCategoryUpdate != null) { if (mCategoryUpdate.asBinder().isBinderAlive()) { getActivity().unbindService(mConnection); } } } catch (Exception e) { e.printStackTrace(); } } /*************************************生命周期的方法********************************/ /** * 更新头部比分 * @param byId */ public abstract void updateScore(CategoryUpdateList.ListBean byId); /** * 更新小比分 * @param scoreDetail * @param score2 */ public abstract void resetAll(CategoryUpdateList.ListBean.ScoreDetailBean scoreDetail, String score2); /** * 更新比分的handler */ protected static class UiUpdateHandler extends Handler { private SoftReference<ScoreFragment> mFragmentReference; private String mLiveId; UiUpdateHandler(SoftReference<ScoreFragment> fragmentReference, String cateId) { mFragmentReference = fragmentReference; mLiveId = cateId; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 0x1000){ ScoreFragment fragment = mFragmentReference.get(); CategoryUpdateList list = msg.getData().getParcelable("Update"); if (fragment != null && list != null&& list.getList() != null && list.getList().size() > 0) { CategoryUpdateList.ListBean byId = fragment.findById(list.getList(), mLiveId); DebugLogger.soutString(mLiveId); fragment.updateScore(byId); DebugLogger.soutString(fragment.toString()); if (byId!=null) fragment.resetAll(byId.getScoreDetail(), byId.getScore2()); } } } } protected static IDataCallback.Stub mCallback = new IDataCallback.Stub() { @Override public void onSuccess(CategoryUpdateList list) throws RemoteException { Message message = Message.obtain(); message.what = 0x1000; Bundle bundle = new Bundle(); bundle.putParcelable("Update", list); message.setData(bundle); mUpdateUI.sendMessage(message); } @Override public void onFailed(String msg) throws RemoteException { } }; /** * 连接服务端 */ protected ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mCategoryUpdate = ICategoryUpdate.Stub.asInterface(service); try { mCategoryUpdate.start(); mCategoryUpdate.setCallback(mCallback); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { try { mCategoryUpdate.clearCallback(mCallback); } catch (RemoteException e) { e.printStackTrace(); } } }; /** * 在指定的类别找到需要更新的id * @param list * @param id * @return */ public CategoryUpdateList.ListBean findById(List<CategoryUpdateList.ListBean> list ,String id){ for (int i = 0; i < list.size(); i++) { if (list.get(i).getLiveid().equals(id)){ return list.get(i); } } return null; } /** * 绑定服务的方法 */ private void bindToService() { Intent intent = new Intent(getActivity(), CategoryUpdateService.class); CategoryServiceNeed need = new CategoryServiceNeed(); need.setRefreashTime(refreshTime); need.setCateId(mScoreGame.getGame().getCateid()); intent.putExtra(CATEGORY_NEED, need); getActivity().bindService(intent, mConnection, Activity.BIND_AUTO_CREATE); } @Override public void onDestroy() { super.onDestroy(); mCategoryUpdate = null; } }
为什么使用软引用 因为我需要长期持有这个引用类,而不希望随时都可以回收,只有在内存不足的时候将其回收,所以采用了软引用,之前采用的弱引用,大概更新两三次过后,弱引用就被回收,UI无法更新,所以采用软引用的方式