سؤال

في تطبيق Android، هل هناك خطأ خطأ في النهج التالي:

public class MyApp extends android.app.Application {

    private static MyApp instance;

    public MyApp() {
        instance = this;
    }

    public static Context getContext() {
        return instance;
    }

}

وتمريرها في كل مكان (مثل sqliteopenhelper) حيث يلزم السياق (وليس تسرب بالطبع)؟

هل كانت مفيدة؟

المحلول

هناك مشاكل محتملة في هذا النهج، على الرغم من ذلك في الكثير من الظروف (مثل مثالك) ستعمل بشكل جيد.

على وجه الخصوص، يجب أن تكون حذرا عند التعامل مع أي شيء يتناول GUI يتطلب ذلك Context. وبعد على سبيل المثال، إذا قمت بتمرير سياق التطبيق إلى LayoutInflater سوف تحصل على استثناء. بشكل عام، نهجك ممتاز: إنها ممارسة جيدة لاستخدام Activity's Context داخل ذلك Activity, ، و ال Application Context عند تمرير سياق خارج نطاق Activity ل تجنب تسرب الذاكرة.

أيضا، لبديل إلى النمط الخاص بك يمكنك استخدام اختصار الاتصال getApplicationContext() على Context كائن (مثل نشاط) للحصول على سياق التطبيق.

نصائح أخرى

في تجربتي، يجب ألا يكون هذا النهج ضروريا. إذا كنت بحاجة إلى السياق لأي شيء يمكنك عادة الحصول عليه عبر مكالمة view.getContext () واستخدام. Context تم الحصول عليها هناك يمكنك الاتصال context.getApplicationContext () للحصول على Application سياق الكلام. إذا كنت تحاول الحصول على Application السياق هذا من Activity يمكنك دائما الاتصال نشاط. getApplication () والتي ينبغي أن تكون قادرة على تمرير Context اللازمة للحصول على دعوة SQLiteOpenHelper().

بشكل عام لا يبدو أن هناك مشكلة في نهجك لهذا الموقف، ولكن عند التعامل معها Context فقط تأكد من أنك لا تسريب الذاكرة في أي مكان كما هو موضح في المسؤول جوجل أندرويد المطورين بلوق.

طلب بعض الناس: كيف يمكن أن يعود Singleton مؤشرا فارغا؟أنا أجب على هذا السؤال. (لا أستطيع الإجابة في تعليق لأنني بحاجة إلى نشر التعليمات البرمجية.)

قد يعود NULL في ما بين حدثين: (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.Sinstance. و X.Yinstance. نكون باطل; ؛ إنها فارغة لأن المتغيرات x.xinstance. جوانب 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 الكائن: لا يتحكم المبرمج في الوقت الذي يتم فيه إنشاءه.

مرة أخرى: الفرق بين المثال الأول والثاني هو أن المثال الثاني يخلق مثيل إذا كان المؤشر الثابت لاغيا. لكن مبرمج لا يمكن أن يخلق ال كائن تطبيق Android قبل أن يقرر النظام القيام بذلك.

تحديث

مثال واحد أكثر حيرة حيث تحدث الحقول الثابتة التهيئة null.

Main.java.:

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

لاحظ أنه لا يمكنك تحريك الإعلان المتغير الثابت العلوي سطر واحد، فلن يترجم الكود.

أنت تحاول إنشاء غلاف للحصول على سياق التطبيق وهناك احتمال أن يعود "nullمؤشر.

حسب فهمي، أعتقد أن نهجها الأفضل للاتصال - أي من 2Context.getApplicationContext() أو Activity.getApplication().

فئة التطبيق:

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

إنه نهج جيد. أنا استخدمها بنفسي كذلك. أود أن أقترح فقط تجاوز onCreate لتعيين Singleton بدلا من استخدام منشئ.

ومنذ ذكرت SQLiteOpenHelper: في onCreate () يمكنك فتح قاعدة البيانات أيضا.

أنا شخصيا أعتقد أن الوثائق حصلت على خطأ في قول ذلك لا حاجة عادة إلى تطبيق Subclass. وبعد أعتقد أن العكس صحيح: يجب عليك دائما تطبيق Subclass.

أود استخدام سياق التطبيق للحصول على خدمة النظام في المنشئ. هذا يخفف من الاختبار والفوائد من التكوين

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

}

سيتم بعد ذلك استخدام فئة الاختبار المنشئ الزائد.

سيستخدم Android المنشئ الافتراضي.

أنا أحب ذلك، لكنني أقترح سينسانتون بدلا من ذلك:

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

أنا أستخدم نفس النهج، أقترح أن أكتب Singleton أفضل قليلا:

public static MyApp getInstance() {

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

    return instance;
}

لكنني لا أستخدم في كل مكان، وأنا استخدم getContext() و getApplicationContext() حيث يمكنني القيام بذلك!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top