Чи є спосіб отримати поточний Context
екземпляр всередині статичного методу?
Я шукаю такий спосіб, тому що я ненавиджу зберігати екземпляр "Контексту" кожного разу, коли він змінюється.
Context
, то може бути кращий спосіб розробити код.
Чи є спосіб отримати поточний Context
екземпляр всередині статичного методу?
Я шукаю такий спосіб, тому що я ненавиджу зберігати екземпляр "Контексту" кожного разу, коли він змінюється.
Context
, то може бути кращий спосіб розробити код.
Відповіді:
Зробити це:
У файлі 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()
щоб статистика вашого додатка статично.
static context
змінну як volatile
?
Більшість додатків, які хочуть зручний метод отримання контексту програми, створюють власний клас, який розширюється android.app.Application
.
Керівництво
Ви можете досягти цього, створивши клас у своєму проекті, наприклад:
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();
}
УВАГА
Перш ніж додати щось подібне до свого проекту, слід подумати про те, що йдеться в документації:
Зазвичай немає необхідності в підкласі Application. У більшості випадків статичні одинаки можуть надавати однакові функціональні можливості більш модульним способом. Якщо вашому одиночному потрібен глобальний контекст (наприклад, для реєстрації приймачів широкомовної передачі), функція для його отримання може бути надана Контексту, який внутрішньо використовує 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);
}
Щасливого кодування!
Припускаючи, що ми говоримо про отримання контексту програми, я реалізував його, як запропонував @Rohit Ghatol, що розширює додаток. Що сталося тоді, це те, що немає гарантії, що отриманий таким чином контекст завжди буде недійсним. У той час, коли вам це потрібно, ви не можете затриматись у часі через те, що ви хочете ініціалізувати помічника або отримати ресурс; поводження з нульовою справою вам не допоможе. Тож я зрозумів, що в основному борюся проти архітектури Android, як зазначено в документах
Примітка: Зазвичай не потрібно підкласових додатків. У більшості ситуацій статичні одиночні кнопки можуть забезпечувати однакову функціональність більш модульним способом. Якщо для вашого одиночного потрібен глобальний контекст (наприклад, для реєстрації широкомовних приймачів), включіть Context.getApplicationContext () як аргумент контексту при виклику методу getInstance () вашого одиночного.
і пояснила Діанна Хакборн
Єдина причина, що Програма існує як щось, з чого можна отримати, - це те, що під час розробки до 1,0 розробник одного з наших додатків постійно переслідував мене про необхідність мати об'єкт додатка вищого рівня, з якого вони можуть виходити, щоб вони могли мати більш "нормальну" "для них модель застосування, і я врешті-решт поступився. Я назавжди пошкодую, що віддав цю програму. :)
Вона також пропонує вирішити цю проблему:
Якщо ви хочете, щоб це було деяким глобальним станом, яке можна ділити в різних частинах вашої програми, скористайтеся однотонним. [...] І це природніше призводить до того, як вам слід керувати цими речами - ініціалізуючи їх на вимогу.
так що я зробив позбавлення від розширення програми і передав контекст безпосередньо до getInstance помічника одиночки (), зберігаючи посилання на контекст програми в приватному конструкторі:
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
getInstance(ctx)
. У вас є корінь instance
типу GC MyHelper
, який має приватне поле mContext
типу Context
, яке посилається на контекст програми, зібраний через переданий контекст getInstance()
. instance
ніколи не встановлюється вдруге і не очищається, тому GC ніколи не вловлює додаток контексту, на який посилається instance
. Ви не просочуєте жодної діяльності, тому це низька вартість IMO.
this
в Application.onCreate()
, що робить прийняту відповідь кращою.
Ні, я не думаю, що існує. На жаль, вам застрягли дзвінки getApplicationContext()
з Activity
одного чи іншого підкласу Context
. Також це питання дещо пов’язане.
Ось бездокументований спосіб отримати додаток (який є контекстом) з будь-якої точки потоку інтерфейсу. Він спирається на прихований статичний метод 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
}
Зауважте, що цей метод може повернути нуль, наприклад, коли ви викликаєте метод поза потоком інтерфейсу або програма не пов'язана з потоком.
Ще краще використовувати рішення @RohitGhatol , якщо ви можете змінити код програми.
Це залежить від того, для чого ви використовуєте контекст. Я можу придумати хоча б один недолік цього методу:
Якщо ви намагаєтесь створити за AlertDialog
допомогою AlertDialog.Builder
, Application
контекст не працюватиме. Я вважаю, що вам потрібен контекст для поточного Activity
...
Котлінський шлях :
Маніфест:
<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
Якщо ви відкриті для використання 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) {
}
}
}
Я використовував це в якийсь момент:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Це дійсний контекст, який я використовував при отриманні системних послуг і працював.
Але я використовував його лише в модифікаціях фреймворку / бази, і не пробував цього в додатках для Android.
Попередження , що ви повинні знати: При реєстрації для радіомовних приймачів з цим контекстом, він не буде працювати , і ви отримаєте:
java.lang.SecurityException: Даний пакет виклику андроїд не запускається в процесі ProcessRecord
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()
Ви можете використовувати наступне:
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();
Якщо ви не хочете змінювати файл маніфесту, ви можете вручну зберігати контекст у статичній змінній у вашій початковій діяльності:
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
}
Примітка. Як і всі інші відповіді, це потенційний витік пам'яті.
Відповідно до цього джерела ви можете отримати власний контекст, розширивши 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();
}
}
Шкільна реалізація контексту, яка просто делегує всі свої виклики іншому контексту. Можна підкласифікувати для зміни поведінки без зміни вихідного контексту.
Якщо ви чомусь хочете контекст програми в будь-якому класі, а не тільки в тих, що розширюють додаток / діяльність, можливо, для деяких заводських чи допоміжних класів. Ви можете додати в додаток наступний синглтон.
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()
Я не рекомендую цей підхід ні до чого, крім контексту програми. Оскільки це може спричинити витік пам'яті.
Я використовую варіацію шаблону дизайну Singleton, щоб допомогти мені в цьому.
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 );
у свою активність.onCreate () та ApplicationContextSingleton.setContext( null );
в onDestroy () ;
Щойно я випустив натхненну jQuery рамку для Android під назвою API Vapor, яка має на меті спростити розробку додатків.
Клас центрального $
фасаду підтримує WeakReference
(посилання на дивовижну публікацію в блозі Java про це Етаном Ніколасом) до поточного Activity
контексту, який ви можете знайти, зателефонувавши:
$.act()
A WeakReference
підтримує посилання, не перешкоджаючи збору сміття відновлювати оригінальний об'єкт, тому у вас не повинно виникнути проблем із витоками пам'яті.
Мінус, звичайно, полягає в тому, що ви ризикуєте $.act()
повернути нуль. Я ще не стикався з цим сценарієм, тому, можливо, це просто мінімальний ризик, який варто згадати.
Ви також можете встановити контекст вручну, якщо ви не використовуєте VaporActivity
в якості Activity
класу:
$.act(Activity);
Крім того, більша частина фреймворку API Vapor по суті використовує цей збережений контекст, що може означати, що вам зовсім не потрібно зберігати його, якщо ви вирішили використовувати фреймворк. Відвідайте сайт для отримання додаткової інформації та зразків.
Я сподіваюся, що це допомагає :)
Відповідь Рохіта здається правильною. Однак майте на увазі, що "Миттєвий запуск" AndroidStudio залежить від того, static Context
що у вашому коді немає атрибутів, наскільки я знаю.
у Котліні, розміщення Context / App Context в супутнім об'єкті все ще створює попередження 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
}
або ви могли б безпечніше використовувати його, використовуючи зведений тип:
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()
Сподіваюся, це допоможе.
Спробуйте щось подібне
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(); } }