Zawsze, gdy moja transmisja jest wykonywana, chcę pokazać alert o działaniach na pierwszym planie.
Zawsze, gdy moja transmisja jest wykonywana, chcę pokazać alert o działaniach na pierwszym planie.
Odpowiedzi:
Wiedząc, że ActivityManager zarządza Aktywnością , możemy uzyskać informacje z ActivityManager . Aktualną działającą aktywność na pierwszym planie otrzymujemy wg
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
UPDATE 2018/10/03
getRunningTasks () jest WYCOFANE. zobacz poniższe rozwiązania.
Ta metoda została wycofana na poziomie API 21. Począwszy od Build.VERSION_CODES.LOLLIPOP, ta metoda nie jest już dostępna dla aplikacji innych firm: wprowadzenie ostatnich zorientowanych na dokumenty oznacza, że może ona wyciekać informacje o osobie do wywołującego. W celu zapewnienia kompatybilności wstecznej nadal zwróci niewielki podzbiór swoich danych: przynajmniej własne zadania wywołującego i prawdopodobnie inne zadania, takie jak dom, o których wiadomo, że nie są wrażliwe.
( Uwaga: oficjalny interfejs API został dodany do API 14: Zobacz tę odpowiedź https://stackoverflow.com/a/29786451/119733 )
NIE UŻYWAJ POPRZEDNIEJ (waqas716) odpowiedzi.
Będziesz miał problem z wyciekiem pamięci z powodu statycznego odniesienia do działania. Aby uzyskać więcej informacji, zobacz poniższy link http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
Aby tego uniknąć, należy zarządzać odniesieniami do działań. Dodaj nazwę aplikacji w pliku manifestu:
<application
android:name=".MyApp"
....
</application>
Twoja klasa aplikacji:
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;
}
}
Utwórz nowe działanie:
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);
}
}
Tak więc zamiast rozszerzać klasę Activity dla swoich działań, po prostu rozszerz MyBaseActivity. Teraz możesz pobrać swoją bieżącą aktywność z aplikacji lub kontekstu działania w następujący sposób:
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
WeakReferences
w Androidzie, GC zbiera je szybciej niż myślisz.
WeakReference
nie jest zalecane do buforowania, to nie jest buforowanie, to znaczy mCurrentActivity
będzie mieć do niego odniesienie tylko wtedy, gdy jest żywy, więc WeakReference
nigdy nie zostaną zebrane, gdy Activity
jest na wierzchu. Jednak to, co sugeruje @NachoColoma, jest błędne, ponieważ WeakReference
może nadal odnosić się do nie wznowionej (nie aktywnej / nie na wierzchu) aktywności, jeśli zmienna nie zostanie wyczyszczona!
Application .ActivityLifecycleCallbacks
, które byłoby bardziej centralne i nie musiałbyś dodawać żadnego kodu zarządzającego do wszystkich swoich działań. Zobacz także developer.android.com/reference/android/app/…
Rozszerzam odpowiedź @ gezdy.
W każdym działaniu, zamiast „rejestrować się” za Application
pomocą ręcznego kodowania, możemy skorzystać z następującego interfejsu API od poziomu 14, aby pomóc nam osiągnąć podobny cel przy mniejszym ręcznym kodowaniu.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
W programie Application.ActivityLifecycleCallbacks
można uzyskać informacje o tym, co Activity
jest „dołączone” lub „odłączone” od tegoApplication
.
Jednak ta technika jest dostępna tylko od poziomu API 14.
implements Application.ActivityLifecycleCallbacks
i dodajesz metody do zaimplementowania tego. Następnie w konstruktorze tej klasy (lub onCreate, init lub innej metodzie, która działa, gdy instancja staje się aktywna / gotowa), umieść getApplication().registerActivityLifecycleCallbacks(this);
jako ostatnią linię.
Aktualizacja 2 : W tym celu dodano oficjalny interfejs API, zamiast tego użyj ActivityLifecycleCallbacks .
AKTUALIZACJA:
Jak wskazał @gezdy i jestem za to wdzięczny. ustaw odniesienie na null również dla bieżącego działania, zamiast aktualizować tylko przy każdym onResume, ustaw go na null w przypadku onDestroy każdego działania, aby uniknąć problemu z wyciekiem pamięci.
Jakiś czas temu potrzebowałem tej samej funkcjonalności i oto metoda, jak to osiągnąłem. W każdej swojej działalności zastąp te metody cyklu życia.
@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);
}
Teraz w swojej klasie transmisji możesz uzyskać dostęp do bieżącej aktywności, aby pokazać jej alert.
Application
jest tworzony tylko raz i nigdy nie zbiera śmieci, dokładnie tak jak zmienna statyczna.
clearReferences()
do (this.equals(currActivity))
.
@lockwobr Dzięki za aktualizację
To nie działa w 100% w wersji API 16, jeśli czytasz kod na github, funkcja "currentActivityThread" została zmieniona w Kitkat, więc chcę powiedzieć, że wersja 19ish, trochę trudna do dopasowania wersji API do wydań na githubie .
Dostęp do prądu Activity
jest bardzo wygodny. Czy nie byłoby miło mieć statyczną getActivity
metodę zwracającą bieżącą aktywność bez zbędnych pytań?
Activity
Klasa jest bardzo przydatna. Daje dostęp do wątku interfejsu użytkownika aplikacji, widoków, zasobów i wielu innych. Liczne metody wymagają Context
, ale jak uzyskać wskaźnik? Oto kilka sposobów:
ActivityThread
. Ta klasa ma dostęp do wszystkich działań i, co jeszcze lepsze, ma statyczną metodę pobierania prądu ActivityThread
. Jest tylko jeden mały problem - lista aktywności ma dostęp do pakietu.Łatwe do rozwiązania za pomocą refleksji:
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;
}
Takiej metody można używać w dowolnym miejscu aplikacji i jest ona znacznie wygodniejsza niż wszystkie wymienione podejścia. Co więcej, wydaje się, że nie jest to tak niebezpieczne, jak się wydaje. Nie wprowadza żadnych nowych potencjalnych wycieków ani zerowych wskaźników.
Powyższy fragment kodu nie obsługuje wyjątków i naiwnie zakłada, że pierwsze uruchomione działanie jest tym, którego szukamy. Możesz chcieć dodać dodatkowe kontrole.
Map
zamiast tego używać interfejsu HashMap
lub ArrayMap
. Edytowałem odpowiedź @AZ_.
Zrobiłem następujące w Kotlinie
Edytuj klasę aplikacji w następujący sposób
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
}
}
}
Utwórz klasę 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
}
}
możesz teraz używać go w dowolnej klasie, wywołując następujące polecenie: FTApplication.currentActivity()
getCurrentActivity () również znajduje się w ReactContextBaseJavaModule.
(Ponieważ to pytanie zostało zadane na początku, wiele aplikacji na Androida ma również komponent ReactNative - aplikację hybrydową).
klasa ReactContext w ReactNative ma cały zestaw logiki do obsługi mCurrentActivity, który jest zwracany w getCurrentActivity ().
Uwaga: Chciałbym, żeby metoda getCurrentActivity () została zaimplementowana w klasie aplikacji na Androida.
Nie mogłem znaleźć rozwiązania, z którego nasz zespół byłby zadowolony, więc stworzyliśmy własne. Używamy ActivityLifecycleCallbacks
do śledzenia bieżącej aktywności, a następnie ujawniamy ją za pośrednictwem usługi. Więcej szczegółów tutaj: https://stackoverflow.com/a/38650587/10793
Aby uzyskać zgodność wsteczną:
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
uchwyt z Application
klasy - podczas gdy ComponentName
wymagane jest określenie, czy żądane Activity
znajduje się na górze listy uruchomionych zadań. A jeśli to nie w pełni odpowiada na pytanie, przyjęta odpowiedź też nie.
topActivity
jest dostępny tylko z Androida Q
Osobiście zrobiłem tak, jak powiedział „Cheok Yan Cheng”, ale użyłem „Listy”, aby mieć „backstack” do wszystkich moich działań.
Jeśli chcesz sprawdzić, które jest bieżącym działaniem, wystarczy pobrać ostatnią klasę aktywności na liście.
Utwórz aplikację, która rozszerza „Aplikacja” i wykonaj następujące czynności:
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() }
}
}
W moim przypadku użyłem „Application.ActivityLifecycleCallbacks” do:
Bind / Unbind Merlin Instance (służy do uzyskiwania zdarzenia, gdy aplikacja utraci lub uzyska połączenie, na przykład gdy zamkniesz mobilną transmisję danych lub gdy ją otworzysz). Jest to przydatne po wyłączeniu akcji intencji „OnConnectivityChanged”. Więcej informacji o MERLINIE znajdziesz na: MERLIN INFO LINK
Zamknij moją ostatnią instancję Realm po zamknięciu aplikacji; Zainicjuję to wewnątrz BaseActivity, które jest rozszerzone ze wszystkich innych działań i które ma prywatną instancję RealmHelper. Aby uzyskać więcej informacji o REALM, zobacz: REALM INFO LINK Na przykład mam statyczną instancję "RealmHelper" wewnątrz mojej klasy "RealmHelper", która jest tworzona w mojej aplikacji "onCreate". Mam usługę synchronizacji, w której tworzę nowy „RealmHelper”, ponieważ Realm jest „powiązany z wątkami”, a instancja dziedziny nie może działać w innym wątku. Aby więc postępować zgodnie z dokumentacją dziedziny „Musisz zamknąć wszystkie otwarte instancje dziedziny, aby uniknąć wycieków zasobów systemowych”, aby to osiągnąć, użyłem „Application.ActivityLifecycleCallbacks”, jak widać powyżej.
Wreszcie mam odbiornik, który jest wyzwalany po zakończeniu synchronizacji aplikacji, a po zakończeniu synchronizacji wywoła metodę „IEndSyncCallback” „onEndSync”, w której sprawdzam, czy mam określoną klasę aktywności na mojej liście ActivitiesBackStack, ponieważ potrzebuję aby zaktualizować dane w widoku, jeśli synchronizacja je zaktualizowała i może być konieczne wykonanie innych operacji po synchronizacji aplikacji.
To wszystko, mam nadzieję, że to pomoże. Widzę cię :)
Odpowiedź waqas716 jest dobra. Stworzyłem obejście dla konkretnego przypadku wymagające mniejszej ilości kodu i mniejszej obsługi.
Znalazłem specyficzne obejście, ponieważ metoda statyczna pobiera widok z działania, które podejrzewam, że znajduje się na pierwszym planie. Możesz iterować wszystkie działania i sprawdzić, czy chcesz, lub uzyskać nazwę działania z odpowiedzi Martina
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Następnie sprawdzam, czy widok nie jest pusty i pobieram kontekst za pomocą metody 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, ..."
w developer.android.com/reference/android/app/ ...
Nie podoba mi się żadna inna odpowiedź. Menedżer aktywności nie jest przeznaczony do pobierania bieżącej aktywności. Super klasyfikacja i zależność od onDestroy jest również delikatna i nie jest najlepszym projektem.
Szczerze mówiąc, najlepsze, co do tej pory wymyśliłem, to po prostu utrzymanie wyliczenia w mojej aplikacji, które jest ustawiane podczas tworzenia działania.
Innym zaleceniem może być unikanie wykonywania wielu czynności, jeśli to możliwe. Można to zrobić za pomocą fragmentów lub według moich preferencji widoków niestandardowych.
Dość prostym rozwiązaniem jest utworzenie pojedynczej klasy menedżera, w której można przechowywać odniesienie do jednego lub więcej działań lub czegokolwiek innego, do czego chcesz mieć dostęp w całej aplikacji.
Połączenie UberManager.getInstance().setMainActivity( activity );
do funkcji onCreate głównej aktywności.
Zadzwoń do UberManager.getInstance().getMainActivity();
dowolnego miejsca w aplikacji, aby ją odzyskać. (Używam tego, aby móc używać Toast z wątku innego niż interfejs użytkownika).
Upewnij się, że dodajesz wywołanie, UberManager.getInstance().cleanup();
gdy Twoja aplikacja jest niszczona.
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;
}
}
Spóźniłem się jakieś 3 lata, ale i tak odpowiem na wypadek, gdyby ktoś znalazł to tak, jak ja.
Rozwiązałem to, po prostu używając tego:
if (getIntent().toString().contains("MainActivity")) {
// Do stuff if the current activity is MainActivity
}
Zwróć uwagę, że „getIntent (). ToString ()” zawiera kilka innych tekstów, takich jak nazwa pakietu i wszelkie filtry intencji dla Twojej aktywności. Technicznie rzecz biorąc, sprawdzamy obecny zamiar, a nie aktywność, ale wynik jest taki sam. Po prostu użyj np. Log.d ("test", getIntent (). ToString ()); jeśli chcesz zobaczyć cały tekst. To rozwiązanie jest trochę hackerskie, ale jest znacznie czystsze w kodzie, a funkcjonalność jest taka sama.