Statische manier om ‘Context’ in Android te krijgen?

Is er een manier om de huidige contextinstantie binnen een statische methode te krijgen?

Ik ben op zoek naar die manier omdat ik er een hekel aan heb om de ‘Context’-instantie telkens op te slaan als deze verandert.


Antwoord 1, autoriteit 100%

Doe dit:

Verklaar het volgende in het Android Manifest-bestand.

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

Schrijf vervolgens de klas:

public class MyApplication extends Application {
    private static Context context;
    public void onCreate() {
        super.onCreate();
        MyApplication.context = getApplicationContext();
    }
    public static Context getAppContext() {
        return MyApplication.context;
    }
}

Bel nu overal MyApplication.getAppContext()aan om uw applicatiecontext statisch te krijgen.


Antwoord 2, autoriteit 8%

De meeste apps die een handige methode willen om de applicatiecontext te krijgen, creëren hun eigen klasse die android.app.Application.

GIDS

U kunt dit bereiken door eerst een klasse in uw project te maken, zoals het volgende:

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

Vervolgens moet u in uw AndroidManifest de naam van uw klas specificeren in de tag van AndroidManifest.xml:

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

U kunt vervolgens de toepassingscontext op elke statische methode ophalen met behulp van het volgende:

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

WAARSCHUWING

Voordat u iets als het bovenstaande aan uw project toevoegt, moet u overwegen wat de documentatie zegt:

Het is normaal gesproken niet nodig om Application een subclassificatie te geven. In de meeste situaties,
statische singletons kunnen dezelfde functionaliteit bieden in een meer modulaire
manier. Als je singleton een globale context nodig heeft (bijvoorbeeld om te registreren)
broadcast-ontvangers), kan de functie om het op te halen een
Context die intern Context.getApplicationContext() gebruikt wanneer
eerst de singleton construeren.


REFLECTIE

Er is ook een andere manier om de toepassingscontext te krijgen met behulp van reflectie. Reflectie wordt vaak neergekeken in Android en persoonlijk vind ik dat dit niet in productie moet worden gebruikt.

Om de toepassingscontext op te halen, moeten we een methode aanroepen op een verborgen klasse (ActivityThread) die beschikbaar is sinds API 1:

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

Er is nog een verborgen klasse (AppGlobals) die een manier biedt om de toepassingscontext op een statische manier te krijgen. Het haalt de context met behulp van ActivityThread, dus er is echt geen verschil tussen de volgende methode en degene die hierboven is gepost:

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

Veel plezier met coderen!


Antwoord 3, autoriteit 5%

Ervan uitgaande dat we het hebben over het verkrijgen van de applicatiecontext, heb ik het geïmplementeerd zoals voorgesteld door @Rohit Ghatol om de applicatie uit te breiden. Wat er toen gebeurde, is dat er geen garantie is dat de context die op zo’n manier wordt opgehaald, altijd niet-nul zal zijn. Op het moment dat je het nodig hebt, is het meestal omdat je een helper wilt initialiseren, of een hulpmiddel wilt krijgen, dat je het niet op tijd kunt uitstellen; het behandelen van de nulzaak zal u niet helpen.
Dus ik begreep dat ik in feite vocht tegen de Android-architectuur, zoals vermeld in de docs

Opmerking: er is normaal gesproken niet nodig om de toepassing te subclass. In de meeste situaties kunnen statische singletons dezelfde functionaliteit op een meer modulaire manier bieden. Als uw Singleton een globale context nodig heeft (bijvoorbeeld om uitzendontvangers te registreren), bijvoorbeeld context.getapplicationContext () als een contextargument bij het aanroepen van de methode van uw Singleton ()

en uitgelegd door Dianne Hackborn

De enige reden die applicatie bestaat als iets waar je vandaan kunt komen, is omdat tijdens de pre-1.0-ontwikkeling een van onze applicatieontwikkelaars me voortdurend lastig valt over het behoeften van een toepassingsobject op het hoogste niveau dat ze kunnen ontlenen meer “normaal” aan hen Applicatiemodel, en ik gaf uiteindelijk in.
Ik zal voor altijd spijt hebben om die op te geven. 🙂

Suggereert ze ook de oplossing voor dit probleem:

Als wat je wilt is een wereldwijde staat die kan worden gedeeld in verschillende delen van je app, een singleton gebruiken. […] en dit leidt natuurlijker op hoe u deze dingen moet beheren – initialiseren ze op aanvraag.

Dus wat ik deed was het ontdoen van uitbreiding van de applicatie en passeer de context rechtstreeks op de getinstance van de Singleton Helper (), terwijl een verwijzing naar de context van het toepassingscontext in de particuliere constructeur is geplaatst:

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

