2016-05-12 7 views
8

हैंडलर संदर्भ लीक चूंकि इस हैंडलर को आंतरिक कक्षा के रूप में घोषित किया जाता है, यह बाहरी वर्ग को कचरा इकट्ठा करने से रोक सकता है। यदि हैंडलर मुख्य धागे के अलावा किसी थ्रेड के लिए लूपर या संदेश क्यूई का उपयोग कर रहा है, तो कोई समस्या नहीं है। यदि हैंडलर मुख्य धागे के लूपर या संदेश क्यूई का उपयोग कर रहा है, तो आपको अपने हैंडलर घोषणा को ठीक करने की आवश्यकता है, निम्नानुसार: हैंडलर को स्थैतिक वर्ग के रूप में घोषित करें; बाहरी कक्षा में, बाहरी कक्षा में वीक रेफरेंस को तुरंत चालू करें और जब आप हैंडलर को तुरंत चालू करते हैं तो इस ऑब्जेक्ट को अपने हैंडलर पर पास करें; WeakReference ऑब्जेक्ट का उपयोग कर बाहरी वर्ग के सदस्यों के सभी संदर्भ बनाएं।यह हैंडलर वर्ग स्थिर होना चाहिए या लीक हो सकता है: AsyncQueryHandler

मुझे एक समाधान की आवश्यकता है !!!!!

public class EMG_Activity extends AppCompatActivity { 

    // An object that manages Messages in a Thread 
    public static Handler HandlerMessager; 
    private static AsyncQueryHandler queryHandler; 

    private static final String EMG = "EMG"; 
    private ManageConnectedSocket manageConnectedSocket; 
    private Thread manageThread; 
    private Button button_start_pause; 


    private int id = 0; 
    private int xValue = 0; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_emg); 

     queryHandler = new AsyncQueryHandler(getContentResolver()) { 

      @Override 
      protected void onInsertComplete(int token, Object cookie, Uri uri) { 
       if(cookie != null) 
        id = (int) ContentUris.parseId(uri); 
      } 
     }; 
       ........... 

     button_start_pause = (Button) findViewById(R.id.button_start_pause); 
     button_start_pause.setOnClickListener(new View.OnClickListener() { 

      @Override 
      public void onClick(View arg0) { 

       .................. 

       manageConnectedSocket = new ManageConnectedSocket(MainActivity.bluetoothDevice, MainActivity.samplingFrequency, new int[]{0}, new int[]{1, 0, 1, 1}); 
       manageThread = new Thread(manageConnectedSocket); 
       manageThread.start(); 

       ContentValues values = getContentValuesExam (EMG, AlsrmSchema.PROGRESS, Utils.getCurrentDate()); 
       queryHandler.startInsert(1, id, AlsrmContract.Exam.CONTENT_URI, values); 

       ...... 
      } 
     }); 
    } 

    @Override 
    public void onDestroy() { 
     super.onDestroy(); 

     ..... 

      ContentValues values = getContentValuesExam (EMG, AlsrmSchema.CORRUPTED, Utils.getCurrentDate()); 
      queryHandler.startUpdate(1, null, AlsrmContract.Exam.CONTENT_URI, values, AlsrmSchema.id + " = ? ", new String[]{"" + id}); 
     } 
    } 
} 

उत्तर

20

निम्नलिखित कोड पर विचार करें:

public class SampleActivity extends Activity { 

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

जबकि स्पष्ट नहीं, तो इस कोड एक बड़े पैमाने पर स्मृति रिसाव हो सकता है। एंड्रॉइड लिंट निम्नलिखित चेतावनी देगा:

In Android, Handler classes should be static or leaks might occur. 

लेकिन वास्तव में रिसाव कहां है और यह कैसे हो सकता है? का पहला दस्तावेजीकरण जो हम जानते द्वारा समस्या के स्रोत का पता लगाने दें:

