हर जगह एप्लिकेशन संदर्भ का उपयोग करना?


476

एंड्रॉइड ऐप में, निम्नलिखित दृष्टिकोण के साथ कुछ गलत है:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

और इसे हर जगह से पास करें (जैसे SQLiteOpenHelper) जहां संदर्भ की आवश्यकता होती है (और निश्चित रूप से लीक नहीं)?


23
इसे लागू करने वाले अन्य लोगों के लिए विस्तृत करने के लिए, आप <application>निम्नलिखित विशेषता को शामिल करने के लिए अपनी AndroidManifest.xml फ़ाइल के नोड को संशोधित कर सकते हैं android:name="MyApp"। MyApp को उसी पैकेज के तहत होना चाहिए, जो आपके मैनिफ़ेस्ट को संदर्भित करता है।
मैट हगिंस

6
SQLiteOpenHelper के संदर्भ में आपूर्ति करने की समस्या को हल करने का अद्भुत तरीका !! मैंने एक सिंगलटन "SQLiteManager" को लागू किया है और "मैं F को सिंगलटन के संदर्भ में कैसे प्राप्त करूं?" पर अटक गया था।
किसी

8
बस आप जानते हैं कि आप इसके सुपर इंटरफेस में से एक द्वारा अपना आवेदन लौटा रहे हैं, इसलिए यदि आप MyApp के भीतर अतिरिक्त तरीके प्रदान करते हैं तो आप उनका उपयोग नहीं कर पाएंगे। आपके getContext () को बदले में MyApp का रिटर्न प्रकार होना चाहिए, और इस तरह आप बाद में जोड़े गए तरीकों का उपयोग कर सकते हैं, साथ ही ContextWrapper और Context के सभी तरीकों का भी उपयोग कर सकते हैं।

5
यह भी देखें goo.gl/uKcFn - यह समान पोस्ट से संबंधित एक और उत्तर है। बेहतर स्थिर चर onCreate में सेट करें और न करें।
एलिकएल्ज़िन-किलक

1
@ChuongPham अगर फ्रेमवर्क ने आपके ऐप को मार दिया है, तो अशक्त संदर्भ तक पहुँचने के लिए कुछ भी नहीं होगा ...
केविन क्रुमविडे

जवाबों:


413

इस दृष्टिकोण के साथ कुछ संभावित समस्याएं हैं, हालांकि बहुत सारी परिस्थितियों में (जैसे कि आपका उदाहरण) यह अच्छी तरह से काम करेगा।

विशेष रूप से आपको सावधान रहना चाहिए जब किसी भी चीज़ से निपटना हो, जिसके लिए सौदा करना GUIआवश्यक है Context। उदाहरण के लिए, यदि आप आवेदन को पास करते हैं तो LayoutInflaterआप एक अपवाद प्राप्त करेंगे। आम तौर पर बोलना, आपका दृष्टिकोण उत्कृष्ट है: यह एक अच्छा प्रयोग Activity's Contextहै कि भीतर का उपयोग करें Activity, और Application Contextजब स्मृति प्रसंग से बचने केActivity लिए एक संदर्भ के दायरे से परे एक संदर्भ गुजर रहा है ।

इसके अलावा, अपने पैटर्न के विकल्प के रूप में आप getApplicationContext()एक Contextवस्तु पर कॉल करने के शॉर्टकट का उपयोग कर सकते हैं (जैसे कि एक गतिविधि) अनुप्रयोग संदर्भ प्राप्त करने के लिए।


22
एक प्रेरणादायक उत्तर के लिए धन्यवाद। मुझे लगता है कि मैं इस दृष्टिकोण को दृढ़ता की परत के लिए उपयोग करूँगा (जैसा कि मैं सामग्री प्रदाताओं के साथ नहीं जाना चाहता)। आश्चर्य है कि SQLiteOpenHelper को इस तरह से डिजाइन करने के पीछे क्या प्रेरणा थी, जो उम्मीद करता है कि इसे एप्लिकेशन से स्वयं प्राप्त करने के बजाय एक प्रसंग की आपूर्ति की जाएगी। पुनश्च और आपकी पुस्तक महान है!
यान्चेन

7
LayoutInflatorसिर्फ मेरे लिए काम के साथ आवेदन संदर्भ का उपयोग करना । पिछले तीन वर्षों में बदल दिया गया होगा।
जैकब फिलिप्स

