์ค๋์ ํ์ฌ์์ ๊ทผ๋ฌด์ค์ ๋ฐ๊ฒฌํ InputMethodManager์ ๊ด๋ จํ Memory Leak์ ๋ํด ํฌ์คํ ํ๋ คํฉ๋๋ค.
์์ฒด์ ์ผ๋ก ์ฑ์ ๋ํด ๋ฉ๋ชจ๋ฆฌ Leak๋ถ๋ถ์ด ์๋์ง ํ์ธ์ค dumpsys ํ์ธ๊ฒฐ๊ณผ GC๊ฐ ๋์๋ Activites์ View๊ฐ ๋ฆด๋ฆฌ์ฆ ๋์ง ์๋ ๋ฌธ์ ์ ์ด ๋ฐ๊ฒฌ๋์ด ์ฝ 1๋ฌ ๋์ ํด๋งธ์ต๋๋ค..
ํ์ฌ ์ ๊ฐ ๊ทผ๋ฌดํ๋ ํ์ฌ์ ์๋๋ก์ด๋ ๋ฒ์ ์ Android4.4(KitKat)๋ก ๋งค์ฐ ์๋ ๋ฒ์ ์ด๋ค๋ณด๋ Profiler๋ฅผ ์ฌ์ฉํ์ง ๋ชปํด ๋ ์๊ฐ์ด ๊ฑธ๋ ธ๋๊ฒ์ด๊ณ ์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ ์ฌ๋ฌ๋ถ๋ค์ ์ฝ๊ฒ ์ฐพ์ ์ ์์๊ฒ๋๋ค. (๋ณ๋ช ์ฃ์กํฉ๋๋ค.)
Profiler๋ฅผ ์ง์ํ์ง ์๋ ๋ฒ์ ์์ ์ฌ์ฉ ๋ฐฉ๋ฒ์ ๋ํด ์ดํ ์ถ๊ฐ์ ์ผ๋ก ํฌ์คํ ํ๋๋ก ํ๊ฒ ์ต๋๋ค.

Profiler ํ์ธ ๊ฒฐ๊ณผ ๋ฉ๋ชจ๋ฆฌ Leak์ด 2๊ฑด ๋ฐ์ ํ์๊ณ ๋ชจ๋ InputMethodManager์์ 'mCurRootView' , 'mServedView , mNextServedView'๋ฅผ ์ฐธ์กฐ ํ๊ณ ์์ด ๋ฐ์ํ๊ฒ์ผ๋ก ํ์ธ ๋์์ต๋๋ค.
์ ํ์์ด ๋ฐ์ํ๋ ์์ธ์ Fragment๋ RecyclerView๋ฑ์ ๊ณ์ธต ๊ตฌ์กฐ์ View๋ฅผ ์ฌ์ฉํ ๋ ์ข ๋ฃ ์ง์ Focus๋์ด ์๋ View์ ๋ํ ์ฐธ์กฐ๋ฅผ ํด์ ํ์ง ๋ชปํ๊ณ ์๋๋ก์ด๋ F/W์์ ์ก๊ณ ์์ด ๋ฐ์ํ๋๊ฒ์ด๋ฉฐ api level 15 ~ 23์์ ๋ํ๋๋ ์๋๋ก์ด๋ ์์ฒด ๋ฒ๊ทธ๋ผ๊ณ ํฉ๋๋ค..;
(๊ตฌ๊ธ์์๋ ์์ง ํด๋น ๋ฒ๊ทธ์ ๋ํด ์์ ํ์ง ๋ชปํ๋ค๋..)
๊ฒ์ ๊ฒฐ๊ณผ ๋์ค์ ์๊ฐ์ด ์ง๋ View๊ฐ ํ์ ์๋ค๊ณ ํ๋จ ๋ ๋ ์๋๋ก์ด๋ ์์ฒด์ ์ผ๋ก ๋ฆด๋ฆฌ์ฆ ์ํจ๋ค๊ณ ๋ ํ๋.. ์ ๋ ๋น์ฅ ํด๊ฒฐํ๊ณ ์ถ์์ต๋๋ค.

