Cada vez que se ejecuta mi transmisión, quiero mostrar alerta a la actividad en primer plano.
Cada vez que se ejecuta mi transmisión, quiero mostrar alerta a la actividad en primer plano.
Respuestas:
Sabiendo que ActivityManager administra Activity , entonces podemos obtener información de ActivityManager . Obtenemos la actividad actual en primer plano ejecutando
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
ACTUALIZACIÓN 2018/10/03
getRunningTasks () está DEPRECADO. ver las soluciones a continuación.
Este método fue desaprobado en el nivel 21 de API. A partir de Build.VERSION_CODES.LOLLIPOP, este método ya no está disponible para aplicaciones de terceros: la introducción de recientes documentos centrados significa que puede filtrar información de la persona a la persona que llama. Para la compatibilidad con versiones anteriores, aún devolverá un pequeño subconjunto de sus datos: al menos las tareas propias de la persona que llama, y posiblemente algunas otras tareas como el hogar que se sabe que no son sensibles.
( Nota: se agregó una API oficial en la API 14: consulte esta respuesta https://stackoverflow.com/a/29786451/119733 )
NO USE la respuesta ANTERIOR (waqas716).
Tendrá un problema de pérdida de memoria debido a la referencia estática a la actividad. Para obtener más detalles, consulte el siguiente enlace http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
Para evitar esto, debe administrar las referencias de actividades. Agregue el nombre de la aplicación en el archivo de manifiesto:
<application
android:name=".MyApp"
....
</application>
Su clase de aplicación:
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;
}
}
Crea una nueva actividad:
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);
}
}
Entonces, ahora en lugar de extender la clase de Actividad para sus actividades, simplemente extienda MyBaseActivity. Ahora, puede obtener su actividad actual de la aplicación o el contexto de Actividad de esa manera:
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
WeakReferences
en Android, el GC los recopila más rápido de lo que piensas.
WeakReference
no se recomienda para el almacenamiento en caché, esto no es almacenamiento en caché, es decir mCurrentActivity
, solo tendrá una referencia cuando esté vivo, por lo WeakReference
que nunca se recolectará mientras Activity
esté en la parte superior. Sin embargo, lo que sugiere @NachoColoma es incorrecto porque WeakReference
aún puede hacer referencia a una actividad no reanudada (no activa / no superior) si la variable no se borra.
Application .ActivityLifecycleCallbacks
, lo que sería más central y no tendría que agregar ningún código de administración en todas sus actividades. Ver también developer.android.com/reference/android/app/…
Me expando en la parte superior de la respuesta de @ gezdy.
En cada actividad, en lugar de tener que "registrarse" Application
con la codificación manual, podemos utilizar la siguiente API desde el nivel 14, para ayudarnos a lograr un propósito similar con menos codificación manual.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
En Application.ActivityLifecycleCallbacks
, puede obtener cuál Activity
está "adjunto" o "desconectado" a esto Application
.
Sin embargo, esta técnica solo está disponible desde el nivel API 14.
implements Application.ActivityLifecycleCallbacks
, y agrega los métodos para implementar eso. Luego, en el constructor de esa clase (o onCreate o init u otro método que se ejecuta cuando la instancia se vuelve activa / lista), coloque getApplication().registerActivityLifecycleCallbacks(this);
como la última línea.
Actualización 2 : hay una API oficial agregada para esto, utilice ActivityLifecycleCallbacks en su lugar.
ACTUALIZAR:
Como señaló @gezdy, y estoy agradecido por eso. establezca la referencia a nulo también para la actividad actual, en lugar de actualizarse en cada onResume, configúrelo como nulo en cada onDestroy de cada actividad para evitar problemas de pérdida de memoria.
Hace un tiempo necesitaba la misma funcionalidad y aquí está el método para lograrlo. En cada una de sus actividades, anule estos métodos del ciclo de vida.
@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);
}
Ahora en su clase de transmisión puede acceder a la actividad actual para mostrar alertas en ella.
Application
solo se crea una vez y nunca se recolecta basura exactamente como una variable estática.
clearReferences()
que (this.equals(currActivity))
.
@lockwobr Gracias por la actualización
Esto no funciona el 100% del tiempo en la versión 16 de la API, si lees el código en github, la función "currentActivityThread" se modificó en Kitkat, por lo que quiero decir que la versión 19ish, un poco difícil de igualar a la versión de la API para lanzamientos en github .
Tener acceso a la corriente Activity
es muy útil. ¿No sería bueno tener un getActivity
método estático que devuelva la Actividad actual sin preguntas innecesarias?
La Activity
clase es muy útil. Da acceso al hilo de la interfaz de usuario de la aplicación, vistas, recursos y muchos más. Numerosos métodos requieren un Context
, pero ¿cómo obtener el puntero? Aquí hay algunas maneras:
ActivityThread
. Esta clase tiene acceso a todas las actividades y, lo que es aún mejor, tiene un método estático para obtener la corriente ActivityThread
. Solo hay un pequeño problema: la lista de actividades tiene acceso a paquetes.Fácil de resolver usando la reflexión:
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;
}
Tal método se puede usar en cualquier lugar de la aplicación y es mucho más conveniente que todos los enfoques mencionados. Además, parece que no es tan inseguro como parece. No introduce nuevas fugas potenciales o punteros nulos.
El fragmento de código anterior carece de manejo de excepciones y asume ingenuamente que la primera Actividad en ejecución es la que estamos buscando. Es posible que desee agregar algunas comprobaciones adicionales.
Map
interfaz en su lugar HashMap
o ArrayMap
. He editado @AZ_ answer.
Hice lo siguiente en Kotlin
Edite la clase de aplicación como sigue
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
}
}
}
Crear la clase 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
}
}
ahora puede usarlo en cualquier clase llamando a lo siguiente: FTApplication.currentActivity()
getCurrentActivity () también está en ReactContextBaseJavaModule.
(Dado que esta pregunta se hizo inicialmente, muchas aplicaciones de Android también tienen el componente ReactNative: aplicación híbrida).
La clase ReactContext en ReactNative tiene todo el conjunto de lógica para mantener mCurrentActivity que se devuelve en getCurrentActivity ().
Nota: Deseo que getCurrentActivity () se implemente en la clase de aplicación de Android.
No pude encontrar una solución con la que nuestro equipo estaría contento, así que desarrollamos la nuestra. Usamos ActivityLifecycleCallbacks
para hacer un seguimiento de la actividad actual y luego exponerla a través de un servicio. Más detalles aquí: https://stackoverflow.com/a/38650587/10793
Para compatibilidad con versiones anteriores:
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
identificador de una Application
clase, mientras que ComponentName
se requiere para determinar si el deseado Activity
está en la parte superior de la lista de tareas en ejecución. Y si esto no responde completamente la pregunta, la respuesta aceptada tampoco.
topActivity
está disponible solo desde Android Q
Personalmente hice lo que dijo "Cheok Yan Cheng", pero usé una "Lista" para tener un "Backstack" de todas mis actividades.
Si desea verificar cuál es la actividad actual, solo necesita obtener la última clase de actividad en la lista.
Cree una aplicación que extienda "Aplicación" y haga esto:
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() }
}
}
En mi caso usé "Application.ActivityLifecycleCallbacks" para:
Enlace / Desvincular instancia de Merlin (se usa para obtener eventos cuando la aplicación pierde u obtiene conexión, por ejemplo, cuando cierra datos móviles o cuando los abre). Es útil después de que se deshabilitó la acción de intención "OnConnectivityChanged". Para más información sobre MERLIN ver: MERLIN INFO LINK
Cerrar mi última instancia de reino cuando la aplicación está cerrada; Lo iniciaré dentro de una BaseActivity que se extiende desde todas las demás actividades y que tiene una instancia privada RealmHelper. Para obtener más información sobre REALM, consulte: REALM INFO LINK. Por ejemplo, tengo una instancia estática "RealmHelper" dentro de mi clase "RealmHelper", que se instancia dentro de mi aplicación "onCreate". Tengo un servicio de sincronización en el que creo un nuevo "RealmHelper" porque Realm está "enlazado a hilos" y una instancia de reino no puede funcionar dentro de un hilo diferente. Entonces, para seguir la documentación del reino "Debe cerrar todas las instancias de reino abiertas para evitar fugas de recursos del sistema", para lograr esto utilicé "Application.ActivityLifecycleCallbacks" como puede ver.
Finalmente, tengo un receptor que se dispara cuando termino de sincronizar mi aplicación, luego, cuando finaliza la sincronización, llamará al método "onEndSync" "IEndSyncCallback" en el que busco si tengo una Clase de Actividad específica dentro de mi Lista de Actividades BackStack porque necesito para actualizar los datos en la vista si la sincronización los actualizó y podría necesitar hacer otras operaciones después de la sincronización de la aplicación.
Eso es todo, espero que esto sea útil. Nos vemos :)
La respuesta de waqas716 es buena. Creé una solución para un caso específico que exige menos código y mantenimiento.
Encontré una solución específica al hacer que un método estático obtenga una vista de la actividad que sospecho que está en primer plano. Puede recorrer todas las actividades y verificar si lo desea u obtener el nombre de la actividad de la respuesta de Martin
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Luego verifico si la vista no es nula y obtengo el contexto a través de 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, ..."
en developer.android.com/reference/android/app/…
No me gusta ninguna de las otras respuestas. El ActivityManager no está destinado a usarse para obtener la actividad actual. Super clasificación y dependiendo de onDestroy también es frágil y no es el mejor diseño.
Honestamente, lo mejor que se me ocurrió hasta ahora es mantener una enumeración en mi aplicación, que se establece cuando se crea una actividad.
Otra recomendación podría ser evitar usar múltiples actividades si es posible. Esto se puede hacer con el uso de fragmentos o en mis vistas personalizadas de preferencia.
Una solución bastante simple es crear una clase de administrador singleton, en la que pueda almacenar una referencia a una o más Actividades, o cualquier otra cosa a la que desee acceder en toda la aplicación.
Llama UberManager.getInstance().setMainActivity( activity );
a onCreate de la actividad principal.
Llame a UberManager.getInstance().getMainActivity();
cualquier parte de su aplicación para recuperarla. (Estoy usando esto para poder usar Toast desde un hilo no UI).
Asegúrate de agregar una llamada UberManager.getInstance().cleanup();
cuando se destruya tu aplicación.
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;
}
}
Llego como 3 años tarde pero lo responderé de todos modos en caso de que alguien encuentre esto como yo.
Resolví esto simplemente usando esto:
if (getIntent().toString().contains("MainActivity")) {
// Do stuff if the current activity is MainActivity
}
Tenga en cuenta que "getIntent (). ToString ()" incluye muchos otros textos, como el nombre de su paquete y cualquier filtro de intención para su actividad. Técnicamente estamos verificando la intención actual, no la actividad, pero el resultado es el mismo. Simplemente use, por ejemplo, Log.d ("prueba", getIntent (). ToString ()); si quieres ver todo el texto Esta solución es un poco hacky pero es mucho más limpia en su código y la funcionalidad es la misma.