de beller geeft dan een lokale context door aan de helper:

Helper.getInstance(myCtx).doSomething();

Dus, om deze vraag goed te beantwoorden: er zijn manieren om statisch toegang te krijgen tot de applicatiecontext, maar ze moeten allemaal worden ontmoedigd, en u zou er de voorkeur aan geven een lokale context door te geven aan getInstance() van de singleton.


Voor iedereen die geïnteresseerd is, kun je een meer gedetailleerde versie lezen op fwd blog


Antwoord 4, autoriteit 4%

Nee, ik denk niet dat die er is. Helaas zit je vast met het aanroepen van getApplicationContext()vanuit Activityof een van de andere subklassen van context. Ook is dezevraag enigszins gerelateerd.


Antwoord 5, autoriteit 3%

Hier is een ongedocumenteerdemanier om een Applicatie te krijgen (wat een context is) van overal in de UI-thread. Het is gebaseerd op de verborgen statische methode ActivityThread.currentApplication(). Het zou in ieder geval moeten werken op 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
}

Merk op dat het mogelijk is voor deze methode om null terug te keren, b.v. Wanneer u de werkwijze buiten de UI-schroefdraad belt, of de toepassing niet aan de draad is gebonden.

Het is nog beter om @rohitghatol oplossing te gebruiken als u de applicatecode kunt wijzigen.


Antwoord 6, Autoriteit 2%

Het hangt af van wat u de context gebruikt. Ik kan denken aan ten minste één nadeel aan die methode:

Als u probeert een AlertDialogte maken met AlertDialog.Builder, zal de ApplicationContext niet werken. Ik geloof dat je de context nodig hebt voor de huidige Activity


Antwoord 7, Autoriteit 2%

Kotlin Way :

manifest:

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

MyAPPLICATIE.KT

class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        instance = this
    }
    companion object {
        lateinit var instance: MyApplication
            private set
    }
}

U kunt vervolgens toegang krijgen tot de accommodatie via MyApplication.instance


Antwoord 8

Als u openstaat voor het gebruik van Roboguice , kunt u de context in elk geval hebben Klasse die je wilt. Hier is een klein voorbeeld van hoe het te doen met Roboguice 2.0 (Beta 4 op het moment van dit schrijven)

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) {
        }
    }
}

Antwoord 9

Kotlin

open class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        mInstance = this
    }
    companion object {
        lateinit var mInstance: MyApp
        fun getContext(): Context? {
            return mInstance.applicationContext
        }
    }
}

en krijg Context zoals

MyApp.mInstance

of

MyApp.getContext()

Antwoord 10

Ik heb dit ooit gebruikt:

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

Dit is een geldige context die ik gebruikte bij het verkrijgen van systeemservices en werkte.

Maar ik gebruikte het alleen in framework/base-aanpassingen en heb het niet geprobeerd in Android-applicaties.

Een waarschuwingdie u moet weten: wanneer u zich registreert voor broadcast-ontvangers met deze context, werkt het niet en krijgt u:

java.lang.SecurityException: gegeven bellerpakket Android is niet actief in ProcessRecord


Antwoord 11

Als u het manifestbestand niet wilt wijzigen, kunt u de context handmatig opslaan in een statische variabele in uw initiële activiteit:

public class App {
    private static Context context;
    public static void setContext(Context cntxt) {
        context = cntxt;
    }
    public static Context getContext() {
        return context;
    }
}

En stel gewoon de context in wanneer uw activiteit (of activiteiten) starten:

// MainActivity
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // Set Context
    App.setContext(getApplicationContext());
    // Other stuff
}

Opmerking:net als alle andere antwoorden is dit een mogelijk geheugenlek.


Antwoord 12

in Kotlin produceert het plaatsen van Context/App-context in het begeleidende object nog steeds een waarschuwing Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

of als je zoiets als dit gebruikt:

   companion object {
        lateinit var instance: MyApp
    }

Het houdt de pluisjes voor de gek om het geheugenlek niet te ontdekken, de App-instantie kan nog steeds een geheugenlek veroorzaken, aangezien de toepassingsklasse en zijn afstammeling een context is.

U kunt ook een functionele interface of functionele eigenschappen gebruiken om u te helpen uw app-context te krijgen.

Maak eenvoudig een objectklasse:

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

of je zou het veiliger kunnen gebruiken met het nullable-type:

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

en voeg in je App-klasse deze regel toe:


class MyApp: Application() {
    override fun onCreate() {
        super.onCreate()
        CoreHelper.contextGetter = {
            this
        }
    }
}

