Ogni volta che la mia trasmissione viene eseguita, voglio mostrare un avviso all'attività in primo piano.
Ogni volta che la mia trasmissione viene eseguita, voglio mostrare un avviso all'attività in primo piano.
Risposte:
Sapendo che ActivityManager gestisce l' attività , in modo da poter ottenere informazioni da ActivityManager . Otteniamo l'attuale primo piano in esecuzione Activity
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
AGGIORNAMENTO 2018/10/03
getRunningTasks () è DEPRECATED. vedi le soluzioni di seguito.
Questo metodo è stato deprecato nel livello API 21. A partire da Build.VERSION_CODES.LOLLIPOP, questo metodo non è più disponibile per le applicazioni di terze parti: l'introduzione di recenti recenti incentrati sui documenti significa che può trasferire informazioni sulla persona al chiamante. Per compatibilità con le versioni precedenti, restituirà comunque un piccolo sottoinsieme dei suoi dati: almeno le attività del chiamante e, eventualmente, altre attività come la casa che non sono sensibili.
( Nota: un'API ufficiale è stata aggiunta nell'API 14: vedere questa risposta https://stackoverflow.com/a/29786451/119733 )
NON USARE la risposta PRECEDENTE (waqas716).
Si verificherà un problema di perdita di memoria, a causa del riferimento statico all'attività. Per maggiori dettagli consultare il seguente link http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
Per evitare ciò, è necessario gestire i riferimenti alle attività. Aggiungi il nome dell'applicazione nel file manifest:
<application
android:name=".MyApp"
....
</application>
La tua classe di applicazione:
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 nuova attività:
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);
}
}
Quindi, ora invece di estendere la classe Activity per le tue attività, estendi MyBaseActivity. Ora puoi ottenere la tua attività corrente dall'applicazione o dal contesto Attività in questo modo:
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
WeakReferencesin Android il GC li raccoglie più velocemente di quanto pensi.
WeakReferencenon è consigliato per la memorizzazione nella cache, questa non è la memorizzazione nella cache, ovvero mCurrentActivityavrà un riferimento ad essa solo quando è viva, quindi WeakReferencenon verrà mai raccolta mentre Activityè in primo piano. Tuttavia, ciò che @NachoColoma suggerisce è sbagliato perché WeakReferencepotrebbe ancora fare riferimento a un'attività non ripresa (non viva / non in cima) se la variabile non viene cancellata!
Application .ActivityLifecycleCallbacks, che sarebbe più centrale e non dovresti aggiungere alcun codice di gestione in tutte le tue attività. Vedi anche developer.android.com/reference/android/app/…
Espando in cima alla risposta di @ gezdy.
In ogni attività, invece di dover "registrarsi" con Application con la codifica manuale, possiamo utilizzare la seguente API dal livello 14, per aiutarci a raggiungere scopi simili con meno codifica manuale.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
In Application.ActivityLifecycleCallbacks, puoi ottenere qualeActivity è "attaccato" o "staccato" a questo Application.
Tuttavia, questa tecnica è disponibile solo dal livello API 14.
implements Application.ActivityLifecycleCallbackse aggiungere i metodi per implementarla. Quindi nel costruttore di quella classe (o onCreate o init o altro metodo che viene eseguito quando l'istanza sta diventando attiva / pronta), inserisci getApplication().registerActivityLifecycleCallbacks(this);come ultima riga.
Aggiornamento 2 : è stata aggiunta un'API ufficiale per questo, utilizzare invece ActivityLifecycleCallbacks .
AGGIORNARE:
Come sottolineato da @gezdy, e te ne sono grato. imposta anche il riferimento su null per l'attività corrente, invece di aggiornarlo su ogni onResume impostalo su null su onDestroy di ogni attività per evitare problemi di perdita di memoria.
Qualche tempo fa avevo bisogno della stessa funzionalità ed ecco il metodo con cui l'ho raggiunto. In ogni tua attività ignora questi metodi del ciclo di vita.
@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);
}
Ora nella tua classe di trasmissione puoi accedere all'attività corrente per mostrare un avviso su di essa.
Applicationviene creato una sola volta e mai immondizia raccolta esattamente come una variabile statica.
clearReferences()a (this.equals(currActivity)).
@lockwobr Grazie per l'aggiornamento
Questo non funziona al 100% delle volte in api versione 16, se leggi il codice su github la funzione "currentActivityThread" è stata modificata in Kitkat, quindi voglio dire la versione 19ish, un po 'difficile da abbinare alla versione di api con le versioni in github .
Avere accesso alla corrente Activityè molto utile. Non sarebbe bello avere una staticagetActivity metodo che restituisca l'attività corrente senza domande inutili?
La Activityclasse è molto utile. Dà accesso al thread dell'interfaccia utente dell'applicazione, visualizzazioni, risorse e molti altri. Numerosi metodi richiedono un Context, ma come ottenere il puntatore? Ecco alcuni modi:
ActivityThread. Questa classe ha accesso a tutte le attività e, per di più, ha un metodo statico per ottenere la correnteActivityThread . C'è solo un piccolo problema: l'elenco delle attività ha accesso al pacchetto.Facile da risolvere usando la riflessione:
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;
}
Tale metodo può essere utilizzato ovunque nell'app ed è molto più conveniente di tutti gli approcci citati. Inoltre, sembra che non sia così pericoloso come sembra. Non introduce nuove potenziali perdite o puntatori nulli.
Lo snippet di codice sopra menzionato manca di gestione delle eccezioni e presume ingenuamente che la prima attività in esecuzione sia quella che stiamo cercando. Potresti voler aggiungere alcuni controlli aggiuntivi.
Mapinvece l'interfaccia HashMapo ArrayMap. Ho modificato la risposta @AZ_.
Ho fatto il seguito a Kotlin
Modifica la classe di applicazione come segue
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
}
}
}Creare 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
}
}ora puoi usarlo in qualsiasi classe chiamando il seguente: FTApplication.currentActivity()
getCurrentActivity () è anche in ReactContextBaseJavaModule.
(Poiché questa domanda è stata inizialmente posta, molte app Android hanno anche il componente ReactNative - app ibrida.)
la classe ReactContext in ReactNative ha l'intera serie di logiche per mantenere mCurrentActivity che viene restituita in getCurrentActivity ().
Nota: vorrei che getCurrentActivity () fosse implementato nella classe Applicazione Android.
Non sono riuscito a trovare una soluzione di cui il nostro team sarebbe contento, quindi abbiamo creato la nostra. Usiamo ActivityLifecycleCallbacksper tenere traccia dell'attività corrente e quindi esporla attraverso un servizio. Maggiori dettagli qui: https://stackoverflow.com/a/38650587/10793
Per compatibilità con le versioni precedenti:
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;
}
WeakReferencehandle da una Applicationclasse, mentre ComponentNameè necessario per determinare se il desiderato Activityè in cima all'elenco delle attività in esecuzione. E se questo non risponde completamente alla domanda, neanche la risposta accettata.
topActivityè disponibile solo da Android Q
Personalmente ho fatto come diceva "Cheok Yan Cheng", ma ho usato un "Elenco" per avere un "Backstack" di tutte le mie attività.
Se si desidera verificare qual è l'attività corrente, è sufficiente ottenere l'ultima classe di attività nell'elenco.
Creare un'applicazione che estenda "Applicazione" e procedere come segue:
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() }
}
}
Nel mio caso ho usato "Application.ActivityLifecycleCallbacks" per:
Bind / Unbind Merlin Instance (utilizzato per ottenere eventi quando l'app perde o ottiene la connessione, ad esempio quando si chiudono i dati mobili o quando li si apre). È utile dopo che l'azione di intento "OnConnectivityChanged" è stata disabilitata. Per ulteriori informazioni su MERLIN, consultare: LINK INFO MERLIN
Chiudi la mia ultima istanza di Realm quando l'applicazione è chiusa; Lo avvierò all'interno di una BaseActivity che è estesa da tutte le altre attività e che ha un'istanza RealmHelper privata. Per maggiori informazioni su REALM vedi: INFO LINK REALM Ad esempio ho un'istanza "RealmHelper" statica all'interno della mia classe "RealmHelper" che viene istanziata nella mia applicazione "onCreate". Ho un servizio di sincronizzazione in cui creo il nuovo "RealmHelper" perché Realm è "Thread-Linked" e un'istanza Realm non può funzionare all'interno di un thread diverso. Quindi, al fine di seguire la documentazione di Realm "È necessario chiudere tutte le istanze di Realm aperte per evitare perdite di risorse di sistema", per realizzare questa cosa ho usato "Application.ActivityLifecycleCallbacks" come puoi vedere.
Finalmente ho un ricevitore che viene attivato quando finisco di sincronizzare la mia applicazione, quindi quando termina la sincronizzazione chiamerà il metodo "IEndSyncCallback" "onEndSync" in cui cerco se ho una Classe di attività specifica all'interno del mio Elenco ActivityBackStack perché ho bisogno per aggiornare i dati nella vista se la sincronizzazione li ha aggiornati e potrei aver bisogno di fare altre operazioni dopo la sincronizzazione dell'app.
Questo è tutto, spero che sia utile. Ci vediamo :)
La risposta di waqas716 è buona. Ho creato una soluzione alternativa per un caso specifico che richiede meno codice e manutenzione.
Ho trovato una soluzione specifica con un metodo statico per recuperare una vista dall'attività che sospetto fosse in primo piano. Puoi scorrere tutte le attività e verificare se lo desideri o ottenere il nome dell'attività dalla risposta di Martin
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Quindi controllo se la vista non è nulla e ottengo il contesto tramite 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/…
Non mi piace nessuna delle altre risposte. ActivityManager non è pensato per essere utilizzato per ottenere l'attività corrente. Super classing e in base a Distruggi è anche fragile e non è il miglior design.
Onestamente, il meglio che mi è venuto in mente finora è solo mantenere un enum nella mia applicazione, che viene impostato quando viene creata un'attività.
Un'altra raccomandazione potrebbe essere quella di evitare di usare più attività se possibile. Questo può essere fatto utilizzando frammenti o nelle mie preferenze visualizzazioni personalizzate.
Una soluzione piuttosto semplice è quella di creare una classe manager singleton, in cui è possibile memorizzare un riferimento a una o più attività o qualsiasi altra cosa a cui si desidera accedere in tutta l'app.
Chiama UberManager.getInstance().setMainActivity( activity );onCreate dell'attività principale.
Chiama UberManager.getInstance().getMainActivity();ovunque nell'app per recuperarla. (Sto usando questo per essere in grado di usare Toast da un thread non UI.)
Assicurati di aggiungere una chiamata a UberManager.getInstance().cleanup();quando l'app viene distrutta.
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;
}
}
Sono in ritardo di 3 anni, ma risponderò comunque nel caso qualcuno lo trovi come ho fatto io.
Ho risolto questo semplicemente usando questo:
if (getIntent().toString().contains("MainActivity")) {
// Do stuff if the current activity is MainActivity
}
Nota che "getIntent (). ToString ()" include un sacco di altro testo come il nome del pacchetto e qualsiasi filtro di intenti per la tua attività. Tecnicamente stiamo verificando l'intento attuale, non l'attività, ma il risultato è lo stesso. Basta usare ad esempio Log.d ("test", getIntent (). ToString ()); se vuoi vedere tutto il testo. Questa soluzione è un po 'confusa ma è molto più pulita nel tuo codice e la funzionalità è la stessa.