Всякий раз, когда моя трансляция выполняется, я хочу показывать предупреждение переднего плана деятельности.
Всякий раз, когда моя трансляция выполняется, я хочу показывать предупреждение переднего плана деятельности.
Ответы:
Зная, что ActivityManager управляет Activity , мы можем получать информацию из ActivityManager . Мы получаем текущий передний план, запускающий Activity by
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
ОБНОВЛЕНИЕ 2018/10/03
getRunningTasks () УСТАРЕЛО. увидеть решения ниже.
Этот метод устарел на уровне API 21. Начиная с версии Build.VERSION_CODES.LOLLIPOP, этот метод больше не доступен для сторонних приложений: введение центрированных на документе рецентов означает, что он может передавать информацию о человеке вызывающей стороне. Для обратной совместимости он по-прежнему будет возвращать небольшое подмножество своих данных: по крайней мере, собственные задачи вызывающего абонента и, возможно, некоторые другие задачи, например домашние, которые, как известно, не являются чувствительными.
( Примечание: официальный API был добавлен в API 14: см. Этот ответ 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);
}
}
Итак, теперь вместо расширения класса Activity для ваших действий просто расширьте 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
.
Однако этот метод доступен только с уровня API 14.
implements Application.ActivityLifecycleCallbacks
и добавляете методы для его реализации. Затем в конструкторе этого класса (или onCreate, или init, или другом методе, который запускается, когда экземпляр становится активным / готовым), поместите getApplication().registerActivityLifecycleCallbacks(this);
в качестве последней строки.
Обновление 2 : для этого добавлен официальный API, пожалуйста, используйте взамен ActivityLifecycleCallbacks .
ОБНОВИТЬ:
Как указано @gezdy, и я благодарен за это. установите ссылку на null тоже для текущей активности, вместо обновления только для каждого onResume, установите для нее значение null в 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.
Я не мог найти решение, которым наша команда была бы довольна, поэтому мы сделали свое дело. Мы используем, 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» во всех моих действиях.
Если вы хотите проверить, что является текущей активностью, вам просто нужно получить последний класс активности в списке.
Создайте приложение, которое расширяет «Приложение», и сделайте это:
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», чтобы:
Bind / Unbind Merlin Instance (используется для получения события, когда приложение теряет или получает соединение, например, когда вы закрываете мобильные данные или когда вы открываете его). Это полезно после того, как намеренное действие «OnConnectivityChanged» было отключено. Для получения дополнительной информации о MERLIN см .: MERLIN INFO LINK
Закрыть мой последний экземпляр области, когда приложение закрыто; Я начну его внутри BaseActivity, которое расширено от всех других действий и имеет частный экземпляр RealmHelper. Для получения дополнительной информации о REALM см .: REALM INFO LINK. Например, у меня есть статический экземпляр «RealmHelper» внутри моего класса «RealmHelper», который создается внутри моего приложения «onCreate». У меня есть служба синхронизации, в которой я создаю новый «RealmHelper», потому что Realm «связан с потоками», а экземпляр Realm не может работать в другом потоке. Поэтому для того, чтобы следовать Документации области «Вам необходимо закрыть все открытые экземпляры области, чтобы избежать утечки системных ресурсов», для достижения этой цели я использовал «Application.ActivityLifecycleCallbacks», как вы можете видеть вверх.
Наконец, у меня есть получатель, который запускается, когда я заканчиваю синхронизацию своего приложения, затем, когда заканчивается синхронизация, он вызывает метод «IEndSyncCallback» «onEndSync», в котором я проверяю, есть ли у меня определенный класс Activity в моем списке 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, ..."
в developer.android.com/reference/android/app/...
Мне не нравятся другие ответы. ActivityManager не предназначен для получения текущей активности. Суперклассификация и в зависимости от onDestroy также хрупкая и не лучший дизайн.
Честно говоря, лучшее, что я придумал до сих пор, это просто поддержание перечисления в моем приложении, которое устанавливается при создании действия.
Другая рекомендация может заключаться в том, чтобы просто избегать использования нескольких видов деятельности, если это возможно. Это может быть сделано либо с использованием фрагментов, либо в моих предпочтениях пользовательских представлений.
Довольно простое решение - создать класс синглтон-менеджера, в котором вы можете хранить ссылку на одно или несколько действий или что-то еще, к чему вы хотите получить доступ во всем приложении.
Позвоните UberManager.getInstance().setMainActivity( activity );
в основной деятельности по созданию.
Звоните в UberManager.getInstance().getMainActivity();
любое место вашего приложения, чтобы получить его. (Я использую это, чтобы иметь возможность использовать тост из потока, не являющегося пользовательским интерфейсом.)
Убедитесь, что вы добавляете вызов, 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 ("test", getIntent (). ToString ()); если вы хотите увидеть весь текст. Это решение немного хакерское, но оно намного чище в вашем коде и функциональность та же.