Sempre que minha transmissão é executada, quero mostrar um alerta para a atividade em primeiro plano.
Sempre que minha transmissão é executada, quero mostrar um alerta para a atividade em primeiro plano.
Respostas:
Sabendo que o ActivityManager gerencia o Activity , podemos obter informações do ActivityManager . Temos o primeiro plano atual executando Activity por
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
ATUALIZAÇÃO 03/03/10
getRunningTasks () está DEPRECADO. veja as soluções abaixo.
Este método foi descontinuado no nível 21. da API. No Build.VERSION_CODES.LOLLIPOP, esse método não está mais disponível para aplicativos de terceiros: a introdução de recentes centrados em documentos significa que pode vazar informações pessoais para o chamador. Para compatibilidade com versões anteriores, ele ainda retornará um pequeno subconjunto de dados: pelo menos as próprias tarefas do chamador e, possivelmente, algumas outras tarefas, como a casa, que sabidamente não são sensíveis.
( Nota: Uma API oficial foi adicionada na API 14: consulte esta resposta https://stackoverflow.com/a/29786451/119733 )
NÃO USE resposta anterior (waqas716).
Você terá um problema de vazamento de memória, devido à referência estática da atividade. Para mais detalhes, consulte o link a seguir http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
Para evitar isso, você deve gerenciar referências de atividades. Adicione o nome do aplicativo no arquivo de manifesto:
<application
android:name=".MyApp"
....
</application>
Sua classe de aplicativo:
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;
}
}
Crie uma nova atividade:
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);
}
}
Portanto, agora, em vez de estender a classe Activity para suas atividades, apenas estenda MyBaseActivity. Agora, você pode obter sua atividade atual a partir do aplicativo ou do contexto de atividades assim:
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
WeakReferences
no Android o GC os coleta mais rapidamente do que você imagina.
WeakReference
não é recomendado para armazenamento em cache, isso não é armazenamento em cache, ou seja, mCurrentActivity
ele só terá uma referência quando estiver vivo, para WeakReference
que nunca seja coletado enquanto Activity
estiver no topo. No entanto, o que o @NachoColoma sugere está errado, porque WeakReference
ainda pode fazer referência a uma atividade não retomada (não está ativa / não está no topo) se a variável não for limpa!
Application .ActivityLifecycleCallbacks
, o que seria mais central e você não precisaria adicionar nenhum código de gerenciamento em todas as suas atividades. Consulte também developer.android.com/reference/android/app/…
Expiro na parte superior da resposta de @ gezdy.
Em todas as atividades, em vez de ter que "se registrar" com Application
a codificação manual, podemos usar a seguinte API desde o nível 14, para nos ajudar a alcançar um objetivo semelhante com menos codificação manual.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
Em Application.ActivityLifecycleCallbacks
, você pode obter o que Activity
está "anexado" ou "desanexado" a esteApplication
.
No entanto, essa técnica está disponível apenas desde o nível 14 da API.
implements Application.ActivityLifecycleCallbacks
e adiciona os métodos para implementá-lo. Então, no construtor dessa classe (ou onCreate ou init ou outro método que é executado quando a instância está se tornando ativa / pronta), coloque getApplication().registerActivityLifecycleCallbacks(this);
como a última linha.
Atualização 2 : há uma API oficial adicionada para isso, use ActivityLifecycleCallbacks .
ATUALIZAR:
Como apontado por @gezdy, e sou grato por isso. configure a referência como null também para a atividade atual, em vez de atualizar apenas todos os onResume, defina-os como nulos em cada atividade do onDestroy para evitar problemas de vazamento de memória.
Há um tempo atrás, eu precisava da mesma funcionalidade e aqui está o método como consegui isso. Em todas as suas atividades, substitua esses métodos do 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);
}
Agora, na sua aula de transmissão, você pode acessar a atividade atual para mostrar um alerta nela.
Application
é criado apenas uma vez e nunca o lixo é coletado exatamente como uma variável estática.
clearReferences()
que (this.equals(currActivity))
.
@lockwobr Obrigado por atualização
Isso não funciona 100% do tempo na versão 16 da API, se você ler o código no github, a função "currentActivityThread" foi alterada no Kitkat, então eu quero dizer a versão 19ish, meio difícil de combinar a versão da API com as versões no github .
Ter acesso à corrente Activity
é muito útil. Não seria bom ter uma estáticagetActivity
método retornando a atividade atual sem perguntas desnecessárias?
A Activity
turma é muito útil. Ele fornece acesso ao thread da interface do usuário do aplicativo, visualizações, recursos e muito mais. Inúmeros métodos requerem um Context
, mas como obter o ponteiro? Aqui estão algumas maneiras:
ActivityThread
. Esta classe tem acesso a todas as atividades e, o que é ainda melhor, possui um método estático para obter a atualização ActivityThread
. Há apenas um pequeno problema - a lista de atividades tem acesso ao pacote.Fácil de resolver usando reflexão:
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;
}
Esse método pode ser usado em qualquer lugar do aplicativo e é muito mais conveniente do que todas as abordagens mencionadas. Além disso, parece que não é tão inseguro quanto parece. Ele não apresenta nenhum novo vazamento em potencial ou ponteiros nulos.
O trecho de código acima não possui tratamento de exceção e assume ingenuamente que a primeira atividade em execução é a que estamos procurando. Você pode querer adicionar algumas verificações adicionais.
Map
interface HashMap
ou ArrayMap
. Eu editei a resposta @AZ_.
Eu fiz o seguinte em Kotlin
Edite a classe de aplicativo da seguinte maneira
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
}
}
}
Crie a 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
}
}
agora você pode usá-lo em qualquer classe chamando o seguinte: FTApplication.currentActivity()
getCurrentActivity () também está em ReactContextBaseJavaModule.
(Como a pergunta foi feita inicialmente, muitos aplicativos Android também possuem o componente ReactNative - aplicativo híbrido.)
A classe ReactContext em ReactNative possui todo o conjunto de lógicas para manter mCurrentActivity retornado em getCurrentActivity ().
Nota: Desejo que getCurrentActivity () seja implementado na classe Aplicativo Android.
Não consegui encontrar uma solução com a qual nossa equipe ficasse feliz, então criamos a nossa. Usamos ActivityLifecycleCallbacks
para acompanhar a atividade atual e, em seguida, expô-la através de um serviço. Mais detalhes aqui: https://stackoverflow.com/a/38650587/10793
Para compatibilidade com versões 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
controlado por uma Application
classe - enquanto isso ComponentName
é necessário para determinar se o desejado Activity
está no topo da lista de tarefas em execução. E se isso não responder totalmente à pergunta, a resposta aceita também não.
topActivity
está disponível apenas no Android Q
Pessoalmente, fiz o que "Cheok Yan Cheng" disse, mas usei uma "Lista" para ter um "Backstack" de todas as minhas atividades.
Se você deseja verificar Qual é a atividade atual, basta obter a última classe de atividade na lista.
Crie um aplicativo que estenda "Aplicativo" e faça o seguinte:
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() }
}
}
No meu caso, usei "Application.ActivityLifecycleCallbacks" para:
Vincular / desvincular a instância Merlin (usada para obter um evento quando o aplicativo perde ou obtém conexão, por exemplo, quando você fecha os dados móveis ou quando os abre). É útil depois que a ação de intenção "OnConnectivityChanged" foi desativada. Para mais informações sobre MERLIN, consulte: MERLIN INFO LINK
Feche minha última instância de região quando o aplicativo for fechado; Vou iniciá-lo dentro de uma BaseActivity que é estendida a todas as outras atividades e que possui uma instância RealmHelper privada. Para mais informações sobre o REALM, consulte: REALM INFO LINK Por exemplo, eu tenho uma instância estática "RealmHelper" dentro da minha classe "RealmHelper", instanciada no meu aplicativo "onCreate". Eu tenho um serviço de sincronização no qual crio o novo "RealmHelper" porque o Realm é "Thread-Linked" e uma Instância de Realm não pode funcionar dentro de um Thread diferente. Portanto, para seguir a Documentação de Região "Você precisa fechar todas as instâncias de região abertas para evitar vazamentos de recursos do sistema", para fazer isso, usei o "Application.ActivityLifecycleCallbacks" como você pode ver acima.
Finalmente, tenho um receptor que é acionado quando eu terminar de sincronizar meu aplicativo e, quando terminar a sincronização, ele chamará o método "IEndSyncCallback" "onEndSync" no qual procuro se tenho uma Classe de Atividade específica na minha Lista de ActivitiesBackStack porque preciso para atualizar os dados na exibição, se a sincronização os atualizou e eu precisar executar outras operações após a sincronização do aplicativo.
Isso é tudo, espero que isso seja útil. Vejo você :)
A resposta por waqas716 é boa. Criei uma solução alternativa para um caso específico que exige menos código e manutenção.
Encontrei uma solução específica ao fazer com que um método estático busque uma visualização da atividade que suspeito estar em primeiro plano. Você pode percorrer todas as atividades e verificar se deseja ou obter o nome da atividade na resposta de martin
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
Verifico se a visualização não é nula e obtém o contexto 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, ..."
em developer.android.com/reference/android/app/...
Não gosto de nenhuma das outras respostas. O ActivityManager não deve ser usado para obter a atividade atual. Super classificar e, dependendo do onDestroy, também é frágil e não é o melhor design.
Honestamente, o melhor que eu criei até agora é apenas manter uma enumeração no meu Aplicativo, que é definida quando uma atividade é criada.
Outra recomendação pode ser evitar o uso de várias atividades, se possível. Isso pode ser feito com o uso de fragmentos ou nas visualizações personalizadas de minha preferência.
Uma solução bastante simples é criar uma classe de gerenciador singleton, na qual você pode armazenar uma referência a uma ou mais atividades ou qualquer outra coisa à qual deseja acessar em todo o aplicativo.
Ligar UberManager.getInstance().setMainActivity( activity );
para o onCreate da atividade principal.
Ligue para UberManager.getInstance().getMainActivity();
qualquer lugar do seu aplicativo para recuperá-lo. (Estou usando isso para poder usar o Toast a partir de um thread que não seja da interface do usuário.)
Adicione uma chamada para UberManager.getInstance().cleanup();
quando seu aplicativo estiver sendo destruído.
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;
}
}
Estou com 3 anos de atraso, mas responderei de qualquer maneira, caso alguém encontre isso como eu.
Eu resolvi isso simplesmente usando isso:
if (getIntent().toString().contains("MainActivity")) {
// Do stuff if the current activity is MainActivity
}
Observe que "getIntent (). ToString ()" inclui vários outros textos, como o nome do seu pacote e quaisquer filtros de intenção para sua atividade. Tecnicamente, estamos verificando a intenção atual, não a atividade, mas o resultado é o mesmo. Basta usar, por exemplo, Log.d ("test", getIntent (). ToString ()); se você quiser ver todo o texto. Essa solução é um pouco hacky, mas é muito mais limpa no seu código e a funcionalidade é a mesma.