এই হ্যান্ডলারের শ্রেণিটি স্থির হওয়া উচিত বা ফাঁস হতে পারে: ইনকামিংহ্যান্ডলার


297

আমি একটি পরিষেবা সহ একটি Android 2.3.3 অ্যাপ্লিকেশন বিকাশ করছি। মূল ক্রিয়াকলাপের সাথে যোগাযোগ করার জন্য আমার এই পরিষেবাটির ভিতরে রয়েছে:

public class UDPListenerService extends Service
{
    private static final String TAG = "UDPListenerService";
    //private ThreadGroup myThreads = new ThreadGroup("UDPListenerServiceWorker");
    private UDPListenerThread myThread;
    /**
     * Handler to communicate from WorkerThread to service.
     */
    private Handler mServiceHandler;

    // Used to receive messages from the Activity
    final Messenger inMessenger = new Messenger(new IncomingHandler());
    // Use to send message to the Activity
    private Messenger outMessenger;

    class IncomingHandler extends Handler
    {
        @Override
        public void handleMessage(Message msg)
        {
        }
    }

    /**
     * Target we publish for clients to send messages to Incoming Handler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());
    [ ... ]
}

এবং এখানে, final Messenger mMessenger = new Messenger(new IncomingHandler());আমি নিম্নলিখিত লিন্ট সতর্কতা পেয়েছি:

This Handler class should be static or leaks might occur: IncomingHandler

এর মানে কী?


24
এই বিষয়ে আরও অনেক তথ্যের জন্য এই ব্লগ পোস্টটি দেখুন!
অ্যাড্রিয়ান সন্ন্যাস

1
ময়লা আবর্জনা সংগ্রহের কারণে মেমরি ফাঁস ...
জাভাটি

উত্তর:


391

যদি IncomingHandler শ্রেণি স্থির না হয় তবে এটিতে আপনার Serviceঅবজেক্টের একটি উল্লেখ থাকবে ।

Handler একই থ্রেডের জন্য অবজেক্টস সমস্ত একটি সাধারণ লুপার অবজেক্ট ভাগ করে, যা তারা বার্তা পোস্ট করে এবং থেকে পড়ে।

বার্তাগুলি লক্ষ্য হিসাবে Handler হিসাবে, যতক্ষণ বার্তা কাতারে টার্গেট হ্যান্ডলার সহ বার্তা রয়েছে, হ্যান্ডলারটি আবর্জনা সংগ্রহ করা যাবে না। যদি হ্যান্ডলার স্থিতিশীল না হয় তবে ধ্বংস হয়ে যাওয়ার পরেও আপনার Serviceবা Activityআবর্জনা সংগ্রহ করা যাবে না।

এটি মেমরি ফাঁস হতে পারে, কিছু সময়ের জন্য কমপক্ষে - যতক্ষণ বার্তাগুলি সারিতে থাকবে না। আপনি দীর্ঘ বিলম্বিত বার্তাগুলি পোস্ট না করা পর্যন্ত এটি কোনও ইস্যু নয়।

আপনি IncomingHandlerঅচল করতে পারেন এবং WeakReferenceআপনার পরিষেবাতে একটি পেতে পারেন :

static class IncomingHandler extends Handler {
    private final WeakReference<UDPListenerService> mService; 

    IncomingHandler(UDPListenerService service) {
        mService = new WeakReference<UDPListenerService>(service);
    }
    @Override
    public void handleMessage(Message msg)
    {
         UDPListenerService service = mService.get();
         if (service != null) {
              service.handleMessage(msg);
         }
    }
}

আরও রেফারেন্সের জন্য রোমেন গাইয়ের এই পোস্টটি দেখুন


3
রোমেন দেখায় যে বাইরের শ্রেণীর প্রতি WeakReferences প্রয়োজনীয় - এটি একটি স্ট্যাটিক নেস্টেড শ্রেণীর প্রয়োজন নেই। আমার মনে হয় আমি WeakReferences পদ্ধতির পছন্দ করব কারণ অন্যথায় পুরো বাহ্যিক শ্রেণি আমার প্রয়োজন সমস্ত 'স্ট্যাটিক' ভেরিয়েবলের কারণে মারাত্মকভাবে পরিবর্তিত হয়।
কেউ কেউ কোথাও

35
আপনি যদি নেস্টেড ক্লাস ব্যবহার করতে চান তবে এটি স্থির থাকতে হবে। অন্যথায়, WeakReferences কিছুই পরিবর্তন করে না। অভ্যন্তরীণ (নেস্টেড তবে স্ট্যাটিক নয়) শ্রেণি সর্বদা বহিরাগত শ্রেণির দৃ strong় রেফারেন্স রাখে। যদিও কোনও স্ট্যাটিক ভেরিয়েবলের প্রয়োজন নেই।
টমাসজ নিডাবাইলস্কি

2
@ সুমোনসোমহর এম সেরিভেস একটি দুর্বল রেফারেন্স। get()রেফারেন্স করা বস্তুটি সিসি-এড হলে নাল ফিরে আসবে। এই ক্ষেত্রে, যখন পরিষেবাটি মারা যায়।
টমাসজ নিডাবাইলস্কি

1
দ্রষ্টব্য: ইনকামিংহ্যান্ডলার স্থির করার পরে, আমি ত্রুটি পেয়ে যাচ্ছিলাম "কনস্ট্রাক্টর মাইঅ্যাক্টিভিটি nc ইনকামিংহ্যান্ডলার () অপরিজ্ঞাত।" "চূড়ান্ত মেসেঞ্জার ইনম্যাসেঞ্জার = নতুন ম্যাসেঞ্জার (নতুন ইনকামিংহ্যান্ডলার ());" লাইনে। সমাধানটি হ'ল এই লাইনটি "চূড়ান্ত মেসেঞ্জার ইন ম্যাসেঞ্জার = নতুন ম্যাসেঞ্জার (নতুন ইনকামিংহ্যান্ডলার (এটি))" পরিবর্তন করা;
ল্যান্স লেফবুর

4
@ কোথাও কোথাও হ্যাঁ, রোমেনের পোষ্টটি ভুল যে এতে তিনি অভ্যন্তরীণ শ্রেণীর স্থির ঘোষণাটি মিস করেন যা পুরো পয়েন্টটি মিস করে। যদি তার কাছে এমন কিছু অতি শীতল সংকলক না থাকে যা শ্রেণি ভেরিয়েবলগুলি ব্যবহার না করে তবে স্বয়ংক্রিয়ভাবে অভ্যন্তরীণ ক্লাসগুলিকে স্ট্যাটিক শ্রেণিতে রূপান্তর করে।
সোগগার

67

অন্যরা যেমন লিঙ্ক সতর্কতা উল্লেখ করেছে সম্ভাব্য মেমরি ফুটো হওয়ার কারণে। আপনি Handler.Callbackনির্মাণের সময় পাস করে লিন্টের সতর্কতা এড়াতে পারবেন Handler(যেমন আপনি সাবক্লাস করেন না Handlerএবং কোনও Handlerস্থিতিশীল অভ্যন্তর শ্রেণি নেই):

Handler mIncomingHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // todo
        return true;
    }
});

আমি যেমন এটি বুঝতে পারি, এটি সম্ভাব্য মেমরির ফাঁস এড়াবে না। Messageঅবজেক্টস অবজেক্টের রেফারেন্স ধরে mIncomingHandlerরাখে যা রেফারেন্স ধারণ করে Handler.Callbackঅবজেক্টটি যা অবজেক্টের রেফারেন্স রাখে ServiceLooperমেসেজের কাতারে যতক্ষণ বার্তা থাকবে ততক্ষণ Serviceজিসি হবে না। তবে আপনার বার্তার সারিটিতে দীর্ঘক্ষণ বিলম্ব হওয়া বার্তা না থাকলে এটি কোনও গুরুতর সমস্যা হবে না।


10
@ ব্রজ আমি লিন্টের সতর্কতা এড়ানো মনে করি না তবে তবুও ত্রুটিটি রাখা মোটেও ভাল সমাধান। যতক্ষণ না, লিন্টের সতর্কতা হিসাবে বলা হয়েছে যে যদি হ্যান্ডলারটি আপনার প্রধান লুপারে না রাখা হয় (এবং আপনি নিশ্চিত করতে পারেন যে এতে থাকা সমস্ত বার্তাগুলি শ্রেণিবদ্ধ হয়ে যাওয়ার পরে ধ্বংস হয়ে যায়) তবে রেফারেন্স ফাঁস হ্রাস করা হবে।
সোগগার

33

সমস্যাটি সমাধানের জন্য দুর্বল রেফারেন্স এবং স্ট্যাটিক হ্যান্ডলার শ্রেণীর ব্যবহারের জেনেরিক উদাহরণ এখানে রয়েছে (লিন্ট ডকুমেন্টেশনে প্রস্তাবিত হিসাবে):

public class MyClass{

  //static inner class doesn't hold an implicit reference to the outer class
  private static class MyHandler extends Handler {
    //Using a weak reference means you won't prevent garbage collection
    private final WeakReference<MyClass> myClassWeakReference; 

    public MyHandler(MyClass myClassInstance) {
      myClassWeakReference = new WeakReference<MyClass>(myClassInstance);
    }

    @Override
    public void handleMessage(Message msg) {
      MyClass myClass = myClassWeakReference.get();
      if (myClass != null) {
        ...do work here...
      }
    }
  }

  /**
   * An example getter to provide it to some external class
   * or just use 'new MyHandler(this)' if you are using it internally.
   * If you only use it internally you might even want it as final member:
   * private final MyHandler mHandler = new MyHandler(this);
   */
  public Handler getHandler() {
    return new MyHandler(this);
  }
}

