यह ट्यूटोरियल बताता है कि नोटिफिकेशन टैप पर एक नई एक्टिविटी इंस्टेंस बनाने के बजाय **मौजूदा एक्टिविटी इंस्टेंस को कैसे पुनर्स्थापित किया जाए**। यह डुप्लिकेट स्क्रीन को रोकता है और ऐप स्थिति को संरक्षित करता है।
मुख्य समाधान: लॉन्च मोड + इंटेंट फ्लैग
दो पूरक दृष्टिकोण एक साथ काम करते हैं:
- मैनिफेस्ट में:
android:launchMode="singleTop"(याsingleTask) - इंटेंट में:
FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP
चरण 1: प्रोजेक्ट सेटअप
एंड्रॉइड स्टूडियो में मानक Empty Activity प्रोजेक्ट।
चरण 2: AndroidManifest.xml - महत्वपूर्ण लॉन्च मोड
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.resumeintent">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ResumeIntent">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"> <!-- कुंजी: मौजूदा इंस्टेंस का पुन: उपयोग करता है -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
चरण 3: लेआउट (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
tools:context=".MainActivity">
<Button
android:id="@+id/btnCreateNotification"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="रिज्यूम-ओनली नोटिफिकेशन बनाएँ"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvInstanceInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="इंस्टेंस आईडी: "
android:textSize="16sp"
app:layout_constraintTop_toBottomOf="@id/btnCreateNotification"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
चरण 4: MainActivity.java
package com.example.resumeintent;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private static final String CHANNEL_ID = "resume_channel";
private static final String CHANNEL_NAME = "रिज्यूम नोटिफिकेशन";
// प्रत्येक इंस्टेंस को विशिष्ट रूप से पहचानने के लिए
private final String instanceId = "Instance-" + System.currentTimeMillis();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tvInstance = findViewById(R.id.tvInstanceInfo);
tvInstance.setText("इंस्टेंस आईडी: " + instanceId);
Button btn = findViewById(R.id.btnCreateNotification);
btn.setOnClickListener(v -> createResumeNotification());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// महत्वपूर्ण: नए इंटेंट डेटा के साथ एक्टिविटी अपडेट करें
setIntent(intent);
// UI रीफ्रेश करें ताकि यह वही इंस्टेंस दिखाए
TextView tvInstance = findViewById(R.id.tvInstanceInfo);
tvInstance.setText("पुनर्स्थापित इंस्टेंस: " + instanceId + " (नया इंटेंट प्राप्त)");
// नए इंटेंट से डेटा निकालें
if (intent.getBooleanExtra("from_notification", false)) {
// नोटिफिकेशन-विशिष्ट लॉजिक संभालें
}
}
private void createResumeNotification() {
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.putExtra("from_notification", true);
notificationIntent.putExtra("timestamp", System.currentTimeMillis());
// कुंजी फ्लैग: मौजूदा इंस्टेंस को शीर्ष पर लाता है, नया नहीं बनाता
notificationIntent.setFlags(
Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
// FLAG_UPDATE_CURRENT सुनिश्चित करता है कि नवीनतम अतिरिक्त डेटा उपयोग किया जाता है
PendingIntent pendingIntent = PendingIntent.getActivity(
this, 0, notificationIntent,
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("रिज्यूम नोटिफिकेशन")
.setContentText("टैप करें - नई एक्टिविटी नहीं बनाएगा")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
int id = (int) System.currentTimeMillis();
manager.notify(id, builder.build());
}
private void createNotificationChannel() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("मौजूदा एक्टिविटी इंस्टेंस को पुनर्स्थापित करता है");
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
}
लॉन्च मोड तुलना
| लॉन्च मोड | व्यवहार | उपयोग का मामला |
|---|---|---|
standard (डिफ़ॉल्ट) | प्रत्येक इंटेंट के लिए नया इंस्टेंस बनाता है | अधिकांश एक्टिविटी |
singleTop | यदि पहले से शीर्ष पर है, तो onNewIntent() के माध्यम से पुन: उपयोग करता है | नोटिफिकेशन लक्ष्य, खोज परिणाम |
singleTask | टास्क में सिंगल इंस्टेंस; उसके ऊपर की अन्य एक्टिविटी साफ़ करता है | मुख्य प्रवेश बिंदु, डीप लिंक |
singleInstance | अपने टास्क में अकेला; अन्य ऐप्स इसका पुन: उपयोग कर सकते हैं | बहुत दुर्लभ; विशेष मामले |
फ्लैग संयोजन व्याख्या
// अनुशंसित संयोजन: intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); // FLAG_ACTIVITY_CLEAR_TOP: यदि लक्ष्य एक्टिविटी स्टैक में मौजूद है, // उसके ऊपर की सभी एक्टिविटी नष्ट हो जाती हैं, और लक्ष्य शीर्ष पर आ जाता है // FLAG_ACTIVITY_SINGLE_TOP: यदि लक्ष्य पहले से शीर्ष पर है, // नया इंस्टेंस बनाने के बजाय onNewIntent() के माध्यम से नया इंटेंट डिलीवर करता है // साथ में: "मौजूदा इंस्टेंस ढूंढें, इसके ऊपर साफ़ करें, इसे शीर्ष पर लाएं, पुन: उपयोग करें"
PendingIntent फ्लैग (Android 12+ के लिए महत्वपूर्ण)
| फ्लैग | उद्देश्य |
|---|---|
FLAG_IMMUTABLE | Android 12+ (API 31) पर आवश्यक; PendingIntent को अपरिवर्तनीय बनाता है |
FLAG_UPDATE_CURRENT | यदि PendingIntent पहले से मौजूद है, तो इसके अतिरिक्त डेटा को नए इंटेंट से अपडेट करता है |
FLAG_CANCEL_CURRENT | मौजूदा PendingIntent को नए से पहले कैंसिल करता है |
सामान्य नुकसान
- launchMode भूलना: मैनिफेस्ट में
singleTopके बिना, फ्लैग का कोई प्रभाव नहीं होता - onNewIntent() लागू नहीं करना: नए डेटा के साथ UI अपडेट नहीं होगा
- FLAG_UPDATE_CURRENT के बजाय FLAG_CANCEL_CURRENT उपयोग करना: यदि एक ही अनुरोध कोड के साथ कई नोटिफिकेशन भेजे जाते हैं तो डेटा बासी हो सकता है
- FLAG_IMMUTABLE छोड़ना: Android 12+ पर क्रैश होता है
परीक्षण सत्यापन
- ऐप लॉन्च करें → इंस्टेंस आईडी नोट करें
- नोटिफिकेशन बनाएँ → होम दबाएँ
- नोटिफिकेशन पर टैप करें → ऐप खुलता है, **समान इंस्टेंस आईडी** दिखाता है
- दोहराएँ → इंस्टेंस आईडी कभी नहीं बदलती