วิธีแบบคงที่จะได้รับ 'บริบท' ใน Android?


970

มีวิธีรับContextอินสแตนซ์ปัจจุบันในวิธีการคงที่หรือไม่

ฉันกำลังมองหาวิธีนี้เพราะฉันเกลียดการบันทึกอินสแตนซ์ 'บริบท' ทุกครั้งที่มีการเปลี่ยนแปลง


57
การไม่บันทึกบริบทเป็นความคิดที่ดีไม่ใช่เพียงเพราะมันไม่สะดวก แต่มากขึ้นเพราะมันสามารถนำไปสู่การรั่วไหลของหน่วยความจำขนาดใหญ่!
Vikram Bodicherla

12
@VikramBodicherla ใช่ แต่คำตอบด้านล่างถือว่าเรากำลังพูดถึงบริบทของแอปพลิเคชัน ดังนั้นการรั่วไหลของหน่วยความจำไม่ใช่ปัญหา แต่ผู้ใช้ควรใช้วิธีแก้ปัญหาเหล่านี้เฉพาะที่เป็นบริบทที่ถูกต้องที่จะใช้
Tom

หากคุณต้องใช้วิธีการแบบคงที่การรับContextอาจมีวิธีที่ดีกว่าในการออกแบบรหัส
Anonsage

3
เอกสาร Android แนะนำให้ส่งบริบทไปยังผู้ได้รับผลประโยชน์จากซิงเกิล developer.android.com/reference/android/app/Application.html
Marco Luglio

สำหรับการเลือกซิงเกิลตันและบริบทที่ส่งผ่านด้วย getInstance () บนบริบทแบบคงที่โปรดดูฉันพยายามอธิบายเหตุผลของฉันที่นี่พร้อมรหัสทำงาน: stackoverflow.com/a/38967293/4469112
Alessio

คำตอบ:


1302

ทำเช่นนี้:

ในไฟล์ Android Manifest ให้ประกาศสิ่งต่อไปนี้

<application android:name="com.xyz.MyApplication">

</application>

จากนั้นเขียนชั้นเรียน

public class MyApplication extends Application {

    private static Context context;

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

    public static Context getAppContext() {
        return MyApplication.context;
    }
}

ตอนนี้ทุกที่โทรMyApplication.getAppContext()เพื่อรับบริบทใบสมัครของคุณแบบคงที่


81
มีข้อเสียสำหรับวิธีนี้หรือไม่? ดูเหมือนว่าการโกง (แฮ็ค?)
jjnguy

203
ข้อเสียคือไม่มีการรับประกันว่าจะไม่เรียกใช้ onCreate () แบบไม่คงที่ก่อนที่รหัสเริ่มต้นคงที่บางส่วนจะพยายามดึงข้อมูลวัตถุบริบทของคุณ นั่นหมายความว่ารหัสการโทรของคุณจะต้องพร้อมที่จะจัดการกับค่า Null ซึ่งจะเอาชนะจุดทั้งหมดของคำถามนี้ได้
Melinda Green

8
บางที .. เราควรจะประกาศstatic contextตัวแปรนี้ด้วยvolatileเหรอ?
Vladimir Sorokin

14
@Tom นี่ไม่ใช่กรณีของสมาชิกข้อมูลแบบคงที่ที่เริ่มคงที่ ในรหัสที่กำหนดสมาชิกแบบคงที่จะถูกเตรียมใช้งานแบบไม่คงที่ใน onCreate () แม้แต่ข้อมูลที่เริ่มต้นแบบสแตติกไม่ดีพอในกรณีนี้เพราะไม่มีสิ่งใดยืนยันได้ว่าการเริ่มต้นแบบคงที่ของคลาสที่กำหนดจะเกิดขึ้นก่อนที่จะสามารถเข้าถึงได้ในระหว่างการเริ่มต้นแบบคงที่ของคลาสอื่น ๆ
Melinda Green