2
Sogger উদাহরণ দুর্দান্ত। যাইহোক, শেষ পদ্ধতিটি পরিবর্তে Myclassহিসাবে ঘোষণা করা উচিতpublic Handler getHandler()public void
জেসন পোর্টার

এটি টমাসজ নিডাবাইলস্কির উত্তরের অনুরূপ
কুলমাইন্ড

24

এইভাবে আমার পক্ষে ভাল কাজ করেছে, আপনি নিজের অভ্যন্তর শ্রেণিতে বার্তাটি যেখানে পরিচালনা করছেন তা রেখে কোড পরিষ্কার রাখে।

আপনি যে হ্যান্ডলারটি ব্যবহার করতে ইচ্ছুক

Handler mIncomingHandler = new Handler(new IncomingHandlerCallback());

অন্তর্গত শ্রেণি

class IncomingHandlerCallback implements Handler.Callback{

        @Override
        public boolean handleMessage(Message message) {

            // Handle message code

            return true;
        }
}

2
এখানে হ্যান্ডেলমেজেজ পদ্ধতিটি শেষে ফিরে আসে। আপনি দয়া করে ব্যাখ্যা করতে পারবেন এর ঠিক কী অর্থ (ফেরতের মানটি সত্য / মিথ্যা)? ধন্যবাদ।
JibW

