किसी गतिविधि के कई उदाहरणों को कैसे रोकें जब इसे विभिन्न इंटेंट्स के साथ लॉन्च किया जाए


121

जब मैं इसे Google Play Store ऐप (पहले एंड्रॉइड मार्केट कहा जाता है) पर "ओपन" बटन का उपयोग करके लॉन्च किया गया था, तो मैं अपने आवेदन में एक बग भर में आया हूं । ऐसा लगता है कि इसे प्ले स्टोर Intentसे लॉन्च करना फोन के एप्लिकेशन मेनू से इसे लॉन्च करने से अलग है। यह उसी गतिविधि की कई प्रतियों की ओर अग्रसर हो रहा है, जो एक-दूसरे के साथ परस्पर विरोधी हैं।

उदाहरण के लिए, यदि मेरे ऐप में एक्टिविटी एबीसी शामिल हैं, तो यह समस्या एबीसीए के ढेर को जन्म दे सकती है।

मैंने android:launchMode="singleTask"इस समस्या को ठीक करने के लिए सभी गतिविधियों का उपयोग करने की कोशिश की , लेकिन जब भी मैं होम बटन को हिट करता हूं, तो गतिविधि स्टैक को रूट करने का अवांछित साइड-इफेक्ट होता है।

अपेक्षित व्यवहार है: एबीसी -> गृह -> और जब एप्लिकेशन को पुनर्स्थापित किया जाता है, तो मुझे आवश्यकता है: एबीसी -> गृह -> एबीसी

क्या गृह बटन का उपयोग करते समय रूट गतिविधि को रीसेट किए बिना, एक ही प्रकार की कई गतिविधियों को लॉन्च करने से रोकने का एक अच्छा तरीका है?


एंड्रॉयड बग ट्रैकर में जुड़े टिकट: issuetracker.google.com/issues/36941942 , issuetracker.google.com/issues/36907463 , issuetracker.google.com/issues/64108432
श्री-आईडीई

जवाबों:


187

इसे onCreate में जोड़ें और आपको जाने के लिए अच्छा होना चाहिए:

// Possible work around for market launches. See https://issuetracker.google.com/issues/36907463
// for more details. Essentially, the market launches the main activity on top of other activities.
// we never want this to happen. Instead, we check if we are the root and if not, we finish.
if (!isTaskRoot()) {
    final Intent intent = getIntent();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && Intent.ACTION_MAIN.equals(intent.getAction())) {
        Log.w(LOG_TAG, "Main Activity is not the root.  Finishing Main Activity instead of launching.");
        finish();
        return;       
    }
}

25
मैं वर्षों से इस बग को हल करने की कोशिश कर रहा हूं, और यह समाधान था जो काम करता है, इसलिए बहुत बहुत धन्यवाद! मुझे यह भी ध्यान देने की आवश्यकता है कि यह एंड्रॉइड मार्केट के भीतर एक समस्या नहीं है, बल्कि एक ऐप को सर्वर पर अपलोड करके, या इसे अपने फोन पर ईमेल करके, इस समस्या का कारण बनता है। ये सभी चीजें पैकेज इंस्टॉलर का उपयोग करके ऐप को स्थापित करती हैं, जहां मेरा मानना ​​है कि बग रहता है। इसके अलावा, अगर यह स्पष्ट नहीं है, तो आपको केवल इस कोड को ऑनक्रिएट विधि में जोड़ने की जरूरत है कि आपकी रूट गतिविधि क्या है।
ubzack

2
मुझे यह बहुत अजीब लगता है कि यह डिवाइस पर तैनात एक हस्ताक्षरित ऐप में होता है, लेकिन एक्लिप्स से तैनात डिबग संस्करण नहीं। डिबग करना काफी मुश्किल हो जाता है!
मैट कॉनॉली

6
यह करता है जब तक आप ग्रहण (या इंटेलीजे या अन्य आईडीई) के माध्यम से भी यह शुरुआत के रूप में के रूप में ग्रहण से तैनात एक डिबग संस्करण के साथ होता है। यह डिवाइस पर ऐप कैसे इंस्टॉल हो जाता है , इससे कोई लेना-देना नहीं है। समस्या ऐप शुरू करने के तरीके के कारण है ।
डेविड वासर

