내 방송이 실행될 때마다 포 그라운드 활동에 경고를 표시하고 싶습니다.
내 방송이 실행될 때마다 포 그라운드 활동에 경고를 표시하고 싶습니다.
답변:
알면 ActivityManager가 관리하는 활동을 , 그래서 우리는 정보 얻을 수 ActivityManager를 . 우리는 현재 포 그라운드에서 활동을 실행합니다.
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
업데이트 2018/10/03
getRunningTasks ()가 더 이상 사용되지 않습니다. 아래 솔루션을 참조하십시오.
이 메소드는 API 레벨 21에서 더 이상 사용되지 않습니다. Build.VERSION_CODES.LOLLIPOP부터이 메소드는 더 이상 써드 파티 애플리케이션에서 사용할 수 없습니다. 문서 중심의 최근 통화가 도입되면 발신자에게 개인 정보가 유출 될 수 있습니다. 이전 버전과의 호환성을 위해 최소한 발신자의 자체 작업과 민감한 것으로 알려진 집과 같은 다른 작업의 데이터의 작은 하위 집합을 반환합니다.
( 참고 : API 14에 공식 API가 추가되었습니다 : https://stackoverflow.com/a/29786451/119733 참조 )
이전 (waqas716) 답변을 사용하지 마십시오.
활동에 대한 정적 참조로 인해 메모리 누수 문제가 발생합니다. 자세한 내용은 다음 링크를 참조 하십시오 http://android-developers.blogspot.fr/2009/01/avoiding-memory-leaks.html
이를 피하려면 활동 참조를 관리해야합니다. 매니페스트 파일에 응용 프로그램 이름을 추가하십시오.
<application
android:name=".MyApp"
....
</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;
}
}
새로운 활동 만들기 :
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);
}
}
따라서 활동에 대한 활동 클래스를 확장하는 대신 MyBaseActivity를 확장하십시오. 이제 응용 프로그램 또는 다음과 같은 활동 컨텍스트에서 현재 활동을 가져올 수 있습니다.
Activity currentActivity = ((MyApp)context.getApplicationContext()).getCurrentActivity();
WeakReferences
GC는 안드로이드에서 사용하기 를 결코 권장하지 않습니다 .
WeakReference
캐싱에 권장되지 않습니다. 이것은 캐싱이 아닙니다. 즉, mCurrentActivity
살아있는 경우에만 참조가 있으므로의 위에는 WeakReference
수집되지 않습니다 Activity
. 그러나 @NachoColoma가 제안한 것은 WeakReference
변수가 지워지지 않은 경우 재개되지 않은 (아무것도 아니거나 정상이 아닌) 활동을 여전히 참조 할 수 있기 때문에 잘못 되었습니다!
Application .ActivityLifecycleCallbacks
하는 것이 가능해야합니다 .이 방법은보다 중심적이며 모든 활동에 관리 코드를 추가하지 않아도됩니다. developer.android.com/reference/android/app/…
@gezdy의 답변 맨 위로 확장합니다.
모든 활동에서 Application
수동 코딩으로 자체를 "등록"하는 대신 레벨 14 이후로 다음 API를 사용하여 수동 코딩을 줄이면서 유사한 목적을 달성 할 수 있습니다.
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
에서 Application.ActivityLifecycleCallbacks
, Activity
"연결된"또는 "분리 된"을 얻을 수 있습니다.Application
.
그러나이 기술은 API 레벨 14 이후에만 사용 가능합니다.
implements Application.ActivityLifecycleCallbacks
그것을 구현하는 메소드를 추가합니다. 그런 다음 해당 클래스의 생성자 (또는 인스턴스가 활성화 / 준비 될 때 실행되는 onCreate 또는 init 또는 기타 메소드) getApplication().registerActivityLifecycleCallbacks(this);
에서 마지막 줄로 입력하십시오.
업데이트 2 : 공식 API가 추가되었으므로 ActivityLifecycleCallbacks를 대신 사용하십시오 .
최신 정보:
@gezdy가 지적한 것처럼, 나는 그것에 대해 감사합니다. 모든 onResume을 업데이트하는 대신 현재 활동에 대해 참조를 null로 설정하여 메모리 누수 문제를 피하기 위해 모든 onResume에서 null로 설정하십시오.
얼마 전 나는 같은 기능이 필요했으며 여기에 내가 달성 한 방법이 있습니다. 모든 활동에서 이러한 수명주기 방법을 대체하십시오.
@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);
}
이제 방송 클래스에서 현재 활동에 액세스하여 경고를 표시 할 수 있습니다.
Application
한 번만 생성되며 정적 변수처럼 가비지 수집되지 않습니다.
clearReferences()
까지 (this.equals(currActivity))
.
@lockwobr 업데이트 해 주셔서 감사합니다
github에서 코드를 읽으면 Kitcurrent에서 "currentActivityThread"함수가 변경되었으므로 api 버전 16에서 100 % 작동하지 않습니다. 따라서 버전 19ish라고 말하고 싶습니다. .
전류에 접근하는 Activity
것이 매우 편리합니다. getActivity
불필요한 질문없이 현재 활동을 반환 하는 정적 메소드 를 갖는 것이 좋지 않습니까?
Activity
클래스는 매우 유용하다. 응용 프로그램의 UI 스레드, 뷰, 리소스 등에 액세스 할 수 있습니다. 수많은 메소드가 필요 Context
하지만 포인터를 얻는 방법은 무엇입니까? 몇 가지 방법이 있습니다.
ActivityThread
있습니다. 이 클래스는 모든 활동에 액세스 할 수 있으며 더 나은 것은 current를 얻는 정적 메소드를 가지고 ActivityThread
있습니다. 한 가지 작은 문제가 있습니다. 활동 목록에는 패키지 액세스 권한이 있습니다.리플렉션을 사용하여 쉽게 해결 :
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;
}
이러한 방법은 앱의 어느 곳에서나 사용할 수 있으며 언급 된 모든 방법보다 훨씬 편리합니다. 또한 보이는 것만 큼 안전하지 않은 것 같습니다. 새로운 잠재적 누출이나 널 포인터를 도입하지 않습니다.
위의 코드 스 니펫에는 예외 처리가 없으며 첫 번째 실행중인 활동이 우리가 찾고있는 활동이라고 순진하게 가정합니다. 추가 검사를 추가 할 수도 있습니다.
Map
인터페이스 대신에 HashMap
또는 ArrayMap
. @AZ_ 답변을 편집했습니다.
나는 Kotlin에서 다음을 수행했습니다.
다음과 같이 응용 프로그램 클래스 편집
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
}
}
}
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
}
}
이제 다음을 호출하여 모든 클래스에서 사용할 수 있습니다. FTApplication.currentActivity()
getCurrentActivity ()도 ReactContextBaseJavaModule에 있습니다.
(이 질문은 처음에 요청되었으므로 많은 Android 앱에도 ReactNative 구성 요소-하이브리드 앱이 있습니다.)
ReactNative의 ReactContext 클래스는 getCurrentActivity ()에 반환되는 mCurrentActivity를 유지하기위한 전체 로직 세트를 갖습니다.
참고 : getCurrentActivity ()가 Android Application 클래스에서 구현되기를 바랍니다.
우리 팀이 만족할만한 해결책을 찾을 수 없었기 때문에 우리는 우리 자신을 굴 렸습니다. 우리 ActivityLifecycleCallbacks
는 현재 활동을 추적 한 다음 서비스를 통해 노출합니다. 자세한 내용은 https://stackoverflow.com/a/38650587/10793
이전 버전과의 호환성을 위해 :
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
에서 핸들을 유지하고 처리 할 수 있지만 원하는 작업이 실행중인 작업 목록 위에 있는지 확인 해야합니다. 이것이 이것이 질문에 완전히 대답하지 못하면 받아 들여진 대답도 그렇지 않습니다. Application
ComponentName
Activity
topActivity
Android Q
개인적으로 "Cheok Yan Cheng"이 말한대로했지만 "List"를 사용하여 모든 활동의 "백 스택"을 사용했습니다.
현재 활동을 확인하려면 목록에서 마지막 활동 클래스를 가져와야합니다.
"응용 프로그램"을 확장하는 응용 프로그램을 작성하고 다음을 수행하십시오.
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() }
}
}
제 경우에는 "Application.ActivityLifecycleCallbacks"를 사용하여 다음을 수행했습니다.
Merlin 인스턴스 바인드 / 바인드 해제 (예 : 모바일 데이터를 닫거나 열 때와 같이 앱의 연결이 끊어 지거나 이벤트가 발생하는 경우 이벤트를 얻는 데 사용). "OnConnectivityChanged"의도 조치가 사용 불가능한 후에 유용합니다. MERLIN에 대한 자세한 내용은 MERLIN INFO LINK를 참조하십시오.
응용 프로그램이 닫히면 마지막 Realm 인스턴스를 닫습니다. BaseActivity 내부에서 초기화하고 다른 모든 활동에서 확장되고 개인 RealmHelper 인스턴스가 있습니다. REALM에 대한 자세한 내용은 다음을 참조하십시오. REALM INFO LINK 예를 들어, "RealmHelper"클래스 내에 정적 "RealmHelper"인스턴스가 있으며이 애플리케이션은 "onCreate"내에서 인스턴스화됩니다. Realm이 "Thread-Linked"이고 Realm 인스턴스가 다른 스레드 내에서 작동 할 수 없기 때문에 새로운 "RealmHelper"를 생성하는 동기화 서비스가 있습니다. 따라서 Realm Documentation "시스템 리소스 누수를 피하기 위해 열려있는 Realm 인스턴스를 모두 닫아야합니다"를 따르기 위해이 작업을 수행하기 위해 "Application.ActivityLifecycleCallbacks"를 사용했습니다.
마지막으로 수신기 동기화가 완료되면 응용 프로그램 동기화가 시작된 다음 동기화 종료시 "IEndSyncCallback" "onEndSync"메서드를 호출하여 필요한 경우 ActivitiesBackStack 목록에 특정 활동 클래스가 있는지 확인합니다. 동기화에서 데이터를 업데이트하고 앱 동기화 후 다른 작업을 수행해야 할 경우 뷰의 데이터를 업데이트합니다.
그게 다야, 이것이 도움이되기를 바랍니다. 당신을 참조하십시오 :)
waqas716 의 답변 이 좋습니다. 코드와 유지 관리가 덜 필요한 특정 사례에 대한 해결 방법을 만들었습니다.
정적 메소드가 포 그라운드에 있다고 생각되는 활동에서 뷰를 가져 와서 특정 해결 방법을 찾았습니다. 모든 활동을 반복하고 원하는 답변이 있는지 확인하거나 Martin의 답변 에서 활동 이름을 가져옵니다.
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
그런 다음 뷰가 null이 아닌지 확인하고 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, ..."
에 developer.android.com/reference/android/app/...
나는 다른 답변을 좋아하지 않습니다. ActivityManager는 현재 활동을 가져 오는 데 사용되지 않습니다. 수퍼 클래 싱 및 onDestroy에 따른 디자인 또한 깨지기 쉬우 며 최고의 디자인은 아닙니다.
솔직히, 지금까지 생각해 낸 최선은 내 응용 프로그램에서 열거 형을 유지하는 것입니다. 활동이 생성 될 때 설정됩니다.
다른 권장 사항은 가능한 경우 여러 활동을 사용하지 않는 것이 좋습니다. 이것은 조각을 사용하거나 선호하는 사용자 정의보기에서 수행 할 수 있습니다.
다소 간단한 해결책은 하나 이상의 활동에 대한 참조 또는 앱 전체에서 액세스하려는 다른 항목을 저장할 수있는 싱글 톤 관리자 클래스를 만드는 것입니다.
UberManager.getInstance().setMainActivity( activity );
주 활동의 onCreate를 호출 하십시오.
UberManager.getInstance().getMainActivity();
앱의 아무 곳이나 호출 하여 앱을 검색하십시오. (UI가 아닌 스레드에서 토스트를 사용할 수 있도록 이것을 사용하고 있습니다.)
UberManager.getInstance().cleanup();
앱이 파괴 될 때 전화를 추가해야합니다 .
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;
}
}
나는 3 년 늦었지만 어쨌든 누군가가 내가 한 것처럼 이것을 발견하면 대답 할 것입니다.
나는 이것을 간단히 사용하여 이것을 해결했다.
if (getIntent().toString().contains("MainActivity")) {
// Do stuff if the current activity is MainActivity
}
"getIntent (). toString ()"에는 패키지 이름 및 활동에 대한 의도 필터와 같은 다른 텍스트가 포함되어 있습니다. 기술적으로 우리는 활동이 아닌 현재 의도를 확인하고 있지만 결과는 같습니다. 예를 들어 Log.d ( "test", getIntent (). toString ()); 모든 텍스트를 보려면 이 솔루션은 약간 해 키지 만 코드에서 훨씬 깨끗하고 기능은 동일합니다.