๊ทธ๋ ๊ฒ ๋ฐฉ๋ฒ์ ์ฐพ์๋ณด๋ 2๊ฐ์ง ์ ๋์ ๋ฐฉ๋ฒ์ด ์์์ต๋๋ค.
1. ์กํฐ๋นํฐ๊ฐ ์ข ๋ฃ๋ ๋ dummy activity๋ฅผ start์ํจํ ๋ฐ๋ก ์ข ๋ฃ ์์ผ focus๋ฅผ ๋ฆด๋ฆฌ์ฆ ์ํค๋ ๋ฐฉ๋ฒ
์ ๋ฐฉ๋ฒ์ ๋์์ ๊น๋นก๊ฑฐ๋ฆผ๋ฑ์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ๊ฒ์ผ๋ก ๋ณด์ฌ ์๋ํ์ง ์์์ต๋๋ค.
2. ์ข ๋ฃ ์์ (onDestroy)์ InputMethodManager ์๋น์ค๋ฅผ ์ง์ ๊ฐ์ ธ์ ์ instance๋ค์ ์ง์ ๋ฆด๋ฆฌ์ฆ ์ํค๋ ๋ฐฉ๋ฒ
์ ๋ ์ ๋ฐฉ๋ฒ์ ํํ์๊ณ ๊ฒฐ๋ก ์ ์ผ๋ก๋ dumpsys์ Profiler์ ๋ฆด๋ฆฌ์ฆ๊ฐ ๋์ง ์๋ ๋ถ๋ถ์ด ํด๊ฒฐ ๋๋ ๊ฒ์ ํ์ธ ํ์์ต๋๋ค.
๋ฐฉ๋ฒ์ ์๋ ์ฐธ๊ณ ๋ถํ๋๋ฆฝ๋๋ค.
public static void fixInputMethodMemoryLeak(Context context) {
if (context == null)
return;
//InputMethodManager ๊ฐ์ ธ์ค๊ธฐ
InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager == null)
return;
String[] viewArr = new String[]{"mCurRootView", "mServedView", "mNextServedView"};
Field field;
Object fieldObj;
for (String view: viewArr) {
try {
field = inputMethodManager.getClass().getDeclaredField(view);
if (!field.isAccessible()) {
field.setAccessible(true);
}
fieldObj = field.get(inputMethodManager);
if (fieldObj != null && fieldObj instanceof View) {
View fieldView = (View) fieldObj;
if (fieldView.getContext() == context) {
//istance null๋ก ๋ฆด๋ฆฌ์ฆ ์ํค๋ ๋์
field.set(inputMethodManager, null);
} else {
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
์์ ๊ฐ์ด ๋ฉ์๋๋ฅผ ํ๋ ์์ฑํ ํ
@Override
protected void onDestroy() {
fixInputMethodMemoryLeak(this);
super.onDestroy();
}
์๋์ ๊ฐ์ด ํธ์ถํ์ฌ ์ฌ์ฉํ์์ต๋๋ค. ๋ฐ๋์ super์ ์ ํธ์ถ ํ์ ์ผ ํฉ๋๋ค. super ์ดํ์ ํธ์ถ ์ ์ด๋ฏธ finish()๋์์ด ์ํ๋๊ธฐ์ ์ธ์คํด์ค๋ค์ ๋ฆด๋ฆฌ์ฆ ํ์ง ์๊ณ ์ฑ์ด ์ข ๋ฃ ๋ฉ๋๋ค..; (์ด๊ฑธ๋ก๋ ์กฐ๊ธ ๊ณ ์ํจ)
์ ์ ๊ฐ์ด Memry Leak์ ํด๊ฒฐ ํ ์ ์์ต๋๋ค.
'Android > Java' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Java][Android] Android ViewTreeObserVer onGlobalLayoutListener ํ์ฉํ๊ธฐ (0) | 2022.08.24 |
---|