2
क्या किसी को पता है कि क्या यह कोड यह सुनिश्चित करेगा कि ऐप के मौजूदा उदाहरण को अग्रभूमि में लाया जाएगा? या यह सिर्फ खत्म () कहता है; और उपयोगकर्ता को बिना किसी दृश्य संकेत के छोड़ दें कि कुछ भी हुआ है?
कार्लोस पी

5
@CarlosP यदि जो गतिविधि बनाई जा रही है वह कार्य की जड़ गतिविधि नहीं है, तो इसके तहत कम से कम एक अन्य गतिविधि होनी चाहिए (परिभाषा के अनुसार)। यदि यह गतिविधि कॉल करती है, finish()तो उपयोगकर्ता उस गतिविधि को देखेगा जो नीचे थी। उसके कारण आप सुरक्षित रूप से मान सकते हैं कि ऐप का मौजूदा उदाहरण अग्रभूमि में लाया जाएगा। यदि ऐसा नहीं होता, तो आपके पास अलग-अलग कार्यों में ऐप के कई उदाहरण होंगे और जो गतिविधि बनाई जा रही है, वह उसके कार्य की जड़ होगी।
डेविड वासर

27

मैं बस यह बताने जा रहा हूं कि यह क्यों विफल हो जाता है, और इस बग को प्रोग्रामेटिक रूप से कैसे पुन: पेश करना है ताकि आप इसे अपने टेस्ट सूट में शामिल कर सकें:

  1. जब आप ग्रहण या बाज़ार ऐप के माध्यम से एक ऐप लॉन्च करते हैं, तो यह आशय के झंडे के साथ लॉन्च होता है: FLAG_ACTIVITY_NEW_TASK।

  2. लॉन्चर (होम) के माध्यम से लॉन्च करते समय, यह झंडे का उपयोग करता है: FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_BROUGHT_TO_FRONT | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED, और क्रिया " MAIN " और श्रेणी " LAUNCHER " का उपयोग करता है ।

यदि आप इसे एक परीक्षण मामले में पुन: पेश करना चाहते हैं, तो इन चरणों का उपयोग करें:

adb shell am start -f 0x10000000 -n com.testfairy.tests.regression.taskroot/.MainActivity 

फिर दूसरी गतिविधि के लिए जो भी आवश्यक हो, करें। अपने उद्देश्यों के लिए, मैंने बस एक बटन रखा जो एक और गतिविधि शुरू करता है। फिर, लॉन्चर (होम) के साथ वापस जाएं:

adb shell am start -W -c android.intent.category.HOME -a android.intent.action.MAIN

और इसके साथ लांचर के माध्यम से इसे लॉन्च करने का अनुकरण करें:

adb shell am start -a "android.intent.action.MAIN" -c "android.intent.category.LAUNCHER" -f 0x10600000 -n com.testfairy.tests.regression.taskroot/.MainActivity

यदि आपने isTaskRoot () वर्कअराउंड को शामिल नहीं किया है, तो यह समस्या को पुन: उत्पन्न करेगा। हम यह सुनिश्चित करने के लिए अपने स्वचालित परीक्षण में इसका उपयोग करते हैं कि यह बग फिर कभी न हो।

उम्मीद है की यह मदद करेगा!


8

क्या आपने सिंगलटॉप लॉन्च मोड की कोशिश की है?

यहाँ http://developer.android.com/guide/topics/manifest/activity-element.html से कुछ विवरण दिया गया है :

... एक नए इरादे को संभालने के लिए "सिंगलटॉप" गतिविधि का एक नया उदाहरण भी बनाया जा सकता है। हालाँकि, यदि लक्ष्य कार्य में पहले से ही इसके स्टैक के शीर्ष पर गतिविधि का एक मौजूदा उदाहरण है, तो वह उदाहरण नए इरादे (एक onNewIntent () कॉल) को प्राप्त करेगा; एक नया उदाहरण नहीं बनाया गया है। अन्य परिस्थितियों में - उदाहरण के लिए, यदि "सिंगलटॉप" गतिविधि का एक मौजूदा उदाहरण लक्ष्य कार्य में है, लेकिन स्टैक के शीर्ष पर नहीं है, या यदि यह स्टैक के शीर्ष पर है, लेकिन लक्ष्य कार्य में नहीं है - तो ए नया उदाहरण बनाया जाएगा और ढेर पर धकेल दिया जाएगा।