5
@JacobPhillips एक गतिविधि के संदर्भ के बिना LayoutInflator का उपयोग करना उस गतिविधि की स्टाइल को याद करेगा। तो यह एक अर्थ में काम करेगा, लेकिन दूसरे में नहीं।
मार्क

1
@MarkCarter क्या आप जानते हैं कि एप्लिकेशन का उपयोग करने से गतिविधि की स्टाइल छूट जाएगी?
जैकब फिलिप्स

1
@JacobPhillips हाँ, एप्लिकेशन संदर्भ स्टाइल नहीं हो सकता क्योंकि हर गतिविधि को एक अलग तरीके से स्टाइल किया जा सकता है।
मार्क

28

मेरे अनुभव में यह दृष्टिकोण आवश्यक नहीं होना चाहिए। आप कुछ भी करने के लिए संदर्भ की जरूरत है तो आप आमतौर पर करने के लिए एक कॉल के माध्यम से प्राप्त कर सकते हैं () View.getContext और का उपयोग कर Contextवहाँ प्राप्त आप कॉल कर सकते हैं Context.getApplicationContext () प्राप्त करने के लिए Applicationसंदर्भ। आप प्राप्त करने के लिए कोशिश कर रहे हैं Applicationएक से संदर्भ यह Activityआप हमेशा कॉल कर सकते हैं Activity.getApplication () जो के रूप में पारित किया जाना में सक्षम होना चाहिए Contextके लिए एक कॉल के लिए आवश्यक SQLiteOpenHelper()

कुल मिलाकर इस स्थिति के लिए आपके दृष्टिकोण में कोई समस्या नहीं है, लेकिन जब Contextयह सुनिश्चित करें कि आप आधिकारिक Google Android डेवलपर्स ब्लॉग पर वर्णित के अनुसार कहीं भी मेमोरी लीक नहीं कर रहे हैं ।


13

कुछ लोगों ने पूछा है: सिंगलटन एक अशक्त सूचक को कैसे लौटा सकता है? मैं उस सवाल का जवाब दे रहा हूं। (मैं एक टिप्पणी में जवाब नहीं दे सकता क्योंकि मुझे कोड पोस्ट करने की आवश्यकता है।)

यह दो घटनाओं के बीच में अशक्त हो सकता है: (1) कक्षा भरी हुई है, और (2) इस वर्ग का उद्देश्य बनाया गया है। यहाँ एक उदाहरण है:

class X {
    static X xinstance;
    static Y yinstance = Y.yinstance;
    X() {xinstance=this;}
}
class Y {
    static X xinstance = X.xinstance;
    static Y yinstance;
    Y() {yinstance=this;}
}

public class A {
    public static void main(String[] p) {
    X x = new X();
    Y y = new Y();
    System.out.println("x:"+X.xinstance+" y:"+Y.yinstance);
    System.out.println("x:"+Y.xinstance+" y:"+X.yinstance);
    }
}

चलो कोड चलाते हैं:

$ javac A.java 
$ java A
x:X@a63599 y:Y@9036e
x:null y:null

दूसरी पंक्ति से पता चलता है कि Y.xinstance और X.yinstance हैं अशक्त ; वे शून्य हैं क्योंकि चर X.xinstance ans Y.yinstance को तब पढ़ा गया था जब वे शून्य थे।

क्या इसे ठीक किया जा सकता है? हाँ,

class X {
    static Y y = Y.getInstance();
    static X theinstance;
    static X getInstance() {if(theinstance==null) {theinstance = new X();} return theinstance;}
}
class Y {
    static X x = X.getInstance();
    static Y theinstance;
    static Y getInstance() {if(theinstance==null) {theinstance = new Y();} return theinstance;}
}

public class A {
    public static void main(String[] p) {
    System.out.println("x:"+X.getInstance()+" y:"+Y.getInstance());
    System.out.println("x:"+Y.x+" y:"+X.y);
    }
}

और यह कोड कोई विसंगति नहीं दिखाता है:

$ javac A.java 
$ java A
x:X@1c059f6 y:Y@152506e
x:X@1c059f6 y:Y@152506e