10
@MelindaGreen ตามเอกสารสำหรับแอปพลิเคชัน onCreate () จะถูกเรียกก่อนที่จะมีการสร้างกิจกรรมบริการหรือผู้รับ (ยกเว้นผู้ให้บริการเนื้อหา) ใด ๆ ดังนั้นโซลูชันนี้จะไม่ปลอดภัยตราบใดที่คุณไม่พยายามเข้าถึง getAppContext () จากผู้ให้บริการเนื้อหา
แมกนัส W

86

แอพพลิเคชั่นส่วนใหญ่ที่ต้องการวิธีการที่สะดวกในการทำให้บริบทของแอพพลิเคชั่นสร้างคลาสของตัวเองandroid.app.Applicationขึ้นมา

GUIDE

คุณสามารถทำได้โดยการสร้างชั้นเรียนในโครงการของคุณเช่น:

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

public class App extends Application {

    private static Application sApplication;

    public static Application getApplication() {
        return sApplication;
    }

    public static Context getContext() {
        return getApplication().getApplicationContext();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sApplication = this;
    }
}

จากนั้นใน AndroidManifest ของคุณคุณควรระบุชื่อคลาสของคุณในแท็กของ AndroidManifest.xml:

<application 
    ...
    android:name="com.example.App" >
    ...
</application>

จากนั้นคุณสามารถดึงบริบทแอปพลิเคชันในวิธีการคงที่ใด ๆ โดยใช้ต่อไปนี้:

public static void someMethod() {
    Context context = App.getContext();
}

คำเตือน

ก่อนที่จะเพิ่มบางอย่างเช่นข้างต้นให้กับโครงการของคุณคุณควรพิจารณาว่าเอกสารระบุว่า:

โดยปกติไม่จำเป็นต้องใช้คลาสย่อย ในสถานการณ์ส่วนใหญ่ Singletons แบบคงที่สามารถให้ฟังก์ชั่นเดียวกันในทางโมดูลาร์มากขึ้น หากซิงเกิลของคุณต้องการบริบทส่วนกลาง (ตัวอย่างเช่นการลงทะเบียนรีซีฟเวอร์รีซีฟเวอร์) ฟังก์ชั่นเพื่อดึงข้อมูลสามารถรับบริบทซึ่งใช้ Context.getApplicationContext () ภายในเมื่อสร้างซิงเกิลครั้งแรก


การสะท้อน

นอกจากนี้ยังมีวิธีอื่นในการรับบริบทของแอปพลิเคชันโดยใช้การสะท้อน การสะท้อนกลับมักถูกมองลงใน Android และโดยส่วนตัวฉันคิดว่าสิ่งนี้ไม่ควรใช้ในการผลิต

ในการเรียกคืนบริบทแอปพลิเคชันเราจะต้องเรียกใช้เมธอดในคลาสที่ซ่อนอยู่ ( ActivityThread ) ซึ่งใช้ได้ตั้งแต่ API 1:

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.ActivityThread")
            .getMethod("currentApplication").invoke(null, (Object[]) null);
}

มีคลาสที่ซ่อนอยู่อีกหนึ่งรายการ ( AppGlobals ) ซึ่งให้วิธีรับบริบทแอปพลิเคชันในแบบคงที่ มันทำให้บริบทใช้ActivityThreadดังนั้นจึงไม่มีความแตกต่างระหว่างวิธีต่อไปนี้กับวิธีที่โพสต์ด้านบน:

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.AppGlobals")
            .getMethod("getInitialApplication").invoke(null, (Object[]) null);
} 

การเข้ารหัสที่มีความสุข!


56

สมมติว่าเรากำลังพูดถึงการรับบริบทของแอปพลิเคชันฉันได้ดำเนินการตามที่แนะนำโดย @Rohit Ghatol การขยายแอปพลิเคชัน สิ่งที่เกิดขึ้นในตอนนั้นก็คือไม่มีการรับประกันว่าบริบทที่ดึงมาในลักษณะนี้จะไม่เป็นโมฆะ ในเวลาที่คุณต้องการมันมักจะเป็นเพราะคุณต้องการเริ่มต้นช่วยเหลือหรือได้รับทรัพยากรที่คุณไม่สามารถล่าช้าในเวลา; การจัดการเคสว่างจะไม่ช่วยคุณ ดังนั้นฉันเข้าใจว่าฉันกำลังต่อสู้กับสถาปัตยกรรม Android ตามที่ระบุในเอกสาร

