Щоразу, коли моя трансляція виконується, я хочу показувати активність переднього плану.
Щоразу, коли моя трансляція виконується, я хочу показувати активність переднього плану.
Відповіді:
Знаючи, що ActivityManager управляє активністю , ми можемо отримувати інформацію від ActivityManager . Ми отримуємо поточну активність переднього плану за допомогою
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
ОНОВЛЕННЯ 2018/10/03
getRunningTasks () ВИДАЛЕНО . дивіться рішення нижче.
Цей метод був застарілий на рівні 21 API. Що стосується Build.VERSION_CODES.LOLLIPOP, цей спосіб більше не доступний для сторонніх додатків: введення документів, орієнтованих на документ, означає, що він може витікати інформацію про особу, що телефонує. Для зворотної сумісності він все одно поверне невелику підмножину своїх даних: принаймні власні завдання абонента та, можливо, деякі інші завдання, наприклад, домашні, які, як відомо, не чутливі.
( Примітка. У API 14 додано офіційний API: див. Цю відповідь https://stackoverflow.com/a/29786451/119733 )
НЕ ВИКОРИСТУЙТЕ попередню (waqas716) відповідь.
У вас виникне проблема з витоком пам’яті через статичне посилання на активність. Більш детально див. Посилання http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
Щоб цього уникнути, вам слід керувати посиланнями на діяльність. Додайте ім’я програми у файл маніфесту:
<application
android:name=".MyApp"
....
</application>
Ваш клас заявки:
public class MyApp extends Application {
public void onCreate() {
super.onCreate();
}
private Activity mCurrentActivity = null;
public Activity getCurrentActivity(){
return mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity){
this.mCurrentActivity = mCurrentActivity;
}
}
Створіть нову активність:
public class MyBaseActivity extends Activity {
protected MyApp mMyApp;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mMyApp = (MyApp)this.getApplicationContext();
}
protected void onResume() {
super.onResume();
mMyApp.setCurrentActivity(this);
}
protected void onPause() {
clearReferences();
super.onPause();
}
protected void onDestroy() {
clearReferences();
super.onDestroy();
}
private void clearReferences(){
Activity currActivity = mMyApp.getCurrentActivity();
if (this.equals(currActivity))
mMyApp.setCurrentActivity(null);
}
}
Отже, тепер замість того, щоб розширювати клас активності для вашої діяльності, просто розгорніть MyBaseActivity. Тепер ви можете отримати свою поточну активність із програми чи контексту діяльності, наприклад:
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
WeakReferences
в Android, GC збирає їх швидше, ніж ви думаєте.
WeakReference
не рекомендується для кешування, це не кешування, це mCurrentActivity
буде мати посилання на нього лише тоді, коли він живий, тому WeakReference
ніколи не буде зібрано, поки Activity
на вершині. Однак те, що @NachoColoma припускає, є неправильним, оскільки WeakReference
може все-таки посилатися на не відновлену (не живу / не вгорі) діяльність, якщо змінна не очищена!
Application .ActivityLifecycleCallbacks
, яке було б більш центральним, і вам не доведеться додавати будь-який код управління у всіх ваших заходах. Також дивіться developer.android.com/reference/android/app/…
Я розгортаюсь у верхній частині відповіді @ gezdy.
У всіх видах діяльності, замість того, щоб "реєструватись" за Application
допомогою ручного кодування, ми можемо використовувати наступний API з рівня 14, щоб допомогти нам досягти подібної мети з меншим кодуванням вручну.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
У цьому Application.ActivityLifecycleCallbacks
ви можете отримати, який Activity
"прикріплений" до цього або "відокремлений" Application
.
Однак ця методика доступна лише з рівня 14 API.
implements Application.ActivityLifecycleCallbacks
і додаєте методи для його реалізації. Потім у цьому конструкторі класу (або onCreate, init чи інший метод, який запускається, коли екземпляр стає активним / готовим), помістіть getApplication().registerActivityLifecycleCallbacks(this);
як останній рядок.
Оновлення 2 : Для цього додано офіційне api, замість цього використовуйте ActivityLifecycleCallbacks .
ОНОВЛЕННЯ:
Як вказував @gezdy, і я за це вдячний. встановіть посилання на null теж для поточної активності, замість оновлення лише кожного onResume встановіть його на нуль для кожного onDestroy діяльності, щоб уникнути проблеми з витоком пам'яті.
Деякий час тому мені потрібна була та сама функціональність, і ось метод, як я цього досяг. У кожній вашій діяльності переважають ці методи життєвого циклу.
@Override
protected void onResume() {
super.onResume();
appConstantsObj.setCurrentActivity(this);
}
@Override
protected void onPause() {
clearReferences();
super.onPause();
}
@Override
protected void onDestroy() {
clearReferences();
super.onDestroy();
}
private void clearReferences(){
Activity currActivity = appConstantsObj.getCurrentActivity();
if (this.equals(currActivity))
appConstantsObj.setCurrentActivity(null);
}
Тепер у вашому класі трансляції ви можете отримати доступ до поточної активності, щоб показати сповіщення про неї.
Application
створюється лише один раз і ніколи не збирається сміття точно як статична змінна.
clearReferences()
до (this.equals(currActivity))
.
@lockwobr Дякуємо за оновлення
Це не працює 100% часу в api версії 16, якщо ви читаєте код на github, функція "currentActivityThread" була змінена в Kitkat, тому я хочу сказати, що версія 19ish, начебто складна відповідність версії api до випусків у github .
Доступ до струму Activity
дуже зручний. Не було б непогано мати статичний getActivity
метод, що повертає поточну активність без зайвих питань?
Activity
Клас дуже корисний. Він надає доступ до потоку інтерфейсу користувача, представлень даних, ресурсів та багатьох інших. Численні методи вимагають Context
, але як отримати вказівник? Ось кілька способів:
ActivityThread
. Цей клас має доступ до всіх видів діяльності і, що ще краще, має статичний метод отримання струму ActivityThread
. Є лише одна невелика проблема - до списку активності доступний пакет.Легко вирішити за допомогою рефлексії:
public static Activity getActivity() {
Class activityThreadClass = Class.forName("android.app.ActivityThread");
Object activityThread = activityThreadClass.getMethod("currentActivityThread").invoke(null);
Field activitiesField = activityThreadClass.getDeclaredField("mActivities");
activitiesField.setAccessible(true);
Map<Object, Object> activities = (Map<Object, Object>) activitiesField.get(activityThread);
if (activities == null)
return null;
for (Object activityRecord : activities.values()) {
Class activityRecordClass = activityRecord.getClass();
Field pausedField = activityRecordClass.getDeclaredField("paused");
pausedField.setAccessible(true);
if (!pausedField.getBoolean(activityRecord)) {
Field activityField = activityRecordClass.getDeclaredField("activity");
activityField.setAccessible(true);
Activity activity = (Activity) activityField.get(activityRecord);
return activity;
}
}
return null;
}
Такий метод можна використовувати будь-де в додатку, і це набагато зручніше, ніж усі згадані підходи. Більше того, здається, що це не так небезпечно, як виглядає. Він не вводить нових потенційних витоків або нульових покажчиків.
У наведеному вище фрагменті коду не вистачає поводження з винятками і наївно передбачається, що перший запуск діяльності - це той, кого ми шукаємо. Ви можете додати кілька додаткових чеків.
Map
інтерфейс HashMap
або ArrayMap
. Я відредагував відповідь @AZ_.
Я робив наступні в Котліні
Відредагуйте клас додатків, як описано нижче
class FTApplication: MultiDexApplication() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
MultiDex.install(this)
}
init {
instance = this
}
val mFTActivityLifecycleCallbacks = FTActivityLifecycleCallbacks()
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(mFTActivityLifecycleCallbacks)
}
companion object {
private var instance: FTApplication? = null
fun currentActivity(): Activity? {
return instance!!.mFTActivityLifecycleCallbacks.currentActivity
}
}
}
Створіть клас ActivityLifecycleCallbacks
class FTActivityLifecycleCallbacks: Application.ActivityLifecycleCallbacks {
var currentActivity: Activity? = null
override fun onActivityPaused(activity: Activity?) {
currentActivity = activity
}
override fun onActivityResumed(activity: Activity?) {
currentActivity = activity
}
override fun onActivityStarted(activity: Activity?) {
currentActivity = activity
}
override fun onActivityDestroyed(activity: Activity?) {
}
override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
}
override fun onActivityStopped(activity: Activity?) {
}
override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
currentActivity = activity
}
}
тепер ви можете використовувати його в будь-якому класі, зателефонувавши за цим: FTApplication.currentActivity()
getCurrentActivity () також знаходиться в ReactContextBaseJavaModule.
(Оскільки це питання було спочатку задано, у багатьох додатках для Android також є компонент ReactNative - гібридне додаток.)
клас ReactContext в ReactNative має весь набір логіки для підтримки mCurrentActivity, який повертається в getCurrentActivity ().
Примітка. Я хочу, щоб getCurrentActivity () реалізований у класі Android Application.
Я не міг знайти рішення, яке б задовольнило нашу команду, тож ми прокатали своє. Ми використовуємо ActivityLifecycleCallbacks
для відстеження поточної діяльності, а потім розкриваємо її через сервіс. Детальніше тут: https://stackoverflow.com/a/38650587/10793
Для зворотної сумісності:
ComponentName cn;
ActivityManager am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
cn = am.getAppTasks().get(0).getTaskInfo().topActivity;
} else {
//noinspection deprecation
cn = am.getRunningTasks(1).get(0).topActivity;
}
WeakReference
обробку від Application
класу - в той час, ComponentName
як потрібно визначити, чи бажане Activity
знаходиться в списку запущених завдань. І якщо це не дає повною мірою відповіді на питання, прийнята відповідь також не відповідає.
topActivity
доступний лише з Android Q
Особисто я робив так, як сказав "Чек Ян Ченг", але я використав "Список", щоб мати "Backstack" усієї своєї діяльності.
Якщо ви хочете перевірити, що таке поточна активність, вам просто потрібно отримати останній клас активності у списку.
Створіть додаток, який розширює "Application", і виконайте це:
public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks,
EndSyncReceiver.IEndSyncCallback {
private List<Class> mActivitiesBackStack;
private EndSyncReceiver mReceiver;
private Merlin mMerlin;
private boolean isMerlinBound;
private boolean isReceiverRegistered;
@Override
public void onCreate() {
super.onCreate();
[....]
RealmHelper.initInstance();
initMyMerlin();
bindMerlin();
initEndSyncReceiver();
mActivitiesBackStack = new ArrayList<>();
}
/* START Override ActivityLifecycleCallbacks Methods */
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
mActivitiesBackStack.add(activity.getClass());
}
@Override
public void onActivityStarted(Activity activity) {
if(!isMerlinBound){
bindMerlin();
}
if(!isReceiverRegistered){
registerEndSyncReceiver();
}
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
if(!AppUtils.isAppOnForeground(this)){
if(isMerlinBound) {
unbindMerlin();
}
if(isReceiverRegistered){
unregisterReceiver(mReceiver);
}
if(RealmHelper.getInstance() != null){
RealmHelper.getInstance().close();
RealmHelper.getInstance().logRealmInstanceCount("AppInBackground");
RealmHelper.setMyInstance(null);
}
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
if(mActivitiesBackStack.contains(activity.getClass())){
mActivitiesBackStack.remove(activity.getClass());
}
}
/* END Override ActivityLifecycleCallbacks Methods */
/* START Override IEndSyncCallback Methods */
@Override
public void onEndSync(Intent intent) {
Constants.SyncType syncType = null;
if(intent.hasExtra(Constants.INTENT_DATA_SYNC_TYPE)){
syncType = (Constants.SyncType) intent.getSerializableExtra(Constants.INTENT_DATA_SYNC_TYPE);
}
if(syncType != null){
checkSyncType(syncType);
}
}
/* END IEndSyncCallback Methods */
private void checkSyncType(Constants.SyncType){
[...]
if( mActivitiesBackStack.contains(ActivityClass.class) ){
doOperation() }
}
}
У моєму випадку я використав "Application.ActivityLifecycleCallbacks" для:
Прив’язати / від’єднати екземпляр Merlin (використовується для отримання події, коли програма втрачає або отримує з'єднання, наприклад, коли ви закриваєте мобільні дані або коли їх відкриваєте). Це корисно після відключення дії наміру "OnConnectivityChanged". Для отримання додаткової інформації про MERLIN см: MERLIN INFO LINK
Закрити мій останній екземпляр Царства, коли програма закрита; Я буду запускати його всередині BaseActivity, яка розширена від усіх інших видів діяльності, яка має приватний екземпляр RealmHelper. Для отримання додаткової інформації про REALM дивіться: ПОСИЛКА ІНФОРМАЦІЇ РЕАЛМУ Наприклад, у мене є статичний екземпляр "RealmHelper" всередині мого класу "RealmHelper", який міститься в моєму додатку "onCreate". У мене є служба синхронізації, в якій я створюю новий "RealmHelper", оскільки Realm є "Thread-Link", а Realm Instance не може працювати в іншій Thread. Тому для того, щоб дотримуватися Документації Царства "Вам потрібно закрити всі відкриті екземпляри Царства, щоб уникнути витоків системних ресурсів", для цього я використав "Application.ActivityLifecycleCallbacks", як ви бачите.
Нарешті, у мене є приймач, який спрацьовує, коли я закінчую синхронізувати свою програму, тоді, коли синхронізація завершиться, вона зателефонує метод "IEndSyncCallback" "onEndSync", в якому я дивлюся, чи є у мене список активності в моєму списку ActivityBackStack, тому що мені потрібно оновити дані в представленні даних, якщо синхронізація оновила їх, і мені могло знадобитися робити інші операції після синхронізації програми.
Це все, сподіваюся, що це корисно. Бачити тебе :)
Відповідь waqas716 хороша. Я створив вирішення конкретного випадку, вимагаючи менше коду та обслуговування.
Я знайшов конкретну роботу, маючи статичний метод, щоб отримати уявлення про діяльність, яку я підозрюю, що на першому плані. Ви можете перебирати всі види діяльності і перевірити , якщо ви хочете або отримати ім'я активності від Мартіна відповіді
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Потім я перевіряю, чи представлення не нульове, і отримую контекст через getContext ().
View v = SuspectedActivity.get_view();
if(v != null)
{
// an example for using this context for something not
// permissible in global application context.
v.getContext().startActivity(new Intent("rubberduck.com.activities.SomeOtherActivity"));
}
getRunningTasks
: "Note: this method is only intended for debugging and presenting task management user interfaces. This should never be used for core logic in an application, ..."
in developer.android.com/reference/android/app/…
Мені не подобається жодна з інших відповідей. ActivityManager не призначений для використання для отримання поточної активності. Супер класифікація і залежно від Destroy також крихка і не найкращий дизайн.
Чесно кажучи, найкраще, що я придумав поки що, це просто підтримка перерахунку в моєму додатку, який встановлюється під час створення діяльності.
Ще однією рекомендацією може бути просто ухилятися від використання кількох заходів, якщо це можливо. Це можна зробити за допомогою фрагментів, або в моїх уподобаннях на власні подання.
Досить простим рішенням є створення однотонного класу менеджера, в якому ви можете зберігати посилання на один або кілька видів діяльності або будь-що інше, до чого ви хочете отримати доступ у всьому додатку.
Зателефонуйте UberManager.getInstance().setMainActivity( activity );
в основну діяльність onCreate.
Зателефонуйте в UberManager.getInstance().getMainActivity();
будь-яку частину програми, щоб отримати його. (Я використовую це, щоб мати можливість використовувати Toast з потоку, який не використовується в інтерфейсі користувача.)
Переконайтеся, що ви додали дзвінок, UberManager.getInstance().cleanup();
коли ваш додаток знищується.
import android.app.Activity;
public class UberManager
{
private static UberManager instance = new UberManager();
private Activity mainActivity = null;
private UberManager()
{
}
public static UberManager getInstance()
{
return instance;
}
public void setMainActivity( Activity mainActivity )
{
this.mainActivity = mainActivity;
}
public Activity getMainActivity()
{
return mainActivity;
}
public void cleanup()
{
mainActivity = null;
}
}
Мені подобається на 3 роки, але я все одно відповім на той випадок, якщо хтось виявить це так, як я.
Я вирішив це, просто скориставшись цим:
if (getIntent().toString().contains("MainActivity")) {
// Do stuff if the current activity is MainActivity
}
Зауважте, що "getIntent (). ToString ()" містить купу іншого тексту, наприклад, назву вашого пакета та будь-які фільтри намірів для вашої діяльності. Технічно ми перевіряємо поточний намір, а не активність, але результат той самий. Просто використовуйте, наприклад, Log.d ("тест", getIntent (). ToString ()); якщо ви хочете переглянути весь текст. Це рішення трохи хакі, але воно набагато чіткіше у вашому коді, а функціональність однакова.