এন্ড্রয়েড মেমোরি লিক

Md Ali Hossain
3 min readAug 9, 2018

--

এন্ড্রয়েড এ মেমোরি লিক একটি কমন সমস্যা। আমরা সাধারানত এটা নিয়ে খুব বেশি চিন্তা করি না। কিন্তু আসলে এটা অ্যাপ্লিকেশান এর performance এ অনেক প্রভাব ফেলে। একজন এন্ড্রয়েড ডেভেলপার এর মেমোরি ম্যানেজমেন্ট এর উপর দক্ষতা থাকতে হবে।

যখন একটি অ্যানড্রয়েড অ্যাপ্লিকেশন প্রথম চালু হয়, অ্যাপ্লিকেশনটির প্রধান থ্রেডের জন্য একটি Looper অবজেক্ট তৈরি করে। Looper একটি MessageQueue ইম্প্লিমেন্ট করে, যে একটি লুপ এর ভিতরে মেসেজ অবজেক্ট গুলো একটার পর আরেকটা প্রসেস করে। মূল থ্রেড এর Looper অ্যাপ্লিকেশন এর প্রথম থেকে শেষ পর্যন্ত চলতে থাকে।

যখন একটি Handler প্রধান থ্রেডে এ চালু হয় এবং Looper এর MessageQueue সাথে যুক্ত হয়। MessageQueue তে পোস্ট করা মেসেজ গুলো হ্যান্ডলারের কাছে একটি রেফারেন্স রাখে যাতে করে ফ্রেমওয়ার্ক Handler#handleMessage(Message)কল করতে পারে যখন Looper শেষ পর্যন্ত মেসেজ প্রসেস করতে পারে।

জাভাতে, নন-স্ট্যাটিক ইনার ক্লাস এবং এননিমাস ক্লাসগুলি তাদের বাইরের ক্লাস এর একটি রেফারেন্স ধরে রাখে। স্ট্যাটিক ইনার ক্লাস যেটি রাখে না। সুতরাং মেমোরি লিক টি কোথায়?

নিচের এক্সাম্পল তা ভাল করে দেখুনঃ

public class SampleActivity extends Activity {

private final Handler mLeakyHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Post a message and delay its execution for 10 minutes.
mLeakyHandler.postDelayed(new Runnable() {
@Override
public void run() { /* ... */ }
}, 1000 * 60 * 10);

// Go back to the previous Activity.
finish();
}
}

এখানে Activity টা শেষ হলেও, Message টি প্রধান থ্রেড এর MessageQueue তে 10 মিনিটের জন্য চলতে থাকবে যতক্ষণ না এটা প্রসেস হয়। Mesage টি Activity এর Hanlder এর রেফারেন্স ধরে রাখে এবং Hanlder টি তার বাইরের ক্লাস এর রেফারেন্স ধারণ করে (এক্ষেত্রে SampleActivity)। Message টি প্রসেস না হওয়া পর্যন্ত এই রেফারেন্সটি থাকবে, যে কারনে Activity context টি Garbage Collection এ বাধা দেয় এবং অ্যাপ্লিকেশন এর রিসোর্সে লিক করা থেকে বিরত রাখে।
উল্লেখ্য, নন-স্ট্যাটিক ইনার ক্লাস এবং এননিমাস ক্লাসগুলি তাদের বাইরের ক্লাস এর একটি রেফারেন্স ধরে রাখে। তাই এখানে Context লিক হবে।

সমস্যাটি ঠিক করার জন্য, হ্যান্ডলারকে একটি নতুন সাবক্লাস অথবা একটি স্ট্যাটিক ইনার ক্লাস ব্যবহার করতে হবে। স্ট্যাটিক ইনার ক্লাস তাদের বাইরের ক্লাস এর রেফারেন্স ধরে রাখে না, তাই activity লিক হবে না। যদি আপনি হ্যান্ডলারের বাইরের activity এর মেথডগুলি ব্যবহার করতে চান, তাহলে হ্যান্ডলারটির জন্য একটি WeakReference ব্যবহার করতে হবে যাতে Context লিক না হয়। আমরা যখন এননিমাস Runnable ক্লাস instantiate করি তখন ক্লাস টি কে স্ট্যাটিক করে দিতে হবে তাহলে মেমোরি লিক এর সমস্যাটি থাকবে না।

এখন মেমোরি লিক হ্যান্ডল করা এক্সাম্পল টি দেখুনঃ

public class SampleActivity extends Activity {  /**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/

private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}
private final MyHandler mHandler = new MyHandler(this); /**
* Instances of anonymous classes do not hold an implicit
* reference to their outer class when they are "static".
*/

private static final Runnable sRunnable = new Runnable() {
@Override
public void run() { /* ... */ }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Post a message and delay its execution for 10 minutes.
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

// Go back to the previous Activity.
finish();
}
}

আশা করি মেমোরি লিক কেন হয় এবং কিভাবে সেটা হ্যান্ডল করতে হয় কিছুটা হলেও একটা ধারনা পাওয়া গিয়েছে।
বুজতে কোন অসুবিধা হলে কমেন্ট এ জানান।

--

--

Md Ali Hossain

Senior Android Engineer @Delivery Hero | Android developer | Kotlin lover | Flutter explorer | Problem Solver