หมายเหตุ: โดยปกติไม่จำเป็นต้องใช้คลาสย่อย ในสถานการณ์ส่วนใหญ่ Singletons แบบคงที่สามารถให้ฟังก์ชั่นเดียวกันในทางโมดูลาร์มากขึ้น หากซิงเกิลของคุณต้องการบริบทส่วนกลาง (ตัวอย่างเช่นในการลงทะเบียนเครื่องรับสัญญาณออกอากาศ) ให้รวม Context.getApplicationContext () เป็นอาร์กิวเมนต์บริบทเมื่อเรียกใช้เมธอด getInstance () ของซิงเกิล

และอธิบายโดยDianne Hackborn

เหตุผลเดียวที่แอปพลิเคชันมีอยู่เป็นสิ่งที่คุณสามารถหาได้เนื่องจากในระหว่างการพัฒนา pre-1.0 หนึ่งในนักพัฒนาแอปพลิเคชันของเราถูก bugging ฉันอย่างต่อเนื่องเกี่ยวกับการต้องการวัตถุแอปพลิเคชันระดับบนสุดที่พวกเขาสามารถสืบทอด "สำหรับพวกเขาในรูปแบบของแอปพลิเคชันและในที่สุดฉันก็ยอมแพ้ฉันจะเสียใจในสิ่งนั้นตลอดไป :)

เธอยังแนะนำวิธีแก้ปัญหานี้ด้วย:

หากสิ่งที่คุณต้องการคือสถานะทั่วโลกที่สามารถแชร์ข้ามส่วนต่าง ๆ ของแอพของคุณได้ [... ] และสิ่งนี้นำไปสู่การจัดการสิ่งเหล่านี้อย่างเป็นธรรมชาติมากขึ้นโดยเริ่มต้นได้ตามต้องการ

ดังนั้นสิ่งที่ฉันทำคือกำจัดแอปพลิเคชันที่ขยายออกและส่งบริบทโดยตรงไปยัง getInstance () ของผู้ช่วยซิงเกิลตันในขณะที่บันทึกการอ้างอิงไปยังบริบทของแอปพลิเคชันใน Constructor ส่วนตัว:

private static MyHelper instance;
private final Context mContext;    

private MyHelper(@NonNull Context context) {
    mContext = context.getApplicationContext();
}

public static MyHelper getInstance(@NonNull Context context) {
    synchronized(MyHelper.class) {
        if (instance == null) {
            instance = new MyHelper(context);
        }
        return instance;
    }
}

ผู้โทรจะส่งบริบทท้องถิ่นไปยังผู้ช่วย:

Helper.getInstance(myCtx).doSomething();

ดังนั้นเพื่อตอบคำถามนี้อย่างถูกต้อง: มีวิธีเข้าถึงบริบทของแอปพลิเคชันแบบคงที่ แต่พวกเขาควรหมดกำลังใจและคุณควรส่งบริบทท้องถิ่นไปที่ getInstance () ของซิงเกิล


สำหรับผู้ที่สนใจคุณสามารถอ่านรายละเอียดเพิ่มเติมได้ที่fwd blog


1
@Alessio วิธีนี้ไม่นำไปสู่การรั่วไหลของหน่วยความจำ
Phillip Kigenyi

2
@codephillip ฉันไม่เข้าใจสิ่งที่คุณกำลังพูดถึง ซิงเกิลอ้างอิงบริบทแอ็พพลิเคชันที่ดึงมาจากกิจกรรมที่ส่งผ่านไม่ใช่กิจกรรมโฮสต์ นั่นเป็นเรื่องถูกต้องและจะไม่ทำให้หน่วยความจำรั่วไหล นั่นคือประเด็นหลักของบล็อกที่ฉันเขียน หากคุณคิดว่าถูกต้องโปรดส่งรหัสตัวอย่างที่ฉันสามารถสร้างการรั่วไหลของหน่วยความจำที่คุณพูดถึงเพราะนั่นไม่ใช่กรณี
Alessio