लेकिन यह एंड्रॉइड Applicationऑब्जेक्ट के लिए एक विकल्प नहीं है : प्रोग्रामर उस समय को नियंत्रित नहीं करता है जब इसे बनाया जाता है।

एक बार फिर: पहले उदाहरण और दूसरे के बीच का अंतर यह है कि दूसरा उदाहरण एक उदाहरण बनाता है यदि स्थिर सूचक शून्य है। लेकिन एक प्रोग्रामर नहीं बना सकते एंड्रॉयड आवेदन वस्तु से पहले प्रणाली यह करने के लिए फैसला किया।

अपडेट करें

एक और अधिक चौंकाने वाला उदाहरण जहां प्रारंभिक स्थिर क्षेत्र होते हैं null

मुख्य.जावा :

enum MyEnum {
    FIRST,SECOND;
    private static String prefix="<", suffix=">";
    String myName;
    MyEnum() {
        myName = makeMyName();
    }
    String makeMyName() {
        return prefix + name() + suffix;
    }
    String getMyName() {
        return myName;
    }
}
public class Main {
    public static void main(String args[]) {
        System.out.println("first: "+MyEnum.FIRST+" second: "+MyEnum.SECOND);
        System.out.println("first: "+MyEnum.FIRST.makeMyName()+" second: "+MyEnum.SECOND.makeMyName());
        System.out.println("first: "+MyEnum.FIRST.getMyName()+" second: "+MyEnum.SECOND.getMyName());
    }
}

और आपको मिलता है:

$ javac Main.java
$ java Main
first: FIRST second: SECOND
first: <FIRST> second: <SECOND>
first: nullFIRSTnull second: nullSECONDnull

ध्यान दें कि आप स्थैतिक चर घोषणा को एक पंक्ति ऊपरी नहीं ले जा सकते, कोड संकलित नहीं करेगा।


3
उपयोगी उदाहरण; यह जानना अच्छा है कि ऐसा कोई छेद है। इससे जो मैं दूर हूँ, वह यह है कि किसी भी वर्ग के स्थैतिक आरंभीकरण के दौरान इस तरह के एक स्थिर चर का उल्लेख करने से बचना चाहिए।
टूलमेकर

10

आवेदन वर्ग:

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {

    private static Context mContext;

    public void onCreate() {
        super.onCreate();
        mContext = getApplicationContext();
    }

    public static Context getAppContext() {
        return mContext;
    }

}

AndroidManifest में अनुप्रयोग घोषित करें:

<application android:name=".MyApplication"
    ...
/>

उपयोग:

MyApplication.getAppContext()

1
मेमोरी लीक होने का खतरा। आपको कभी भी ऐसा नहीं करना चाहिए।
ड्रैगस

9

आप अनुप्रयोग संदर्भ प्राप्त करने के लिए एक आवरण बनाने की कोशिश कर रहे हैं और संभावना है कि यह " null" पॉइंटर लौटा सकता है ।

मेरी समझ के अनुसार, मुझे कॉल करने के लिए इसके बेहतर दृष्टिकोण का अनुमान है- 2 में से कोई भी Context.getApplicationContext() या Activity.getApplication()


13
इसे कब वापस करना चाहिए?
Stuck

25
कोई स्थिर संदर्भ नहीं है ।getApplicationContext () विधि जिसके बारे में मुझे पता है। क्या मैं कुछ भूल रहा हूँ?
दल्कांतरा

मैं अपने एप्लिकेशन में भी यही दृष्टिकोण लागू करता हूं, लेकिन SQLiteOpenHelper में कॉल करने पर, यह अशक्त सूचक देता है। इस तरह की स्थिति के लिए कोई भी जवाब।
आशुतोष

2
यदि आप एप्लिकेशन से पहले लोड किए गए कंटेंटप्रूडर में SQLiteOpenHelper कहते हैं तो यह मामला हो सकता है।
गुन्नार बर्नस्टीन

5

यह एक अच्छा तरीका है। मैं इसका इस्तेमाल खुद भी करता हूं। मैं केवल onCreateएक कंस्ट्रक्टर का उपयोग करने के बजाय एकल सेट करने के लिए ओवरराइड करने का सुझाव दूंगा।

और जब से आपने उल्लेख किया है SQLiteOpenHelper: onCreate ()आप डेटाबेस को भी खोल सकते हैं।