2
मैंने इसके बारे में सोचा, लेकिन क्या होगा अगर गतिविधि स्टैक के शीर्ष पर नहीं है? उदाहरण के लिए, ऐसा लगता है कि सिंगलटॉप एएए को रोक देगा, लेकिन एबीए को नहीं।
bsberkeley

क्या आप गतिविधि के भीतर सिंगलटॉप और फिनिश विधियों का उपयोग करके जो चाहें प्राप्त कर सकते हैं?
एरिक लेवाइन

मुझे नहीं पता कि अगर मैं चाहता हूं तो यह काफी पूरा हो जाएगा। उदाहरण: यदि मैं A और B को पॉप करने के बाद गतिविधि C पर हूँ, तो एक नई गतिविधि A लॉन्च हो जाती है और मेरे पास CA जैसा कुछ नहीं होगा?
bsberkeley

इसका जवाब यह समझने में मुश्किल है कि ये गतिविधियाँ क्या करती हैं। क्या आप अपने आवेदन और गतिविधियों के बारे में अधिक जानकारी प्रदान कर सकते हैं? मुझे आश्चर्य है कि अगर होम बटन क्या करता है और आपके कार्य के लिए यह कैसे चाहता है के बीच एक बेमेल है। होम बटन एक गतिविधि से बाहर नहीं निकलता है, यह "पृष्ठभूमि" है ताकि उपयोगकर्ता कुछ और पर स्विच कर सके। बैक बटन वह है जो बाहर निकलता है / खत्म होता है और गतिविधि करता है। ब्रेकिंग कि प्रतिमान उपयोगकर्ताओं को भ्रमित / निराश कर सकता है।
एरिक लेवाइन

मैंने इस थ्रेड में एक और उत्तर जोड़ा है ताकि आप मैनिफ़ेस्ट की एक प्रति देख सकें।
bsberkeley

4

शायद यह यह मुद्दा है ? या उसी बग के कुछ अन्य रूप?


कोड. google.com/p/android/issues/detail?id=26658 भी देखें , जो दर्शाता है कि यह ग्रहण के अलावा अन्य चीजों के कारण होता है।
क्रिस्टोफर जॉनसन

1
तो मुझे एक ऐसे विवरण का कॉपी-पेस्ट करना चाहिए जो बासी हो सकता है? कौन से हिस्से? यदि लिंक बदलता है तो क्या आवश्यक भागों को रखा जाना चाहिए, और क्या यह मेरी जिम्मेदारी है कि उत्तर को अद्यतन रखा जाए? यदि समस्या हल हो गई है, तो लिंक को केवल अमान्य होना चाहिए। यह एक ब्लॉग के लिए लिंक नहीं है, सब के बाद।
DuneCat

2

मुझे लगता है कि स्वीकृत उत्तर ( डुआने होमिक ) के मामले सामने नहीं आए हैं:

आपके पास अलग-अलग एक्स्ट्रा कलाकार हैं (और परिणामस्वरूप ऐप डुप्लिकेट):

  • जब आप बाज़ार से या होम स्क्रीन आइकन से आवेदन लॉन्च करते हैं (जो स्वचालित रूप से बाज़ार द्वारा रखा जाता है)
  • जब आप लॉन्चर द्वारा एप्लिकेशन लॉन्च करते हैं या मैन्युअल रूप से होम स्क्रीन आइकन बनाते हैं

यहाँ एक समाधान है (SDK_INT> = 11 सूचनाओं के लिए) जो मैं इन मामलों को संभालता हूँ और स्टेटसबार सूचनाएं भी।

प्रकट :

    <activity
        android:name="com.acme.activity.LauncherActivity"
        android:noHistory="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>
    <service android:name="com.acme.service.LauncherIntentService" />

लॉन्चर गतिविधि :