1
ฉันคิดว่า @KigenyiPhillip ถูกต้องและสิ่งนี้ยังคงแสดงถึงการรั่วไหลของทรัพยากร getInstance(ctx)ภาพแผนภูมิการอ้างอิงหลังจากที่สายแรกของคุณไป คุณมีราก GC instanceประเภทMyHelperซึ่งมีข้อมูลส่วนตัวmContextของประเภทซึ่งอ้างอิงบริบทแอพลิเคชันที่เก็บรวบรวมผ่านบริบทที่ผ่านมา Context ไม่เคยตั้งเป็นครั้งที่สองหรือเคลียร์เพื่อให้ประชาคมโลกจะไม่จับ appcontext อ้างอิงโดย คุณไม่รั่วไหลกิจกรรมใด ๆ ดังนั้นมันจึงเป็น IMO ที่ราคาถูก getInstance()instanceinstance
Mark McKenna

1
@MarkMcKenna ตามที่คุณระบุ "ซึ่งมีฟิลด์ mContext ส่วนบุคคลที่เป็นประเภทบริบทซึ่งอ้างอิงถึงบริบทของแอปพลิเคชัน" ดังนั้นจึงเป็นที่ชัดเจนสำหรับคุณว่า mContext เป็นการอ้างอิงถึงบริบทของแอปพลิเคชันไม่ใช่บริบทใด ๆ ในเอกสาร getApplicationContext () ที่คุณอ่าน: "บริบทที่มีวงจรชีวิตแยกจากบริบทปัจจุบันซึ่งเชื่อมโยงกับอายุการใช้งานของกระบวนการมากกว่าองค์ประกอบปัจจุบัน" สิ่งนี้จะสร้างหน่วยความจำรั่วได้อย่างไร บริบทของแอปพลิเคชันคือ GC'd เมื่อกระบวนการออกจากเท่านั้น
Alessio

1
@Alessio ถ้าคุณยอมรับว่ามีการอ้างอิงถึงบริบทใบสมัครไม่ได้มีคุณสมบัติเป็นการรั่วไหลของทรัพยากรแล้วคุณสามารถลดความซับซ้อนของการโพสต์นี้โดยอ้างอิงคงที่thisในApplication.onCreate()ซึ่งจะทำให้คำตอบที่ได้รับการยอมรับที่ดีขึ้น
Mark McKenna

49

ไม่ฉันไม่คิดว่าจะมี แต่น่าเสียดายที่คุณโทรติดgetApplicationContext()จากActivityหรือหนึ่ง subclasses อื่น ๆ Contextของ นอกจากนี้คำถามนี้ค่อนข้างเกี่ยวข้องกัน


8
ลิงก์ด้านขวาไปยังบทความ: android-developers.blogspot.co.il/2009/01/…
Tal Weiss

38

นี่คือวิธีที่ไม่มีเอกสารในการรับแอปพลิเคชัน (ซึ่งเป็นบริบท) จากทุกที่ในเธรด UI ActivityThread.currentApplication()มันอาศัยอยู่กับวิธีการแบบคงที่ซ่อนอยู่ มันควรจะทำงานอย่างน้อยใน Android 4.x

try {
    final Class<?> activityThreadClass =
            Class.forName("android.app.ActivityThread");
    final Method method = activityThreadClass.getMethod("currentApplication");
    return (Application) method.invoke(null, (Object[]) null);
} catch (final ClassNotFoundException e) {
    // handle exception
} catch (final NoSuchMethodException e) {
    // handle exception
} catch (final IllegalArgumentException e) {
    // handle exception
} catch (final IllegalAccessException e) {
    // handle exception
} catch (final InvocationTargetException e) {
    // handle exception
}

โปรดทราบว่าเป็นไปได้ที่วิธีนี้จะคืนค่า null เช่นเมื่อคุณเรียกใช้เมธอดภายนอกเธรด UI หรือแอปพลิเคชันไม่ได้ผูกไว้กับเธรด

การใช้โซลูชันของ@RohitGhatolจะดีกว่าหากคุณสามารถเปลี่ยนรหัสแอปพลิเคชันได้


