เมื่อใดก็ตามที่การออกอากาศของฉันถูกดำเนินการฉันต้องการที่จะแสดงการแจ้งเตือนให้ทำกิจกรรมเบื้องหน้า
เมื่อใดก็ตามที่การออกอากาศของฉันถูกดำเนินการฉันต้องการที่จะแสดงการแจ้งเตือนให้ทำกิจกรรมเบื้องหน้า
คำตอบ:
รู้ว่า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 อย่างเป็นทางการใน API 14: ดูคำตอบนี้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
ใน Android อีกเลย GC จะรวบรวมพวกเขาได้เร็วขึ้นจากนั้นคุณคิดว่า
WeakReference
ไม่แนะนำให้ใช้กับการแคชนี่ไม่ใช่การแคชนั่นคือความmCurrentActivity
ประสงค์จะมีการอ้างอิงถึงมันเมื่อมันยังมีชีวิตอยู่ดังนั้นWeakReference
จะไม่มีการรวบรวมในขณะที่Activity
อยู่ด้านบน อย่างไรก็ตามสิ่งที่ @NachoColoma แนะนำผิดเพราะWeakReference
อาจยังคงอ้างอิงกิจกรรมที่ไม่ได้ดำเนินการต่อ (ไม่ใช่มีชีวิตอยู่ / ไม่อยู่ด้านบน) หากตัวแปรไม่ถูกล้าง!
Application .ActivityLifecycleCallbacks
ซึ่งจะเป็นศูนย์กลางมากขึ้นและคุณไม่จำเป็นต้องเพิ่มรหัสการจัดการใด ๆ ในทุกกิจกรรมของคุณ ดูได้ที่developer.android.com/reference/android/app/…
ฉันขยายคำตอบที่ด้านบนของ @ gezdy
ในทุกกิจกรรมแทนที่จะต้อง "ลงทะเบียน" ด้วยApplication
การเข้ารหัสด้วยตนเองเราสามารถใช้ API ต่อไปนี้ตั้งแต่ระดับ 14 เพื่อช่วยให้เราบรรลุวัตถุประสงค์ที่คล้ายกันโดยใช้การเข้ารหัสด้วยตนเองน้อยลง
public void registerActivityLifecycleCallbacks (Application.ActivityLifecycleCallbacks callback)
ในApplication.ActivityLifecycleCallbacks
คุณจะได้รับซึ่งActivity
คือ "แนบ" หรือ "แฝด" Application
นี้
อย่างไรก็ตามเทคนิคนี้ใช้ได้เฉพาะตั้งแต่ API ระดับ 14
implements Application.ActivityLifecycleCallbacks
และเพิ่มวิธีการที่จะใช้ จากนั้นในตัวสร้างคลาส '(หรือ onCreate หรือ init หรือวิธีอื่นที่รันเมื่ออินสแตนซ์กำลังทำงานอยู่ / พร้อมใช้งาน) ให้ใส่getApplication().registerActivityLifecycleCallbacks(this);
เป็นบรรทัดสุดท้าย
อัปเดต 2 : มีการเพิ่ม API อย่างเป็นทางการสำหรับเรื่องนี้โปรดใช้ActivityLifecycleCallbacksแทน
UPDATE:
ตามที่ @gezdy ชี้และฉันรู้สึกขอบคุณสำหรับสิ่งนั้น ตั้งค่าการอ้างอิงเป็น nullสำหรับกิจกรรมปัจจุบันแทนที่จะอัปเดตในทุก ๆ onResume ตั้งค่าเป็นโมฆะในทุกกิจกรรมของ onDestroy เพื่อหลีกเลี่ยงปัญหาการรั่วไหลของหน่วยความจำ
ไม่นานมานี้ฉันต้องการฟังก์ชั่นเดียวกันและนี่คือวิธีการที่ฉันประสบความสำเร็จ ในทุกกิจกรรมของคุณจะแทนที่วิธีวงจรชีวิตเหล่านี้
@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ขอบคุณสำหรับการอัปเดต
นี่ใช้งานไม่ได้ 100% ใน api เวอร์ชั่น 16 ถ้าคุณอ่านโค้ดบน gitHub ฟังก์ชั่น "currentActivityThread" นั้นถูกเปลี่ยนใน Kitkat ดังนั้นฉันอยากจะบอกว่ารุ่น 19ish ยากที่จะจับคู่ api version ออกมาใน github .
มีการเข้าถึงปัจจุบันActivity
มีประโยชน์มาก จะดีไหมถ้ามีgetActivity
วิธีสแตติกคืนกิจกรรมปัจจุบันโดยไม่มีคำถามที่ไม่จำเป็น?
Activity
ระดับเป็นประโยชน์อย่างมาก มันช่วยให้สามารถเข้าถึงเธรด UI ของแอปพลิเคชันมุมมองทรัพยากรและอื่น ๆ อีกมากมาย วิธีการมากมายต้องใช้Context
แต่วิธีรับตัวชี้? นี่คือวิธีการบางอย่าง:
ActivityThread
มีระดับที่เรียกว่าเป็น 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 - แอพไฮบริด)
class ReactContext ใน ReactNative มีชุดตรรกะทั้งหมดเพื่อรักษา mCurrentActivity ซึ่งส่งคืนใน getCurrentActivity ()
หมายเหตุ: ฉันต้องการ getCurrentActivity () นำไปใช้ในคลาสแอปพลิเคชัน Android
ฉันไม่สามารถหาวิธีแก้ปัญหาที่ทีมของเราจะมีความสุขด้วยดังนั้นเราจึงรีดของเราเอง เราใช้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" เพื่อมี "Backstack" ของกิจกรรมทั้งหมดของฉัน
ถ้าคุณต้องการตรวจสอบว่ากิจกรรมใดเป็นกิจกรรมปัจจุบันคุณเพียงแค่ต้องรับคลาสกิจกรรมล่าสุดในรายการ
สร้างแอปพลิเคชันที่ขยาย "แอปพลิเคชัน" และทำสิ่งนี้:
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" เพื่อ:
Bind / Unbind Merlin Instance (ใช้เพื่อรับเหตุการณ์เมื่อแอปขาดหรือรับการเชื่อมต่อตัวอย่างเช่นเมื่อคุณปิดข้อมูลมือถือหรือเมื่อคุณเปิด) มันจะมีประโยชน์หลังจากการกระทำที่ตั้งใจ "OnConnectivityChanged" ถูกปิดใช้งาน สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ MERLIN โปรดดู: MERLIN INFO LINK
ปิด Realm Instance สุดท้ายของฉันเมื่อปิดแอปพลิเคชัน ฉันจะเริ่มต้นมันภายใน BaseActivity ซึ่งขยายจากกิจกรรมอื่น ๆ ทั้งหมดและมีอินสแตนซ์ของ RealmHelper ส่วนตัว สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ REALM โปรดดู: REALM INFO LINK ตัวอย่างเช่นฉันมีอินสแตนซ์ "RealmHelper" แบบคงที่ในคลาส "RealmHelper" ของฉันซึ่งมีอินสแตนซ์ภายในแอปพลิเคชันของฉัน "onCreate" ฉันมีบริการการซิงโครไนซ์ที่ฉันสร้างฉันใหม่ "RealmHelper" เนื่องจากอาณาจักรนั้นเป็น "การเชื่อมโยงเธรด" และอินสแตนซ์ของอาณาจักรไม่สามารถทำงานภายในเธรดอื่นได้ ดังนั้นในการติดตามเอกสารของ Realm "คุณต้องปิดอินสแตนซ์ของอาณาจักรที่เปิดทั้งหมดเพื่อหลีกเลี่ยงการรั่วไหลของทรัพยากรระบบ" เพื่อบรรลุสิ่งนี้ฉันใช้ "Application.ActivityLifecycleCallbacks" ตามที่คุณเห็น
ในที่สุดฉันก็มีผู้รับที่ถูกทริกเกอร์เมื่อฉันเสร็จสิ้นการประสานใบสมัครของฉันแล้วเมื่อสิ้นสุดการซิงค์มันจะเรียกวิธีการ "IEndSyncCallback" "onEndSync" ซึ่งฉันดูว่าฉันมีคลาสกิจกรรมเฉพาะภายในรายการกิจกรรมของฉัน เพื่ออัปเดตข้อมูลในมุมมองหากการซิงค์อัปเดตและฉันอาจต้องดำเนินการอื่นหลังจากการซิงค์แอป
หวังว่าสิ่งนี้จะเป็นประโยชน์ เจอกัน :)
คำตอบโดยwaqas716นั้นดี ฉันสร้างวิธีแก้ปัญหาสำหรับกรณีเฉพาะที่ต้องการรหัสและการบำรุงรักษาน้อยลง
ฉันพบการทำงานที่เฉพาะเจาะจงโดยมีวิธีการแบบคงที่ดึงมุมมองจากกิจกรรมที่ฉันสงสัยว่าจะอยู่เบื้องหน้า คุณสามารถวนซ้ำกิจกรรมทั้งหมดและตรวจสอบว่าคุณต้องการหรือรับชื่อกิจกรรมจากคำตอบของมาร์ติน
ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
ฉันจะตรวจสอบว่ามุมมองนั้นไม่ว่างและรับบริบทผ่าน 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 ไม่ได้มีไว้เพื่อใช้ในการรับกิจกรรมปัจจุบัน การจัดระดับสุดยอดและขึ้นอยู่กับ Devroy นั้นเปราะบางและไม่ใช่การออกแบบที่ดีที่สุด
ความจริงแล้วสิ่งที่ดีที่สุดที่ฉันเคยได้รับคือการรักษา enum ในแอปพลิเคชันของฉันซึ่งได้รับการตั้งค่าเมื่อมีการสร้างกิจกรรม
ข้อเสนอแนะอีกประการหนึ่งก็อาจจะห่างจากการใช้กิจกรรมหลายอย่างถ้าเป็นไปได้ สิ่งนี้สามารถทำได้ด้วยการใช้ชิ้นส่วนหรือในมุมมองที่กำหนดเองการตั้งค่าของฉัน
วิธีแก้ปัญหาที่ค่อนข้างง่ายคือการสร้างคลาสผู้จัดการแบบซิงเกิลซึ่งคุณสามารถจัดเก็บการอ้างอิงไปยังกิจกรรมหนึ่งรายการหรือมากกว่าหรือสิ่งอื่นที่คุณต้องการเข้าถึงได้ทั่วทั้งแอพ
โทรUberManager.getInstance().setMainActivity( activity );
ในกิจกรรมหลักของ onCreate
โทรไปUberManager.getInstance().getMainActivity();
ที่ใดก็ได้ในแอปของคุณเพื่อรับข้อมูล (ฉันใช้สิ่งนี้เพื่อให้สามารถใช้ Toast จากเธรด 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 ()); ถ้าคุณต้องการที่จะเห็นข้อความทั้งหมด วิธีนี้ค่อนข้างแฮ็ค แต่มันก็ดีกว่าในโค้ดของคุณและฟังก์ชั่นก็เหมือนกัน