  1. जब एक Android आवेदन पहले शुरू होता है, ढांचे के आवेदन के मुख्य थ्रेड के लिए एक Looper वस्तु बनाता है। एक लूपर एक साधारण संदेश कतार लागू करता है, एक संदेश के बाद एक लूप में संदेश ऑब्जेक्ट्स को संसाधित करता है। सभी प्रमुख अनुप्रयोग ढांचे की घटनाएं (जैसे कि गतिविधि जीवनचक्र विधि कॉल, बटन क्लिक इत्यादि) संदेश ऑब्जेक्ट्स के अंदर निहित हैं, जिन्हें लूपर की संदेश कतार में जोड़ा जाता है और एक-एक करके संसाधित होते हैं। मुख्य धागा का लूपर पूरे एप्लिकेशन के जीवन चक्र में मौजूद है।
  2. जब एक हैंडलर मुख्य धागे पर तत्काल होता है, तो यह लूपर की संदेश कतार से जुड़ा होता है। संदेश कतार में पोस्ट किए गए संदेश हैंडलर का संदर्भ रखेंगे ताकि ढांचा अंततः संदेश को संसाधित करते समय हैंडलर # हैंडल मैसेज (संदेश) को कॉल कर सके।
  3. जावा में, गैर स्थैतिक आंतरिक और अज्ञात कक्षाएं उनके बाहरी वर्ग के लिए एक निहित संदर्भ रखती हैं। दूसरी ओर, स्थिर आंतरिक कक्षाएं, नहीं।

तो मेमोरी लीक कहां है? यह बहुत ही सूक्ष्म है, लेकिन एक उदाहरण के रूप निम्नलिखित कोड पर विचार करें:

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(); 
    } 
} 

जब गतिविधि समाप्त हो गया है, देरी संदेश 10 मिनट के लिए मुख्य थ्रेड के संदेश कतार में रहने के लिए इससे पहले कि यह संसाधित किया जाता है जारी रहेगा। संदेश में गतिविधि के हैंडलर का संदर्भ है, और हैंडलर अपने बाहरी वर्ग (नमूनात्मकता, इस मामले में) के लिए एक अंतर्निहित संदर्भ रखता है। यह संदर्भ तब तक जारी रहेगा जब तक संदेश संसाधित नहीं हो जाता है, इस प्रकार गतिविधि संदर्भ को कचरा होने और सभी अनुप्रयोगों के संसाधनों को लीक करने से रोकता है। ध्यान दें कि लाइन 15 पर अज्ञात रननेबल क्लास के साथ भी यह सच है। अज्ञात वर्गों के गैर स्थैतिक उदाहरण उनके बाहरी वर्ग के लिए एक अंतर्निहित संदर्भ रखते हैं, इसलिए संदर्भ लीक हो जाएगा।

समस्या को ठीक करने के लिए, हैंडलर को एक नई फ़ाइल में उपclass या इसके बजाय एक स्थिर आंतरिक वर्ग का उपयोग करें। स्टेटिक आंतरिक कक्षाओं में उनके बाहरी वर्ग के लिए एक अंतर्निहित संदर्भ नहीं है, इसलिए गतिविधि को लीक नहीं किया जाएगा। यदि आपको हैंडलर के भीतर से बाहरी गतिविधि के तरीकों का आह्वान करने की आवश्यकता है, तो हैंडलर को गतिविधि में वीक रेफरेंस रखें ताकि आप गलती से संदर्भ को रिसाव न करें।स्मृति रिसाव ऐसा होता है जब हम गुमनाम Runnable वर्ग का दृष्टांत, हम चर वर्ग के एक स्थिर क्षेत्र बनाने के ठीक करने के लिए (के बाद से गुमनाम वर्गों के स्थिर उदाहरणों उनके बाहरी वर्ग के लिए एक अंतर्निहित संदर्भ पकड़ नहीं है):

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(); 
    } 
} 

स्थैतिक और गैर स्थैतिक आंतरिक कक्षाओं के बीच का अंतर सूक्ष्म है, लेकिन कुछ एंड्रॉइड डेवलपर को समझना चाहिए। नीचे की रेखा क्या है? किसी गतिविधि में गैर स्थैतिक आंतरिक कक्षाओं का उपयोग करने से बचें यदि आंतरिक वर्ग के उदाहरण गतिविधि के जीवन चक्र को पार कर सकते हैं। इसके बजाय, स्थिर आंतरिक कक्षाओं को प्राथमिकता दें और अंदर की गतिविधि के लिए एक कमजोर संदर्भ रखें।

+0

मेरी चेतावनी AsyncQueryHandler –

+0

यह भी AsyncQueryHandler –

+0

@shriduttkothari पर लागू होती है कॉलर क्लास से कमजोर संदर्भ को तुरंत सुरक्षित करना सुरक्षित नहीं है? 'निजी फाइनल माईहैंडलर mHandler = नया MyHandler (नया WeakReference <> (यह)) में बदल रहा है; ' – hannunehg

संबंधित मुद्दे