Chaque fois que ma diffusion est exécutée, je souhaite afficher une alerte sur les activités de premier plan.
Sachant que ActivityManager gère Activity, nous pouvons obtenir des informations à partir de ActivityManager. Nous obtenons l’activité courante de premier plan par
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
MISE À JOUR 2018/10/03
getRunningTasks () est DEPRECATED. voir les solutions ci-dessous.
Cette méthode était obsolète dans l'API de niveau 21 . À partir de 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 transmettre des informations personnelles à l'appelant. Pour assurer la compatibilité ascendante, il retournera toujours un petit sous-ensemble de ses données: au moins les tâches propres à l'appelant et éventuellement d'autres tâches, telles que la tâche home, dont on sait qu'elles ne sont pas sensibles.
(Remarque: Une API officielle a été ajoutée dans l'API 14: voir cette réponse https://stackoverflow.com/a/29786451/119733 )
NE PAS UTILISER Previous (waqas716) answer.
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 aux 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éer 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 d’activité pour vos activités, il suffit d’allonger MyBaseActivity. Maintenant, vous pouvez obtenir votre activité actuelle à partir d'une application ou d'un contexte d'activité comme ça:
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
J'étends au sommet de la réponse de @ gezdy.
Dans toutes les activités, au lieu de devoir "s'enregistrer" avec Application
avec codage manuel, nous pouvons utiliser l'API suivante depuis le niveau 14 pour nous aider à atteindre le même objectif avec moins de codage manuel.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
Dans Application.ActivityLifecycleCallbacks
, vous pouvez savoir quelle Activity
est "attachée" ou "détachée" à cette Application
.
Cependant, cette technique n'est disponible que depuis l'API de niveau 14.
Update 2: une API officielle a été ajoutée à cet effet. Veuillez utiliser ActivityLifecycleCallbacks à la place.
METTRE À JOUR:
Comme l'a souligné @gezdy, et j'en suis reconnaissant. définit la référence sur null aussi pour l'activité en cours, au lieu de mettre à jour chaque onResume, elle est définie sur null pour chaque activité onDestroy afin d'éviter un problème 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 toutes 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);
}
Désormais, dans votre classe de diffusion, vous pouvez accéder à l'activité en cours pour y afficher une alerte.
@lockwobr Merci pour la mise à jour
Cela ne fonctionne pas 100% du temps dans la version 16 de l’API, si vous lisez le fichier code sur github la fonction "currentActivityThread" a été modifiée dans KitKat, donc je veux dire la version 19ish, un peu difficile à égaler api version à libérer dans github.
Avoir accès à la version actuelle Activity
est très pratique. Ne serait-il pas agréable d’avoir une méthode statique getActivity
renvoyant l’activité en cours sans questions inutiles?
La classe Activity
est très utile. Il donne accès au fil d’interface utilisateur de l’application, aux vues, aux ressources, etc. De nombreuses méthodes nécessitent un Context
, mais comment obtenir le pointeur? Voici quelques façons:
ActivityThread
. Cette classe dispose d’un accès À toutes les activités et, mieux encore, d’une méthode statique Pour obtenir le ActivityThread
actuel. Il n'y a qu'un seul petit problème La liste d'activités a un accès au package. Facile à résoudre en utilisant la 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, ce qui est beaucoup plus pratique que toutes les approches mentionnées. De plus, il semble que ce n’est pas aussi dangereux qu’il le semble. Il n'introduit pas de nouvelles fuites potentielles ni de pointeurs nuls.
L'extrait de code ci-dessus manque de gestion des 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.
getCurrentActivity () est également dans ReactContextBaseJavaModule.
(Depuis que cette question a été posée à l’origine, de nombreuses applications Android possèdent également un composant ReactNative - application hybride.)
la classe ReactContext dans ReactNative contient l'ensemble de la logique permettant de conserver mCurrentActivity, qui est renvoyée dans getCurrentActivity ().
Remarque: je souhaite que getCurrentActivity () soit implémenté dans la classe d'applications Android.
Je ne pouvais pas trouver une solution qui plairait à notre équipe et nous avons donc lancé la nôtre. Nous utilisons ActivityLifecycleCallbacks
pour suivre l'activité en cours et l'exposer ensuite via un service. Plus de détails ici: https://stackoverflow.com/a/38650587/10793
Personnellement, j'ai fait ce que "Cheok Yan Cheng" avait 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 le dernier cours 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 les 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 à la fermeture de l'application. Je l'initierai dans une BaseActivity qui est étendue à toutes les autres activités et qui a une instance privée de RealmHelper. 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" car Realm est "lié au thread" et une instance de realm ne peut pas fonctionner dans un autre thread .. Pour pouvoir suivre la documentation du realm, vous devez fermer Toutes les instances du domaine ouvert pour éviter les fuites des ressources système ", pour ce faire, j'ai utilisé" Application.ActivityLifecycleCallbacks "comme vous pouvez le voir.
Enfin, j'ai un récepteur qui est déclenché quand je termine la synchronisation de mon application, puis quand la fin de la synchronisation est appelée, elle appellera la méthode "IEndSyncCallback" "onEndSync" dans laquelle je regarde si j'ai une classe d'activité spécifique dans ma liste ActivitiesBackStack parce que j'ai besoin de faire une opération si j'ai déjà passé ce point dans mon application.
C'est tout, espérons que cela vous sera utile. A plus :)
Pour la compatibilité ascendante:
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;
}
J'ai fait la suite à Kotlin
Modifier 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éez 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 le numéro suivant: FTApplication.currentActivity()