2
সত্য প্রত্যাবর্তনের বিষয়ে আমার বোঝাটি হ'ল আপনি বার্তাটি পরিচালনা করেছেন এবং তাই বার্তাটি অন্য কোথাও যেমন পাস করা উচিত নয় যেমন অন্তর্নিহিত হ্যান্ডলারটি। এটি বলেছিল যে আমি কোনও নথিপত্র পাইনি এবং আনন্দের সাথে সংশোধন করব।
স্টুয়ার্ট ক্যাম্পবেল

1
জাভাডোক বলেছেন: কনস্ট্রাক্টর এই হ্যান্ডলারটি বর্তমান থ্রেডের জন্য লুপারের সাথে সংযুক্ত করে এবং একটি কলব্যাক ইন্টারফেস নেয় যাতে আপনি বার্তাগুলি পরিচালনা করতে পারেন। যদি এই থ্রেডটিতে লুপার না থাকে তবে এই হ্যান্ডলারটি কোনও ব্যতিক্রম নিক্ষেপ করা হওয়ায় বার্তাগুলি গ্রহণ করতে সক্ষম হবে না। <- আমি মনে করি যখন থ্রেডের সাথে কোনও লুপার সংযুক্ত নেই তখন নতুন হ্যান্ডলার (নতুন ইনকামিংহ্যান্ডলারক্যালব্যাক ()) কাজ করবে না এবং এটি হতে পারে be আমি কিছু ক্ষেত্রে এটি করার পক্ষে এর ভুল বলছি না, আমি কেবল এটি বলছি যা আপনি আশা করেন তেমন কাজ করে না।
ব্যবহারকারী 504342