en declareer in uw manifest de app-naam bij . MyApp


    <application
            android:name=".MyApp"

Als je de context wilt weten, bel dan gewoon:

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

Ik hoop dat het zal helpen.


Antwoord 13

U kunt het volgende gebruiken:

MainActivity.this.getApplicationContext();

MainActivity.java:

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

Elke andere les:

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

Antwoord 14

Volgens deze bronkunt u uw eigen context verkrijgen door ContextWrapper uit te breiden

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 voor ContextWrapper

Proxy-implementatie van Context die eenvoudig al zijn aanroepen naar een andere Context delegeert. Kan worden gesubklasseerd om gedrag te wijzigen zonder de oorspronkelijke context te wijzigen.


Antwoord 15

Ik denk dat je een body nodig hebt voor de getAppContext()methode:

public static Context getAppContext()
   return MyApplication.context; 

Antwoord 16

Ik heb zojuist een op jQuery geïnspireerd framework voor Android uitgebracht, genaamd Vapor API, dat tot doel heeft app-ontwikkeling eenvoudiger te maken.

De centrale $gevelklasseonderhoudt een WeakReference(link naar geweldige Java-blogpost hierover door Ethan Nicholas) naar de huidige Activity-context die u kunt ophalen door te bellen met:

$.act()

Een WeakReferencehandhaaft een referentie zonder te voorkomen dat de garbagecollection het oorspronkelijke object terugwint, dus u zou geen probleem moeten hebben met geheugenlekken.

Het nadeel is natuurlijk dat je het risico loopt dat $.act()null teruggeeft. Ik ben dit scenario echter nog niet tegengekomen, dus het is misschien maar een minimaal risico, het vermelden waard.

Je kunt de context ook handmatig instellen als je geen gebruik maakt van VaporActivityals je Activityklas:

$.act(Activity);

Bovendien gebruikt een groot deel van het Vapor API-framework deze opgeslagen context inherent, wat kan betekenen dat u het niet hoeft op te slaan jezelf helemaal niet als je besluit om het raamwerk te gebruiken. Bekijk de sitevoor meer informatie en voorbeelden.

Ik hoop dat dat helpt 🙂


Antwoord 17

Als je om de een of andere reden Applicatie-context in elke klasse wilt, niet alleen die welke applicatie/activiteit uitbreiden, misschien voor sommige fabrieks- of hulpklassen. Je kunt de volgende single aan je app toevoegen.

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

Initialiseer het vervolgens in uw toepassingsklasse onCreate with

GlobalAppContextSingleton.getInstance().initialize(this);

gebruik het overal door te bellen

GlobalAppContextSingleton.getInstance().getApplicationContext()

Ik raad deze benadering echter niet aan voor iets anders dan de toepassingscontext. Omdat het geheugenlekken kan veroorzaken.


Antwoord 18

Ik gebruik een variatie op het Singleton-ontwerppatroon om me hierbij te helpen.

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

Ik roep dan ApplicationContextSingleton.setContext( this );aan in mijn activity.onCreate()en ApplicationContextSingleton.setContext( null );in onDestroy();


Antwoord 19

Het antwoord van Rohit lijkt correct. Houd er echter rekening mee dat AndroidStudio’s “Instant Run” afhankelijk is van het ontbreken van static Context-kenmerken in uw code, voor zover ik weet.


Antwoord 20

Vandaag de dag is de juiste manier om contextte hebben, het gebruik van afhankelijkheidsinjectie.
Zo kan men Hilt gebruiken om context te injecteren op elke plaats waar het nodig is. Laten we zeggen dat men contextnodig heeft in een databasemanager, dan kan dit op de volgende manier worden opgelost:

Gevest in Gradle toevoegen:

implementation "com.google.dagger:hilt-android:2.35"
kapt "com.google.dagger:hilt-android-compiler:2.35"

Definieer toepassingsklasse met @HiltAndroidAppannotatie (laat het bijvoorbeeld de databasemanager injecteren):

@HiltAndroidApp
class MyApplication : Application() {
    @Inject
    lateinit var dbManager: DBManager
    override fun onCreate() {
        super.onCreate()
        dbManager.initDB()
    }
}

Definieer Database manager (laat het bijvoorbeeld ook @Singletonzijn):

@Singleton
class DBManager @Inject constructor(
    @ApplicationContext private val context: Context
) {
    fun initDB() {
        // context is avaiable
        databaseInit(context)
    }
}

En dat is het. De DBManagerkan context op de juiste manier benaderen zonder geheugenlekken.

Other episodes