1
ฉันใช้วิธีดังกล่าวข้างต้น KennyTM แต่บางครั้งวิธีคืนกลับเป็นค่าว่าง มีทางเลือกอื่นบ้างไหมในเรื่องนี้? เช่นถ้าเราได้รับโมฆะที่นี่เราสามารถดึงบริบทจากที่อื่น ในกรณีของฉัน onCreate () ของแอปพลิเคชันไม่ได้ถูกเรียก แต่วิธีการข้างต้นได้รับการเรียกก่อน Plzzz help
AndroidGuy

สิ่งนี้จะไม่สามารถใช้งานได้ในกรณีที่ GC ล้างสิ่งที่เกี่ยวข้องกับกิจกรรมทั้งหมด
AlexVPerl

32

ขึ้นอยู่กับสิ่งที่คุณใช้บริบท ฉันสามารถคิดถึงข้อเสียอย่างน้อยหนึ่งวิธี:

ถ้าคุณกำลังพยายามที่จะสร้างAlertDialogกับAlertDialog.Builderที่Applicationบริบทจะไม่ทำงาน ฉันเชื่อว่าคุณต้องการบริบทสำหรับปัจจุบันActivity...


6
ถูกตัอง. หากคุณใช้บริบทแอปพลิเคชันสำหรับสิ่งนั้นคุณอาจเห็นกล่องโต้ตอบของคุณซ่อนอยู่ภายใต้กิจกรรมเบื้องหน้า
เนท

3
+1 ก่อนอื่น และข้อผิดพลาดที่อาจเกิดขึ้นคือไม่สามารถเริ่มกิจกรรม ComponentInfo {com.samples / com.MyActivity}: android.view.WindowManager $ BadTokenException: ไม่สามารถเพิ่มหน้าต่าง - โทเค็น null ไม่ได้สำหรับแอปพลิเคชัน
Govind

15

วิธี Kotlin :

Manifest:

<application android:name="MyApplication">

</application>

MyApplication.kt

class MyApplication: Application() {

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    companion object {
        lateinit var instance: MyApplication
            private set
    }
}

จากนั้นคุณสามารถเข้าถึงคุณสมบัติผ่าน MyApplication.instance


11

หากคุณเปิดให้ใช้RoboGuiceคุณสามารถใส่บริบทเข้าไปในชั้นเรียนที่คุณต้องการ นี่คือตัวอย่างเล็ก ๆ ของวิธีการทำกับ RoboGuice 2.0 (เบต้า 4 ในขณะที่เขียนนี้)

import android.content.Context;
import android.os.Build;
import roboguice.inject.ContextSingleton;

import javax.inject.Inject;

@ContextSingleton
public class DataManager {
    @Inject
    public DataManager(Context context) {
            Properties properties = new Properties();
            properties.load(context.getResources().getAssets().open("data.properties"));
        } catch (IOException e) {
        }
    }
}

8

ฉันเคยใช้สิ่งนี้ในบางจุด:

ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();

นี่เป็นบริบทที่ถูกต้องที่ฉันใช้ในการรับบริการระบบและทำงานได้

แต่ฉันใช้มันเฉพาะในการแก้ไขเฟรมเวิร์ก / ฐานและไม่ได้ลองในแอปพลิเคชัน Android

เตือนว่าคุณต้องรู้: เมื่อลงทะเบียนรับการออกอากาศกับบริบทนี้มันจะไม่ทำงานและคุณจะได้รับ:

java.lang.SecurityException: ชุดผู้โทรที่ได้รับ android ไม่ได้ทำงานในกระบวนการ ProcessRecord


7

Kotlin

open class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        mInstance = this
    }

    companion object {
        lateinit var mInstance: MyApp
        fun getContext(): Context? {
            return mInstance.applicationContext
        }
    }
}

และรับบริบทเช่น

MyApp.mInstance

หรือ

MyApp.getContext()

4

คุณสามารถใช้สิ่งต่อไปนี้:

MainActivity.this.getApplicationContext();

MainActivity.java:

...
public class MainActivity ... {
    static MainActivity ma;
...
    public void onCreate(Bundle b) {
         super...
         ma=this;
         ...

คลาสอื่น ๆ :

public ...
    public ANY_METHOD... {
         Context c = MainActivity.ma.getApplicationContext();

3
ใช้งานได้เฉพาะในกรณีที่คุณอยู่ในคลาสภายในซึ่งแทบจะไม่เป็นเช่นนั้นใน OP
Richard J. Ross III

3
สิ่งนี้จะทำงานได้ตราบใดที่ ANY_METHOD ถูกเรียกหลังจาก MainActivity ถูกสร้างขึ้น แต่การเก็บการอ้างอิงแบบคงที่ให้กับกิจกรรมเกือบจะนำหน่วยความจำรั่วมาใช้อย่างหลีกเลี่ยงไม่ได้ บริบทเท่านั้น
handtwerk

1
ชั้นในนั้นชั่วร้าย ส่วนที่แย่ที่สุดคือผู้คนจำนวนมากทำเช่นนั้นกับ AsyncTasks และสิ่งต่าง ๆ เช่นนั้นเนื่องจากบทเรียนจำนวนมากทำในแบบนั้น ...
352 Reinherd

4

หากคุณไม่ต้องการแก้ไขไฟล์รายการคุณสามารถจัดเก็บบริบทด้วยตนเองในตัวแปรสแตติกในกิจกรรมเริ่มต้นของคุณ:

public class App {
    private static Context context;

    public static void setContext(Context cntxt) {
        context = cntxt;
    }

    public static Context getContext() {
        return context;
    }
}

และเพียงกำหนดบริบทเมื่อกิจกรรม (หรือกิจกรรม) ของคุณเริ่มต้น:

// MainActivity

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Set Context
    App.setContext(getApplicationContext());

    // Other stuff
}

หมายเหตุ:เช่นเดียวกับคำตอบอื่น ๆ ทั้งหมดนี่เป็นความจำที่อาจรั่วไหล


1
จะเกิดอะไรขึ้นถ้าบริบทในกรณีนี้ถูกผูกไว้กับแอปพลิเคชัน หากแอปพลิเคชันตายดังนั้นจะทำทุกอย่างอื่น
TheRealChx101

3

ฉันคิดว่าคุณต้องการร่างกายสำหรับgetAppContext()วิธีการ:

public static Context getAppContext()
   return MyApplication.context; 

3

ตามแหล่งที่มานี้คุณสามารถรับบริบทของคุณเองโดยขยาย ContextWrapper

public class SomeClass extends ContextWrapper {

    public SomeClass(Context base) {
      super(base);
    }

    public void someMethod() {
        // notice how I can use "this" for Context
        // this works because this class has it's own Context just like an Activity or Service
        startActivity(this, SomeRealActivity.class);

        //would require context too
        File cacheDir = getCacheDir();
    }
}

JavaDoc สำหรับ ContextWrapper

การนำพร็อกซีมาใช้ของบริบทที่เพียงมอบหมายการเรียกทั้งหมดไปยังบริบทอื่น สามารถ subclassed เพื่อปรับเปลี่ยนพฤติกรรมโดยไม่ต้องเปลี่ยนบริบทเดิม


1
สิ่งนี้น่าสนใจ ดีที่จะเรียนรู้เกี่ยวกับ ContextWrapper อย่างไรก็ตามหากคุณต้องการส่งผ่านบริบทของแอปพลิเคชันไปยังตัวสร้างนี้คุณยังต้องได้รับจากที่อื่น
jk7

2

หากคุณต้องการบริบทของแอปพลิเคชันในชั้นเรียนไม่ว่าจะเป็นการขยายแอพพลิเคชั่น / กิจกรรมบางทีสำหรับบางคลาสจากโรงงานหรือผู้ช่วย คุณสามารถเพิ่มซิงเกิลต่อไปนี้ในแอปของคุณ

public class GlobalAppContextSingleton {
    private static GlobalAppContextSingleton mInstance;
    private Context context;

    public static GlobalAppContextSingleton getInstance() {
        if (mInstance == null) mInstance = getSync();
        return mInstance;
    }

    private static synchronized GlobalAppContextSingleton getSync() {
        if (mInstance == null) mInstance = 
                new GlobalAppContextSingleton();
        return mInstance;
    }

    public void initialize(Context context) {
        this.context = context;
    }

    public Context getApplicationContext() {
        return context;
    }
}

จากนั้นเริ่มต้นได้ใน onCreate ด้วยคลาสแอปพลิเคชันของคุณ

GlobalAppContextSingleton.getInstance().initialize(this);

ใช้งานได้ทุกที่โดยการโทร

GlobalAppContextSingleton.getInstance().getApplicationContext()

ฉันไม่แนะนำวิธีการนี้ให้กับสิ่งใดนอกจากบริบทของแอปพลิเคชัน เนื่องจากอาจทำให้หน่วยความจำรั่ว


มันไม่เหมือนชื่อคลาส / เมธอดที่ตั้งค่าไว้ในหินทำให้มันยาวและหวังว่าจะได้คำอธิบายสำหรับคำถามและคำตอบสั้น ๆ เพื่อใช้เอง
ทางกลับ

1

ฉันใช้รูปแบบการออกแบบซิงเกิลที่หลากหลายเพื่อช่วยฉันในเรื่องนี้

import android.app.Activity;
import android.content.Context;

public class ApplicationContextSingleton {
    private static Activity gContext;

    public static void setContext( Activity activity) {
        gContext = activity;
    }

    public static Activity getActivity() {
        return gContext;
    }

    public static Context getContext() {
        return gContext;
    }
}

จากนั้นฉันก็โทรหาApplicationContextSingleton.setContext( this );ในกิจกรรมของฉันบนสร้าง()และApplicationContextSingleton.setContext( null );ในonDestroy () ;


หากสิ่งที่คุณต้องการคือบริบทคุณสามารถเรียกใช้งาน activity.getApplicationContext (); ที่สามารถยึดไว้กับที่โดยไม่ต้องกังวลเกี่ยวกับการรั่ว
MinceMan

2
สิ่งนี้จะทำให้เกิดการรั่วไหลของหน่วยความจำ
BlueWizard

1

ฉันเพิ่งเปิดตัวเฟรมเวิร์กที่ได้รับแรงบันดาลใจจาก jQuery สำหรับ Android ที่เรียกว่าVapor APIที่มีวัตถุประสงค์เพื่อทำให้การพัฒนาแอปง่ายขึ้น

$คลาสซุ้มกลางรักษาWeakReference(ลิงก์ไปยังโพสต์บล็อก Java ที่ยอดเยี่ยมเกี่ยวกับเรื่องนี้โดย Ethan Nicholas) ไปยังActivityบริบทปัจจุบันซึ่งคุณสามารถดึงข้อมูลได้ด้วยการโทร:

$.act()

การWeakReferenceเก็บรักษาข้อมูลอ้างอิงโดยไม่ป้องกันการรวบรวมขยะเรียกคืนวัตถุต้นฉบับดังนั้นคุณไม่ควรมีปัญหากับหน่วยความจำรั่ว

ข้อเสียของหลักสูตรคือคุณมีความเสี่ยงที่$.act()อาจส่งคืนค่าว่าง ฉันยังไม่ได้เจอกับสถานการณ์นี้ดังนั้นอาจเป็นเพียงความเสี่ยงขั้นต่ำที่ควรค่าแก่การกล่าวถึง

คุณยังสามารถตั้งค่าบริบทด้วยตนเองหากคุณไม่ได้ใช้VaporActivityเป็นActivityคลาสของคุณ:

$.act(Activity);

นอกจากนี้เฟรมเวิร์กVapor API ส่วนใหญ่ใช้บริบทที่เก็บไว้โดยเนื้อแท้ซึ่งอาจหมายความว่าคุณไม่จำเป็นต้องจัดเก็บด้วยตัวคุณเองเลยถ้าคุณตัดสินใจใช้เฟรมเวิร์ก ตรวจสอบเว็บไซต์สำหรับข้อมูลเพิ่มเติมและตัวอย่าง

ฉันหวังว่าจะช่วย :)


1
เห็นได้ชัดว่านี่เพิ่งลงคะแนน .. คำอธิบายน่าจะดี!
Darius

1
ฉันไม่ได้ลงคะแนนในสิ่งนี้ แต่ Javascript ไม่มีอะไรเกี่ยวข้องกับคำถามในมือซึ่งจะอธิบาย downvote ใด ๆ ที่คุณอาจมี! ไชโย
Ernani Joppert

มันจะไร้สาระเพราะมันได้รับแรงบันดาลใจจากบางแง่มุมของ jQuery เช่นอินเทอร์เฟซที่คล่องแคล่วและ abstractions .. สิ่งเหล่านี้เป็นหลักการที่ไม่เชื่อเรื่องภาษาต้นแบบ!
Darius

1
ดังนั้นคุณกำลัง downvoting มันเพราะมันเป็นแรงบันดาลใจจากความหมาย API ของกรอบที่ไม่ได้อยู่ในแพลตฟอร์มเดียวกัน! ฉันคิดว่าพวกคุณพลาดจุดที่จะใช้หลักการที่ไม่เชื่อเรื่องพระเจ้าแพลตฟอร์ม .....................................
Darius

3
คำตอบนี้ไม่เกี่ยวข้องกับ JavaScript ทั้งหมด อ่านคำตอบก่อนลงคะแนน: /
BlueWizard

1

คำตอบของ Rohit นั้นถูกต้อง อย่างไรก็ตามโปรดทราบว่า "การเรียกใช้ทันที" ของ AndroidStudio ขึ้นอยู่กับการไม่มีstatic Contextแอตทริบิวต์ในรหัสของคุณเท่าที่ฉันรู้


1
คุณพูดถูก และจะส่งผลให้หน่วยความจำรั่ว!
user1506104

1

ใน Kotlin การวางบริบท / แอปบริบทในวัตถุร่วมยังคงสร้างคำเตือน Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

หรือถ้าคุณใช้สิ่งนี้:

    companion object {
        lateinit var instance: MyApp
    }

เป็นเพียงการหลอกผ้าสำลีที่จะไม่ค้นพบการรั่วไหลของหน่วยความจำอินสแตนซ์ของแอปยังคงสามารถสร้างการรั่วไหลของหน่วยความจำได้เนื่องจากคลาสแอปพลิเคชันและผู้สืบทอดเป็นบริบท

หรือคุณสามารถใช้ส่วนต่อประสานการใช้งานหรือคุณสมบัติการใช้งานเพื่อช่วยให้คุณรับบริบทแอพของคุณ

เพียงสร้างคลาสวัตถุ:

object CoreHelper {
    lateinit var contextGetter: () -> Context
}

หรือคุณสามารถใช้อย่างปลอดภัยมากขึ้นโดยใช้ประเภท nullable:

object CoreHelper {
    var contextGetter: (() -> Context)? = null
}

และในคลาสแอพของคุณให้เพิ่มบรรทัดนี้:


class MyApp: Application() {

    override fun onCreate() {
        super.onCreate()
        CoreHelper.contextGetter = {
            this
        }
    }
}

และในรายการของคุณประกาศชื่อแอป . MyApp


    <application
            android:name=".MyApp"

เมื่อคุณต้องการรับบริบทเพียงโทร:

CoreHelper.contextGetter()

// or if you use the nullable version
CoreHelper.contextGetter?.invoke()

หวังว่ามันจะช่วย


คลาสอ็อบเจ็กต์ของ corehelper นี้จะถูกกำหนดค่าเริ่มต้นและสามารถใช้งานผ่านกิจกรรมในระยะหลังได้หรือไม่ ขออภัยฉันใหม่กับ kotlin
ดร. aNdRO

ใช่
Hayi Nukman

-1

ลองอะไรเช่นนี้

import androidx.appcompat.app.AppCompatActivity;  
import android.content.Context; 
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {
    private static Context context;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = getApplicationContext();
    }

    public static void getContext(View view){
        Toast.makeText(context, "Got my context!",
                    Toast.LENGTH_LONG).show();    
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.