public static Integer lastLaunchTag = null;
@Override
public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mInflater = LayoutInflater.from(this);
    View mainView = null;
    mainView = mInflater.inflate(R.layout.act_launcher, null); // empty layout
    setContentView(mainView);

    if (getIntent() == null || getIntent().getExtras() == null || !getIntent().getExtras().containsKey(Consts.EXTRA_ACTIVITY_LAUNCH_FIX)) {
        Intent serviceIntent = new Intent(this, LauncherIntentService.class);
        if (getIntent() != null && getIntent().getExtras() != null) {
            serviceIntent.putExtras(getIntent().getExtras());
        }
        lastLaunchTag = (int) (Math.random()*100000);
        serviceIntent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_TAG, Integer.valueOf(lastLaunchTag));
        startService(serviceIntent);

        finish();
        return;
    }

    Intent intent = new Intent(this, SigninActivity.class);
    if (getIntent() != null && getIntent().getExtras() != null) {
        intent.putExtras(getIntent().getExtras());
    }
    startActivity(intent);
}

सेवा :

@Override
protected void onHandleIntent(final Intent intent) {
    Bundle extras = intent.getExtras();
    Integer lastLaunchTag = extras.getInt(Consts.EXTRA_ACTIVITY_LAUNCH_TAG);

    try {
        Long timeStart = new Date().getTime(); 
        while (new Date().getTime() - timeStart < 100) {
            Thread.currentThread().sleep(25);
            if (!lastLaunchTag.equals(LauncherActivity.lastLaunchTag)) {
                break;
            }
        }
        Thread.currentThread().sleep(25);
        launch(intent);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private void launch(Intent intent) {
    Intent launchIintent = new Intent(LauncherIntentService.this, LauncherActivity.class);
    launchIintent.addCategory(Intent.CATEGORY_LAUNCHER);
    launchIintent.setAction(Intent.ACTION_MAIN); 
    launchIintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    launchIintent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 
    if (intent != null && intent.getExtras() != null) {
        launchIintent.putExtras(intent.getExtras());
    }
    launchIintent.putExtra(Consts.EXTRA_ACTIVITY_LAUNCH_FIX, true);
    startActivity(launchIintent);
}

अधिसूचना :

ComponentName actCN = new ComponentName(context.getPackageName(), LauncherActivity.class.getName()); 
Intent contentIntent = new Intent(context, LauncherActivity.class);
contentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    
if (Build.VERSION.SDK_INT >= 11) { 
    contentIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // if you need to recreate activity stack
}
contentIntent.addCategory(Intent.CATEGORY_LAUNCHER);
contentIntent.setAction(Intent.ACTION_MAIN);
contentIntent.putExtra(Consts.EXTRA_CUSTOM_DATA, true);

2

मुझे एहसास है कि सवाल का ज़मरीन एंड्रॉइड से कोई लेना-देना नहीं है लेकिन मैं कुछ पोस्ट करना चाहता था क्योंकि मैंने इसे कहीं और नहीं देखा था।

Xamarin Android में इसे ठीक करने के लिए मैंने @DuaneHomick से कोड का उपयोग किया और इसमें जोड़ा गया MainActivity.OnCreate()। Xamarin के साथ अंतर यह है कि बाद में जाना चाहिए Xamarin.Forms.Forms.Init(this, bundle);और LoadApplication(new App());। तो मेरी OnCreate()तरह दिखेगा:

protected override void OnCreate(Bundle bundle) {
    base.OnCreate(bundle);

    Xamarin.Forms.Forms.Init(this, bundle);
    LoadApplication(new App());

    if(!IsTaskRoot) {
        Intent intent = Intent;
        string action = intent.Action;
        if(intent.HasCategory(Intent.CategoryLauncher) && action != null && action.Equals(Intent.ActionMain, System.StringComparison.OrdinalIgnoreCase)) {
            System.Console.WriteLine("\nIn APP.Droid.MainActivity.OnCreate() - Finishing Activity and returning since a second MainActivity has been created.\n");
            Finish();
            return; //Not necessary if there is no code below
        }
    }
}

* संपादित करें: Android 6.0 के बाद से, उपरोक्त समाधान कुछ स्थितियों के लिए पर्याप्त नहीं है। मैंने भी अब सेट LaunchModeकर लिया है SingleTask, जिससे लगता है कि चीजों को एक बार फिर से सही ढंग से काम करना होगा। यह निश्चित नहीं है कि दुर्भाग्य से अन्य चीजों पर इसका क्या प्रभाव पड़ सकता है।


0

मुझे एक ही समस्या थी, और मैंने इसे निम्नलिखित समाधान का उपयोग करके ठीक किया।

अपनी मुख्य गतिविधि में इस कोड को onCreateविधि के शीर्ष पर जोड़ें :

ActivityManager manager = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE );
List<RunningTaskInfo> tasks =  manager.getRunningTasks(Integer.MAX_VALUE);

for (RunningTaskInfo taskInfo : tasks) {
    if(taskInfo.baseActivity.getClassName().equals(<your package name>.<your class name>) && (taskInfo.numActivities > 1)){
        finish();
    }
}

अपने घोषणापत्र में इस अनुमति को जोड़ना न भूलें।

< uses-permission android:name="android.permission.GET_TASKS" />

आशा है कि यह आपकी मदद करता है।


0

मुझे भी यह समस्या थी

  1. कॉल समाप्त न करें (); घर की गतिविधि में यह अंतहीन रूप से चलेगा - समाप्त होने पर एक्टिविटी मैन द्वारा होम गतिविधि को बुलाया जा रहा है।
  2. आमतौर पर जब कॉन्फ़िगरेशन बदल रहा है (यानी स्क्रीन को घुमाएगी, भाषा बदलें, टेलीफोनी सेवा बदल जाए यानी mcc mcc इत्यादि) गतिविधि फिर से बनाए - और अगर घर की गतिविधि चल रही है, तो यह ए के लिए फिर से कॉल करता है ताकि प्रकट में जोड़ने की आवश्यकता हो android:configChanges="mcc|mnc"- यदि आपके पास सेल्युलर से संबंध हैं, http://developer.android.com/guide/topics/manifest/activity-element.html#config देखें, जिसके लिए सिस्टम को बूट करते समय या खुले या जो कुछ भी है वहां कॉन्फ़िगरेशन है।

0

इस समाधान का प्रयास करें: कक्षा
बनाएं Applicationऔर वहां परिभाषित करें:

public static boolean IS_APP_RUNNING = false;

फिर इसे जोड़ने onCreateसे पहले अपनी पहली (लॉन्चर) गतिविधि में setContentView(...):

if (Controller.IS_APP_RUNNING == false)
{
  Controller.IS_APP_RUNNING = true;
  setContentView(...)
  //Your onCreate code...
}
else
  finish();

पीएस Controllerमेरी Applicationक्लास है।


आपको आदिम बूलियन का उपयोग करना चाहिए, जो अशक्त अनावश्यक के लिए जाँच करता है।
वंडरसकाबो

यह हमेशा काम नहीं करेगा। आप कभी भी अपने ऐप को लॉन्च नहीं कर पाएंगे, अपने ऐप से बाहर निकल सकते हैं और फिर अपने ऐप को फिर से लॉन्च कर सकते हैं। जैसे ही कोई सक्रिय गतिविधियाँ नहीं होती हैं, Android आवश्यक रूप से होस्टिंग OS प्रक्रिया को नहीं मारता है। इस स्थिति में, जब आप ऐप को फिर से शुरू करते हैं, तो चर IS_APP_RUNNINGहोगा trueऔर आपका ऐप तुरंत छोड़ देगा। ऐसा नहीं है कि उपयोगकर्ता को मनोरंजक लगने की संभावना है।
डेविड वासर

-2

एकलता लॉन्च मोड का उपयोग करके ट्रिनिटी को अनुमति देने के लिए सेट करें यह हमेशा नए कार्य में गतिविधि बनाएगा, लेकिन इसके पुनर्संतलन की भी अनुमति देगा। चेक डिस: आत्मीयता विशेषता


2
शायद काम नहीं करेगा क्योंकि, प्रलेखन के अनुसार "री-पैरेंटिंग" मानक "और" सिंगलटॉप "मोड तक सीमित है।" क्योंकि "सिंगलटैस्क" या "सिंगलइनस्टांस" लॉन्च मोड के साथ गतिविधियां केवल एक कार्य के मूल में हो सकती हैं
bsberkeley

-2

मुझे उसी गतिविधियों को शुरू करने से रोकने का एक तरीका मिला, यह मेरे लिए बहुत अच्छा काम करता है

if ( !this.getClass().getSimpleName().equals("YourActivityClassName")) {
    start your activity
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.