मैं अपने एंड्रॉइड ऐप में एक स्टेटस बार नोटिफिकेशन कर रहा हूं जो कि c2dm द्वारा ट्रिगर किया गया है। यदि ऐप चल रहा है तो मैं अधिसूचना प्रदर्शित नहीं करना चाहता। यदि एप्लिकेशन चल रहा है और अग्रभूमि में है तो आप कैसे निर्धारित करते हैं?
मैं अपने एंड्रॉइड ऐप में एक स्टेटस बार नोटिफिकेशन कर रहा हूं जो कि c2dm द्वारा ट्रिगर किया गया है। यदि ऐप चल रहा है तो मैं अधिसूचना प्रदर्शित नहीं करना चाहता। यदि एप्लिकेशन चल रहा है और अग्रभूमि में है तो आप कैसे निर्धारित करते हैं?
जवाबों:
की तरह एक वैश्विक चर बनाने private boolean mIsInForegroundMode;
और एक आवंटित false
में मूल्य onPause()
और एक true
में मूल्य onResume()
।
नमूना कोड:
private boolean mIsInForegroundMode;
@Override
protected void onPause() {
super.onPause();
mIsInForegroundMode = false;
}
@Override
protected void onResume() {
super.onResume();
mIsInForegroundMode = true;
}
// Some function.
public boolean isInForeground() {
return mIsInForegroundMode;
}
वैकल्पिक रूप से, आप विधि ActivityManager
द्वारा चलाए जा रहे कार्यों की जांच कर सकते हैं getRunningTasks
। फिर दिए गए कार्यों की सूची में पहले कार्य (अग्रभूमि में कार्य) के साथ जांचें, यदि यह आपका कार्य है।
यहाँ कोड उदाहरण है:
public Notification buildNotification(String arg0, Map<String, String> arg1) {
ActivityManager activityManager = (ActivityManager) appContext.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager
.getRunningTasks(Integer.MAX_VALUE);
boolean isActivityFound = false;
if (services.get(0).topActivity.getPackageName().toString()
.equalsIgnoreCase(appContext.getPackageName().toString())) {
isActivityFound = true;
}
if (isActivityFound) {
return null;
} else {
// write your code to build a notification.
// return the notification you built here
}
}
और उपरोक्त कोड में विधि को चलाने में सक्षम होने के लिए मैनिफ़ेस्टGET_TASKS
. xml फ़ाइल में अनुमति जोड़ना न भूलें getRunningTasks()
:
<uses-permission android:name="android.permission.GET_TASKS" />
p / s: यदि इस तरह से सहमत हैं, तो कृपया ध्यान दें कि यह अनुमति अब पदावनत है।
toString()
अ स्ट्रींग पर कॉल getPackageName()
करना निरर्थक है। इसके अलावा, जैसा कि हम केवल पहले कार्य में रुचि रखते हैं getRunningTasks()
, हम 1
इसके बजाय पास कर सकते हैं Integer.MAX_VALUE
।
यह एक बहुत पुरानी पोस्ट है लेकिन अभी भी काफी प्रासंगिक है। उपरोक्त स्वीकृत समाधान काम कर सकता है लेकिन गलत है।जैसा कि डायने हैकॉर्न ने लिखा है:
ये एपीआई अपने यूआई प्रवाह को आधार बनाने के लिए अनुप्रयोगों के लिए नहीं हैं, लेकिन उन चीजों को करने के लिए जैसे उपयोगकर्ता को चल रहे एप्लिकेशन, या एक कार्य प्रबंधक, या इस तरह से दिखाते हैं।
हाँ इन बातों के लिए स्मृति में रखी गई एक सूची है। हालाँकि, यह एक अन्य प्रक्रिया में बंद है, आपके द्वारा अलग से चल रहे थ्रेड्स द्वारा प्रबंधित किया जाता है, और कुछ ऐसा नहीं है जिस पर आप (a) सही निर्णय लेने के लिए समय में देख सकते हैं (b) आपके पास लौटने के समय तक एक सुसंगत तस्वीर है। प्लस "जाने के लिए" अगली "गतिविधि के बारे में निर्णय हमेशा उस बिंदु पर किया जाता है जहां स्विच होना है, और यह उस सटीक बिंदु तक नहीं है (जहां गतिविधि राज्य संक्षेप में स्विच करने के लिए बंद है) कि हम वास्तव में इस तरह के लिए जानते हैं कि अगली चीज क्या होगी।
और यहां के कार्यान्वयन और वैश्विक व्यवहार भविष्य में समान रहने की गारंटी नहीं है।
सही समाधान को लागू करना है: एक्टिविटीलाइफ साइकल कॉलबैक ।
यह मूल रूप से एक एप्लिकेशन क्लास की जरूरत है और ऐप में आपकी गतिविधियों की स्थिति की पहचान करने के लिए हैंडलर को वहां सेट किया जा सकता है।
onPause
और onResume
विधि से अलग कैसे है ?
जैसा कि विनय कहते हैं, शायद सबसे अच्छा समाधान (नए एंड्रॉइड संस्करणों का समर्थन करने के लिए, 14+) वर्ग कार्यान्वयन ActivityLifecycleCallbacks
में उपयोग Application
करना है।
package com.telcel.contenedor.appdelegate;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
/** Determines global app lifecycle states.
*
* The following is the reference of activities states:
*
* The <b>visible</b> lifetime of an activity happens between a call to onStart()
* until a corresponding call to onStop(). During this time the user can see the
* activity on-screen, though it may not be in the foreground and interacting with
* the user. The onStart() and onStop() methods can be called multiple times, as
* the activity becomes visible and hidden to the user.
*
* The <b>foreground</b> lifetime of an activity happens between a call to onResume()
* until a corresponding call to onPause(). During this time the activity is in front
* of all other activities and interacting with the user. An activity can frequently
* go between the resumed and paused states -- for example when the device goes to
* sleep, when an activity result is delivered, when a new intent is delivered --
* so the code in these methods should be fairly lightweight.
*
* */
public class ApplicationLifecycleManager implements ActivityLifecycleCallbacks {
/** Manages the state of opened vs closed activities, should be 0 or 1.
* It will be 2 if this value is checked between activity B onStart() and
* activity A onStop().
* It could be greater if the top activities are not fullscreen or have
* transparent backgrounds.
*/
private static int visibleActivityCount = 0;
/** Manages the state of opened vs closed activities, should be 0 or 1
* because only one can be in foreground at a time. It will be 2 if this
* value is checked between activity B onResume() and activity A onPause().
*/
private static int foregroundActivityCount = 0;
/** Returns true if app has foreground */
public static boolean isAppInForeground(){
return foregroundActivityCount > 0;
}
/** Returns true if any activity of app is visible (or device is sleep when
* an activity was visible) */
public static boolean isAppVisible(){
return visibleActivityCount > 0;
}
public void onActivityCreated(Activity activity, Bundle bundle) {
}
public void onActivityDestroyed(Activity activity) {
}
public void onActivityResumed(Activity activity) {
foregroundActivityCount ++;
}
public void onActivityPaused(Activity activity) {
foregroundActivityCount --;
}
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
public void onActivityStarted(Activity activity) {
visibleActivityCount ++;
}
public void onActivityStopped(Activity activity) {
visibleActivityCount --;
}
}
और आवेदन onCreate()
विधि में:
registerActivityLifecycleCallbacks(new ApplicationLifecycleManager());
तब ApplicationLifecycleManager.isAppVisible()
या ApplicationLifecycleManager.isAppInForeground()
इच्छित स्थिति को जानने के लिए उपयोग किया जाएगा।
एपीआई 16 के बाद से आप इसे इस तरह से कर सकते हैं:
static boolean shouldShowNotification(Context context) {
RunningAppProcessInfo myProcess = new RunningAppProcessInfo();
ActivityManager.getMyMemoryState(myProcess);
if (myProcess.importance != RunningAppProcessInfo.IMPORTANCE_FOREGROUND)
return true;
KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
// app is in foreground, but if screen is locked show notification anyway
return km.inKeyguardRestrictedInputMode();
}
गैडेनकॉन के समाधान के संस्करण को थोड़ा साफ किया । इसे किसी भी गतिविधि, या शायद अपने सभी गतिविधियों के लिए एक आधार वर्ग रखें।
protected boolean isRunningInForeground() {
ActivityManager manager =
(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = manager.getRunningTasks(1);
if (tasks.isEmpty()) {
return false;
}
String topActivityName = tasks.get(0).topActivity.getPackageName();
return topActivityName.equalsIgnoreCase(getPackageName());
}
कॉल करने में सक्षम होने के लिए getRunningTasks()
, आपको इसे अपने में जोड़ना होगा AndroidManifest.xml
:
<uses-permission android:name="android.permission.GET_TASKS"/>
ध्यान दें कि ActivityManager.getRunningTasks()
जवादोक क्या कहता है:
नोट: यह विधि केवल डीबगिंग और कार्य प्रबंधन उपयोगकर्ता इंटरफ़ेस प्रस्तुत करने के लिए है। किसी अनुप्रयोग में मुख्य तर्क के लिए इसका उपयोग कभी नहीं किया जाना चाहिए, जैसे कि यहां मिली जानकारी के आधार पर विभिन्न व्यवहारों के बीच निर्णय लेना। इस तरह के उपयोग समर्थित नहीं हैं, और भविष्य में होने की संभावना है।
ध्यान दें कि एपीआई स्तर 21 में पदावनत कियाgetRunningTasks()
गया था !
चूंकि
LOLLIPOP
यह विधि अब तृतीय पक्ष एप्लिकेशन के लिए उपलब्ध नहीं है: दस्तावेज़-केंद्रित पुनरावृत्ति की शुरुआत का अर्थ है कि यह व्यक्ति की जानकारी कॉलर को लीक कर सकता है। पीछे की संगतता के लिए, यह अभी भी अपने डेटा का एक छोटा सबसेट लौटाएगा: कम से कम कॉलर के स्वयं के कार्य, और संभवतः घर जैसे कुछ अन्य कार्य जो संवेदनशील नहीं होने के लिए जाने जाते हैं।
इसलिए जो मैंने पहले लिखा था वह और भी अधिक प्रासंगिक है:
कई मामलों में आप बेहतर समाधान के साथ आ सकते हैं। उदाहरण के लिए, में कुछ कर रही onPause()
है और onResume()
, शायद अपने सभी गतिविधियों के लिए एक BaseActivity में।
(यदि हम अग्रभूमि में नहीं हैं, तो हमारे मामले में हम एक ऑफ़लाइन अलर्ट गतिविधि शुरू नहीं करना चाहते हैं, इसलिए बेसएक्टिविटी में onPause()
हम केवल Subscription
"ऑफ़लाइन हो गए" संकेत सुनकर आरएक्सजेवा से अनसब्सक्राइब करते हैं ।)
गैडेनकॉन के जवाब के बाद मुझे कुछ इस तरह की जरूरत पड़ी, ताकि मैं यह बता सकूं कि क्या मेरा ऐप अग्रभूमि में नहीं चल रहा है, लेकिन मुझे कुछ ऐसा चाहिए था, जो ऐप चौड़ा हो और मुझे अपने आवेदन के दौरान झंडे सेट करने / परेशान करने की आवश्यकता न हो।
गैडेनकॉन का कोड बहुत ज्यादा नाखून को सिर पर मारता था, लेकिन यह मेरे ही अंदाज में नहीं था और ऐसा लगा कि यह ख़राब हो सकता है, इसलिए मेरे ऐप में इसकी वजह से यह कम हो गया।
if (!context.getPackageName().equalsIgnoreCase(((ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0).topActivity.getPackageName()))
{
// App is not in the foreground
}
(साइड नोट: आप अभी हटा सकते हैं! यदि आप चाहते हैं कि चेक दूसरी तरह से काम करे)
हालांकि इस दृष्टिकोण के साथ आपको GET_TASKS
अनुमति की आवश्यकता है ।
समर्थन लाइब्रेरी संस्करण 26 को प्रारंभ करके आप अनुप्रयोग वर्तमान स्थिति का निर्धारण करने के लिए ProcessLifecycleOwner का उपयोग कर सकते हैं , बस इसे अपनी निर्भरता में जोड़ें जैसे कि यहाँ वर्णित है , उदाहरण के लिए:
dependencies {
def lifecycle_version = "1.1.1"
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
// alternatively - Lifecycles only (no ViewModel or LiveData).
// Support library depends on this lightweight import
implementation "android.arch.lifecycle:runtime:$lifecycle_version"
annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version" // use kapt for Kotlin
}
, अब आप क्वेरी कर सकते हैं ProcessLifecycleOwner
जब भी आप ऐप राज्य की जांच करना चाहते हैं, उदाहरण के लिए यह जांचने के लिए कि ऐप अग्रभूमि में चल रहा है या नहीं, आपको बस यह करना है:
boolean isAppInForeground = ProcessLifecycleOwner.get().getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);
if(!isAppInForeground)
//Show Notification in status bar
implementation 'androidx.lifecycle:lifecycle-process:2.2.0'
अपने प्रोजेक्ट ग्रेडेल में उपयोग करें।
विभिन्न उत्तरों और टिप्पणियों के आधार पर, यहां एक अधिक इनलाइन संस्करण है जिसे आप एक सहायक वर्ग में जोड़ सकते हैं:
public static boolean isAppInForeground(Context context) {
List<RunningTaskInfo> task =
((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE))
.getRunningTasks(1);
if (task.isEmpty()) {
return false;
}
return task
.get(0)
.topActivity
.getPackageName()
.equalsIgnoreCase(context.getPackageName());
}
जैसा कि अन्य उत्तरों में बताया गया है कि आपको निम्नलिखित अनुमति को अपने साथ जोड़ना होगा AndroidManifest.xml
।
<uses-permission android:name="android.permission.GET_TASKS"/>
मैं यह करना चाहूंगा कि ऐसा करने का एक सुरक्षित तरीका है - यह जाँचने की तुलना में कि क्या आपका ऐप नोटिफिकेशन बनाने से पहले बैकग्राउंड में है - क्रमशः ब्रॉडकास्ट रिसीवर ऑनपॉज () और ऑनस्क्यूम () पर डिसेबल करना है।
यह विधि आपको वास्तविक एप्लिकेशन लॉजिक में अधिक नियंत्रण प्रदान करती है और भविष्य में बदलने की संभावना नहीं है।
@Override
protected void onPause() {
unregisterReceiver(mHandleMessageReceiver);
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(mHandleMessageReceiver, new IntentFilter(DISPLAY_MESSAGE_ACTION));
}
यहाँ @ user2690455 द्वारा ऊपर वर्णित अच्छे सरल समाधान के लिए कोड है। हालाँकि यह थोड़ा सा दिखता है, लेकिन आप देखेंगे कि यह वास्तव में काफी हल्का है
मेरे मामले में हम AppCompatActivity का भी उपयोग करते हैं, इसलिए मेरे पास 2 आधार कक्षाएं होनी चाहिए।
public class BaseActivity extends Activity {
/**
* Let field be set only in base class
* All callers must use accessors,
* and then it's not up to them to manage state.
*
* Making it static since ..
* 1. It needs to be used across two base classes
* 2. It's a singleton state in the app
*/
private static boolean IS_APP_IN_BACKGROUND = false;
@Override
protected void onResume() {
super.onResume();
BaseActivity.onResumeAppTracking(this);
BaseActivity.setAppInBackgroundFalse();
}
@Override
protected void onStop() {
super.onStop();
BaseActivity.setAppInBackgroundTrue();
}
@Override
protected void onPause() {
super.onPause();
BaseActivity.setAppInBackgroundFalse();
}
protected static void onResumeAppTracking(Activity activity) {
if (BaseActivity.isAppInBackground()) {
// do requirements for returning app to foreground
}
}
protected static void setAppInBackgroundFalse() {
IS_APP_IN_BACKGROUND = false;
}
protected static void setAppInBackgroundTrue() {
IS_APP_IN_BACKGROUND = true;
}
protected static boolean isAppInBackground() {
return IS_APP_IN_BACKGROUND;
}
}
यह तभी उपयोगी है जब आप अपनी गतिविधि शुरू होने पर कुछ कार्रवाई करना चाहते हैं और इसकी जांच करना चाहते हैं कि क्या ऐप अग्रभूमि या पृष्ठभूमि में है।
गतिविधि प्रबंधक का उपयोग करने के बजाय एक सरल चाल है जिसे आप कोड के माध्यम से कर सकते हैं। यदि आप गतिविधि चक्र का बारीकी से निरीक्षण करते हैं, तो दो गतिविधियों और पृष्ठभूमि के अग्रभूमि के बीच का प्रवाह निम्नानुसार है। मान लीजिए कि A और B दो गतिविधियाँ हैं।
जब A से B में संक्रमण होता है तो A के 2. onPause () को 2. कहा जाता है। onResume () को B कहा जाता है। A का B
जब ऐप बैकग्राउंड में जाता है: 1. A के ऑनपॉज़ () को कॉल किया जाता है। A का 2. onStop () कहा जाता है
आप बस गतिविधि में एक झंडा लगाकर अपनी पृष्ठभूमि की घटना का पता लगा सकते हैं।
एक अमूर्त गतिविधि करें और इसे अपनी अन्य गतिविधियों से बढ़ाएँ, ताकि आपको जहाँ भी पृष्ठभूमि की घटना हो, आपको अन्य सभी गतिविधियों के लिए कोड पेस्ट करना पड़े।
अमूर्त गतिविधि में झंडा isAppInBackground बनाएँ।
ऑनक्रिएट () विधि: isAppInBackground = false;
ऑनपॉज़ () विधि में: isAppInBackground = false;
OnStop () विधि: isAppInBackground = true;
आपको बस अपने onResume () में जांच करने की आवश्यकता है अगर isAppInBackground सच है। n अपने ध्वज की जाँच करने के बाद फिर से isAppInBackground = false सेट करें
OnSTop () के बाद से दो गतिविधियों के बीच संक्रमण के लिए पहले को हमेशा दूसरे एक्टिविटी रिज्यूमे के बाद बुलाया जाएगा, झंडा कभी भी सही नहीं होगा और जब ऐप बैकग्राउंड में होगा, तो onPtop के तुरंत बाद गतिविधि के onStop () को कॉल किया जाएगा और इसलिए झंडा तभी सही होगा जब आप बाद में ऐप खोलें।
इस दृष्टिकोण में एक और परिदृश्य है। यदि आपकी कोई ऐप स्क्रीन पहले से खुली हुई है और आपने मोबाइल को निष्क्रिय कर दिया है तो कुछ समय बाद मोबाइल स्लीप मोड में चला जाएगा और जब आप मोबाइल को अनलॉक करेंगे, तो इसका इलाज बैकग्राउंड इवेंट में किया जाएगा।
यहाँ एक विधि है जिसका मैं उपयोग करता हूँ (और सहायक विधि):
private boolean checkIfAppIsRunningInForeground() {
ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
for(ActivityManager.RunningAppProcessInfo appProcessInfo : activityManager.getRunningAppProcesses()) {
if(appProcessInfo.processName.contains(this.getPackageName())) {
return checkIfAppIsRunningInForegroundByAppImportance(appProcessInfo.importance);
}
}
return false;
}
private boolean checkIfAppIsRunningInForegroundByAppImportance(int appImportance) {
switch (appImportance) {
//user is aware of app
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE:
return true;
//user is not aware of app
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_EMPTY:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE:
case ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE:
default:
return false;
}
}
इसके लिए कोई वैश्विक कॉलबैक नहीं है, लेकिन प्रत्येक गतिविधि के लिए यह onStop () है। आप एक परमाणु int के साथ गड़बड़ करने की जरूरत नहीं है। बस शुरू की गतिविधियों की संख्या के साथ एक वैश्विक int है, हर गतिविधि में इसे बढ़ाएँ () और इसे onStop () में घटाएँ।
इसका पालन करें
public static boolean isAppRunning(Context context) {
// check with the first task(task in the foreground)
// in the returned list of tasks
ActivityManager activityManager = (ActivityManager)
context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services =
activityManager.getRunningTasks(Integer.MAX_VALUE);
if
(services.get(0).topActivity.getPackageName().toString().equalsIgnoreCase(context.getPackageName().toString()))
{
return true;
}
return false;
}
यहां बताए गए पिछले दृष्टिकोण इष्टतम नहीं हैं। कार्य आधारित दृष्टिकोण के लिए एक अनुमति की आवश्यकता होती है जो वांछित नहीं हो सकती है और "बूलियन" दृष्टिकोण समवर्ती संशोधन गड़बड़ी से ग्रस्त है।
मैं जिस दृष्टिकोण का उपयोग करता हूं और जो (मेरा मानना है कि) ज्यादातर मामलों में काफी अच्छा काम करता है:
AtomicInteger में गतिविधि गणना को ट्रैक करने के लिए "MainApplication" वर्ग रखें :
import android.app.Application;
import java.util.concurrent.atomic.AtomicInteger;
public class MainApplication extends Application {
static class ActivityCounter {
private static AtomicInteger ACTIVITY_COUNT = new AtomicInteger(0);
public static boolean isAppActive() {
return ACTIVITY_COUNT.get() > 0;
}
public static void activityStarted() {
ACTIVITY_COUNT.incrementAndGet();
}
public static void activityStopped() {
ACTIVITY_COUNT.decrementAndGet();
}
}
}
और एक आधार गतिविधि वर्ग बनाएँ जो अन्य गतिविधियों का विस्तार करे:
import android.app.Activity;
import android.support.annotation.CallSuper;
public class TestActivity extends Activity {
@Override
@CallSuper
protected void onStart() {
MainApplication.ActivityCounter.activityStarted();
super.onStart();
}
@Override
@CallSuper
protected void onStop() {
MainApplication.ActivityCounter.activityStopped();
super.onStop();
}
}