1
@ স্টুয়ার্ট ক্যাম্পবেল: আপনি সঠিক বলেছেন। দেখুন: groups.google.com/forum/#!topic/android-developers/L_xYM0yS6z8
MDTech.us_MAN

2

@ সোগারের উত্তরের সাহায্যে, আমি একটি জেনেরিক হ্যান্ডলার তৈরি করেছি:

public class MainThreadHandler<T extends MessageHandler> extends Handler {

    private final WeakReference<T> mInstance;

    public MainThreadHandler(T clazz) {
        // Remove the following line to use the current thread.
        super(Looper.getMainLooper());
        mInstance = new WeakReference<>(clazz);
    }

    @Override
    public void handleMessage(Message msg) {
        T clazz = mInstance.get();
        if (clazz != null) {
            clazz.handleMessage(msg);
        }
    }
}

ইন্টারফেস:

public interface MessageHandler {

    void handleMessage(Message msg);

}

আমি নিম্নলিখিত হিসাবে এটি ব্যবহার করছি। তবে আমি এটি 100% নিশ্চিত নই যদি এটি ফাঁস-নিরাপদ হয়। হতে পারে কেউ এই সম্পর্কে মন্তব্য করতে পারে:

public class MyClass implements MessageHandler {

    private static final int DO_IT_MSG = 123;

    private MainThreadHandler<MyClass> mHandler = new MainThreadHandler<>(this);

    private void start() {
        // Do it in 5 seconds.
        mHandler.sendEmptyMessageDelayed(DO_IT_MSG, 5 * 1000);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case DO_IT_MSG:
                doIt();
                break;
        }
    }

    ...

}

0

আমি নিশ্চিত নই তবে আপনি অন্টস্ট্রাইয়ে হ্যান্ডলারটি বাতিল করতে চেষ্টা করতে পারেন ()


1
একই থ্রেডের জন্য হ্যান্ডলার অবজেক্টসগুলি একটি সাধারণ লুপার অবজেক্ট ভাগ করে, যা তারা বার্তা পোস্ট করে এবং থেকে পড়ে। বার্তাগুলি লক্ষ্য হ্যান্ডলারকে ধারণ করে, যতক্ষণ বার্তা সারিটিতে লক্ষ্য হ্যান্ডলার সহ বার্তা রয়েছে, হ্যান্ডলারটি আবর্জনা সংগ্রহ করা যাবে না।
মিসেসমিলু

0

আমি বিভ্রান্ত আমি যে উদাহরণটি পেয়েছি তা স্থির সম্পত্তি পুরোপুরি এড়িয়ে যায় এবং ইউআই থ্রেড ব্যবহার করে:

    public class example extends Activity {
        final int HANDLE_FIX_SCREEN = 1000;
        public Handler DBthreadHandler = new Handler(Looper.getMainLooper()){
            @Override
            public void handleMessage(Message msg) {
                int imsg;
                imsg = msg.what;
                if (imsg == HANDLE_FIX_SCREEN) {
                    doSomething();
                }
            }
        };
    }

এই সমাধানটি সম্পর্কে আমি যে জিনিসটি পছন্দ করি তা হ'ল শ্রেণি এবং পদ্ধতি ভেরিয়েবলগুলি মিশ্রিত করার চেষ্টা করার ক্ষেত্রে কোনও সমস্যা নেই।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.