Chaque fois que ma diffusion est exécutée, je souhaite afficher une alerte sur l'activité au premier plan.
Chaque fois que ma diffusion est exécutée, je souhaite afficher une alerte sur l'activité au premier plan.
Réponses:
Sachant qu'ActivityManager gère Activity , nous pouvons donc obtenir des informations d' ActivityManager . Nous obtenons l'activité courante de premier plan en cours d'exécution par
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
MISE À JOUR 2018/10/03
getRunningTasks () est obsolète. voir les solutions ci-dessous.
Cette méthode était obsolète au niveau de l'API 21. Depuis Build.VERSION_CODES.LOLLIPOP, cette méthode n'est plus disponible pour les applications tierces: l'introduction de recents centrés sur le document signifie qu'elle peut divulguer des informations sur la personne à l'appelant. Pour des raisons de compatibilité ascendante, il renverra toujours un petit sous-ensemble de ses données: au moins les propres tâches de l'appelant, et éventuellement d'autres tâches telles que la maison qui sont connues pour ne pas être sensibles.
( Remarque: une API officielle a été ajoutée dans l'API 14: voir cette réponse https://stackoverflow.com/a/29786451/119733 )
N'UTILISEZ PAS LA RÉPONSE PRÉCÉDENTE (waqas716).
Vous aurez un problème de fuite de mémoire, en raison de la référence statique à l'activité. Pour plus de détails, voir le lien suivant http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
Pour éviter cela, vous devez gérer les références d'activités. Ajoutez le nom de l'application dans le fichier manifeste:
<application
android:name=".MyApp"
....
</application>
Votre classe d'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;
}
}
Créez une nouvelle activité:
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);
}
}
Donc, maintenant, au lieu d'étendre la classe Activity pour vos activités, étendez simplement MyBaseActivity. Maintenant, vous pouvez obtenir votre activité actuelle à partir d'un contexte d'application ou d'activité comme celui-ci:
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
WeakReferences
dans Android le GC les recueille plus rapidement que vous ne le pensez.
WeakReference
n'est pas conseillée pour la mise en cache, ce n'est pas de la mise en cache, c'est-à-dire mCurrentActivity
qu'elle n'aura une référence que lorsqu'elle est vivante, donc le WeakReference
ne sera jamais collecté tant que le Activity
est en haut. Cependant, ce que @NachoColoma suggère est faux car il WeakReference
peut toujours faire référence à une activité non reprise (pas en vie / pas en haut) si la variable n'est pas effacée!
Application .ActivityLifecycleCallbacks
, ce qui serait plus central et vous n'auriez pas à ajouter de code de gestion dans toutes vos activités. Voir aussi developer.android.com/reference/android/app/…
Je développe en haut de la réponse de @ gezdy.
Dans toutes les activités, au lieu d'avoir à "s'enregistrer" Application
avec un codage manuel, nous pouvons utiliser l'API suivante depuis le niveau 14, pour nous aider à atteindre un objectif similaire avec moins de codage manuel.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
Dans Application.ActivityLifecycleCallbacks
, vous pouvez obtenir ce qui Activity
est "attaché" ou "détaché" à celui-ci Application
.
Cependant, cette technique n'est disponible qu'à partir du niveau d'API 14.
implements Application.ActivityLifecycleCallbacks
et ajoutez les méthodes pour l'implémenter. Ensuite, dans le constructeur de cette classe (ou onCreate ou init ou une autre méthode qui s'exécute lorsque l'instance devient active / prête), mettez getApplication().registerActivityLifecycleCallbacks(this);
en dernière ligne.
Mise à jour 2 : Une API officielle a été ajoutée pour cela, veuillez utiliser ActivityLifecycleCallbacks à la place.
METTRE À JOUR:
Comme l'a souligné @gezdy, et j'en suis reconnaissant. définissez la référence sur null également pour l'activité actuelle, au lieu de mettre à jour uniquement sur chaque onResume, définissez-la sur null sur le onDestroy de chaque activité pour éviter les problèmes de fuite de mémoire.
Il y a quelque temps, j'avais besoin de la même fonctionnalité et voici la méthode pour y parvenir. Dans chacune de vos activités, remplacez ces méthodes de cycle de vie.
@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);
}
Maintenant, dans votre classe de diffusion, vous pouvez accéder à l'activité actuelle pour afficher une alerte à ce sujet.
Application
est créé une seule fois et jamais garbage collecté exactement comme une variable statique.
clearReferences()
de (this.equals(currActivity))
.
@lockwobr Merci pour la mise à jour
Cela ne fonctionne pas 100% du temps dans la version 16 de l'API, si vous lisez le code sur github, la fonction "currentActivityThread" a été modifiée dans Kitkat, donc je veux dire la version 19ish, un peu difficile à faire correspondre la version de l'API aux versions de github .
Avoir accès au courant Activity
est très pratique. Ne serait-il pas bien d'avoir une getActivity
méthode statique renvoyant l'activité actuelle sans questions inutiles?
Le Activity
cours est très utile. Il donne accès au thread d'interface utilisateur de l'application, aux vues, aux ressources et bien d'autres. De nombreuses méthodes nécessitent un Context
, mais comment obtenir le pointeur? Voici quelques moyens:
ActivityThread
. Cette classe a accès à toutes les activités et, ce qui est encore mieux, dispose d'une méthode statique pour obtenir le courant ActivityThread
. Il n'y a qu'un seul petit problème: la liste des activités a accès aux packages.Facile à résoudre par réflexion:
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;
}
Une telle méthode peut être utilisée n'importe où dans l'application et elle est beaucoup plus pratique que toutes les approches mentionnées. De plus, il semble que ce ne soit pas aussi dangereux qu'il en a l'air. Il n'introduit aucune nouvelle fuite potentielle ou pointeur nul.
L'extrait de code ci-dessus ne gère pas les exceptions et suppose naïvement que la première activité en cours d'exécution est celle que nous recherchons. Vous voudrez peut-être ajouter des vérifications supplémentaires.
Map
interface à la place HashMap
ou ArrayMap
. J'ai édité la réponse @AZ_.
J'ai fait ce qui suit à Kotlin
Modifiez la classe d'application comme suit
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
}
}
}
Créer la classe 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
}
}
vous pouvez maintenant l'utiliser dans n'importe quelle classe en appelant ce qui suit: FTApplication.currentActivity()
getCurrentActivity () est également dans ReactContextBaseJavaModule.
(Depuis que cette question a été initialement posée, de nombreuses applications Android ont également un composant ReactNative - application hybride.)
La classe ReactContext dans ReactNative a l'ensemble de la logique pour maintenir mCurrentActivity qui est retournée dans getCurrentActivity ().
Remarque: je souhaite que getCurrentActivity () soit implémenté dans la classe Application Android.
Je n'ai pas trouvé de solution qui plairait à notre équipe, alors nous avons lancé la nôtre. Nous utilisons ActivityLifecycleCallbacks
pour garder une trace de l'activité actuelle, puis l'exposer via un service. Plus de détails ici: https://stackoverflow.com/a/38650587/10793
Pour une compatibilité descendante:
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
handle d'une Application
classe - alors que le ComponentName
est nécessaire pour déterminer si le désiré Activity
est en haut de la liste des tâches en cours. Et si cela ne répond pas complètement à la question, la réponse acceptée non plus.
topActivity
est disponible uniquement à partir d'Android Q
Personnellement, j'ai fait ce que "Cheok Yan Cheng" a dit, mais j'ai utilisé une "Liste" pour avoir un "Backstack" de toutes mes activités.
Si vous voulez vérifier quelle est l'activité actuelle, il vous suffit d'obtenir la dernière classe d'activité de la liste.
Créez une application qui étend "Application" et procédez comme suit:
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() }
}
}
Dans mon cas, j'ai utilisé "Application.ActivityLifecycleCallbacks" pour:
Bind / Unbind Merlin Instance (utilisé pour obtenir un événement lorsque l'application perd ou obtient une connexion, par exemple lorsque vous fermez des données mobiles ou lorsque vous l'ouvrez). Il est utile après la désactivation de l'action d'intention «OnConnectivityChanged». Pour plus d'informations sur MERLIN, voir: MERLIN INFO LINK
Fermez ma dernière instance de royaume lorsque l'application est fermée; Je vais l'initier dans une BaseActivity qui est étendue de toutes les autres activités et qui a une instance RealmHelper privée. Pour plus d'informations sur REALM voir: REALM INFO LINK Par exemple, j'ai une instance statique "RealmHelper" dans ma classe "RealmHelper" qui est instanciée dans mon application "onCreate". J'ai un service de synchronisation dans lequel je crée un nouveau "RealmHelper" parce que Realm est "Thread-Linked" et qu'une instance de Realm ne peut pas fonctionner dans un Thread différent. Donc, afin de suivre la documentation de Realm "Vous devez fermer toutes les instances de Realm ouvertes pour éviter les fuites de ressources système", pour accomplir cela, j'ai utilisé les "Application.ActivityLifecycleCallbacks" comme vous pouvez le voir.
Enfin, j'ai un récepteur qui se déclenche lorsque je termine la synchronisation de mon application, puis lorsque la synchronisation se termine, il appelle la méthode "IEndSyncCallback" "onEndSync" dans laquelle je regarde si j'ai une classe d'activité spécifique dans ma liste ActivitiesBackStack car j'ai besoin pour mettre à jour les données de la vue si la synchronisation les a mises à jour et que je pourrais avoir besoin d'effectuer d'autres opérations après la synchronisation de l'application.
C'est tout, j'espère que cela vous sera utile. A plus :)
La réponse de waqas716 est bonne. J'ai créé une solution de contournement pour un cas spécifique nécessitant moins de code et de maintenance.
J'ai trouvé un travail spécifique en demandant à une méthode statique de récupérer une vue de l'activité que je soupçonne d'être au premier plan. Vous pouvez parcourir toutes les activités et vérifier si vous le souhaitez ou obtenir le nom de l'activité à partir de la réponse de Martin
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Je vérifie ensuite si la vue n'est pas nulle et récupère le contexte via 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, ..."
dans developer.android.com/reference/android/app/…
Je n'aime aucune des autres réponses. Le ActivityManager n'est pas destiné à être utilisé pour obtenir l'activité en cours. Super classer et dépendre de Détruire est également fragile et pas le meilleur design.
Honnêtement, le mieux que j'ai trouvé jusqu'à présent est simplement de maintenir une énumération dans mon application, qui est définie lorsqu'une activité est créée.
Une autre recommandation pourrait être d'éviter d'utiliser plusieurs activités si possible. Cela peut être fait soit avec l'utilisation de fragments, soit dans mes préférences personnalisées.
Une solution plutôt simple consiste à créer une classe de gestionnaire singleton, dans laquelle vous pouvez stocker une référence à une ou plusieurs activités, ou à tout autre élément auquel vous souhaitez accéder dans toute l'application.
Appelez UberManager.getInstance().setMainActivity( activity );
onCreate de l'activité principale.
Appelez UberManager.getInstance().getMainActivity();
n'importe où dans votre application pour la récupérer. (J'utilise ceci pour pouvoir utiliser Toast à partir d'un thread non UI.)
Assurez-vous d'ajouter un appel UberManager.getInstance().cleanup();
lorsque votre application est en cours de destruction.
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;
}
}
J'ai environ 3 ans de retard mais j'y répondrai quand même au cas où quelqu'un trouverait ça comme moi.
J'ai résolu cela en utilisant simplement ceci:
if (getIntent().toString().contains("MainActivity")) {
// Do stuff if the current activity is MainActivity
}
Notez que "getIntent (). ToString ()" inclut un tas d'autres textes tels que le nom de votre package et les filtres d'intention pour votre activité. Techniquement, nous vérifions l'intention actuelle, pas l'activité, mais le résultat est le même. Utilisez simplement par exemple Log.d ("test", getIntent (). ToString ()); si vous voulez voir tout le texte. Cette solution est un peu piratée mais elle est beaucoup plus propre dans votre code et la fonctionnalité est la même.