व्यक्तिगत रूप से मुझे लगता है कि दस्तावेज में यह कहना गलत है कि आम तौर पर एप्लिकेशन को उपवर्ग करने की कोई आवश्यकता नहीं है । मुझे लगता है कि विपरीत सच है: आपको हमेशा एप्लिकेशन को उप-वर्ग करना चाहिए।


3

मैं कंस्ट्रक्टर में एक सिस्टम सेवा प्राप्त करने के लिए एप्लिकेशन संदर्भ का उपयोग करेगा। यह रचना से परीक्षण और लाभ को आसान बनाता है

public class MyActivity extends Activity {

    private final NotificationManager notificationManager;

    public MyActivity() {
       this(MyApp.getContext().getSystemService(NOTIFICATION_SERVICE));
    }

    public MyActivity(NotificationManager notificationManager) {
       this.notificationManager = notificationManager;
    }

    // onCreate etc

}

टेस्ट क्लास फिर ओवरलोडेड कंस्ट्रक्टर का उपयोग करेगा।

एंड्रॉइड डिफॉल्ट कंस्ट्रक्टर का उपयोग करेगा।


1

मुझे यह पसंद है, लेकिन मैं इसके बजाय एक सिंगलटन का सुझाव दूंगा:

package com.mobidrone;

import android.app.Application;
import android.content.Context;

public class ApplicationContext extends Application
{
    private static ApplicationContext instance = null;

    private ApplicationContext()
    {
        instance = this;
    }

    public static Context getInstance()
    {
        if (null == instance)
        {
            instance = new ApplicationContext();
        }

        return instance;
    }
}

31
Android.app.application का विस्तार करना पहले से ही सिंगलटन की गारंटी देता है, इसलिए यह अनावश्यक है
विंसेंट

8
यदि आप गैर-गतिविधि वर्गों से एकेस चाहते हैं तो क्या होगा?
मैक्सरनर

9
आपको कभी newभी एप्लिकेशन को स्वयं नहीं करना चाहिए (यूनिट परीक्षण के संभावित अपवाद के साथ)। ऑपरेटिंग सिस्टम ऐसा करेगा। आपके पास कंस्ट्रक्टर भी नहीं होना चाहिए। जो है, onCreateउसके लिए है
मार्टिन

@ विजेंट: क्या आप इस पर कुछ लिंक पोस्ट कर सकते हैं? अधिमानतः कोड - मैं यहां पूछ रहा हूं: stackoverflow.com/questions/19365797/…
Mr_and_Mrs_D

@radzio क्यों हम इसे निर्माता में नहीं करना चाहिए?
Miha_x64

1

मैं उसी दृष्टिकोण का उपयोग कर रहा हूं, मैं सिंगलटन को थोड़ा बेहतर लिखने का सुझाव देता हूं:

public static MyApp getInstance() {

    if (instance == null) {
        synchronized (MyApp.class) {
            if (instance == null) {
                instance = new MyApp ();
            }
        }
    }

    return instance;
}

लेकिन मैं हर जगह उपयोग नहीं कर रहा हूं, मैं उपयोग करता हूं getContext()और getApplicationContext()जहां मैं कर सकता हूं!


तो, कृपया यह समझाने के लिए एक टिप्पणी लिखें कि आपने उत्तर को क्यों अस्वीकृत किया है ताकि मैं समझ सकूं। सिंगलटन दृष्टिकोण का उपयोग व्यापक रूप से गतिविधियों या विचारों के शरीर के बाहर एक वैध संदर्भ प्राप्त करने के लिए किया जाता है ...
सेराफिम का

1
कोई ज़रूरत नहीं है क्योंकि ऑपरेटिंग सिस्टम यह सुनिश्चित करता है कि एप्लिकेशन को एक बार ठीक से इंस्टेंट किया जाए। यदि कोई हो तो मैं सिंगेल्टन को ऑनक्रिएट () में सेट करने का सुझाव दूंगा।
मार्टिन

1
आलसी के लिए एक अच्छा धागा-सुरक्षित तरीका एक सिंगलटन को इनिशियलाइज़ करता है, लेकिन यहाँ नेक्सेसरी नहीं है।
naXa

2
वाह, जब मैंने सोचा कि लोगों ने अंततः डबल-चेक लॉकिंग का उपयोग करना बंद कर दिया है ... cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
सॉरेन बोइसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.