আমি এমন একটি অ্যাপ্লিকেশন লেখার চেষ্টা করছি যা কিছু সময় পরে ফোরগ্রাউন্ডে ফিরে এলে নির্দিষ্ট কিছু করে। কোনও অ্যাপ্লিকেশন যখন ব্যাকগ্রাউন্ডে প্রেরণ করা হয় বা সম্মুখভাগে আনা হয় তখন এটি সনাক্ত করার কোনও উপায় আছে কি?
আমি এমন একটি অ্যাপ্লিকেশন লেখার চেষ্টা করছি যা কিছু সময় পরে ফোরগ্রাউন্ডে ফিরে এলে নির্দিষ্ট কিছু করে। কোনও অ্যাপ্লিকেশন যখন ব্যাকগ্রাউন্ডে প্রেরণ করা হয় বা সম্মুখভাগে আনা হয় তখন এটি সনাক্ত করার কোনও উপায় আছে কি?
উত্তর:
onPause()
এবং onResume()
পদ্ধতি যখন আবেদন আবার পটভূমি প্রয়োজন এবং ফোরগ্রাউন্ড মধ্যে আনা হয় বলা হয়। তবে, অ্যাপ্লিকেশনটি প্রথমবারের জন্য এবং এটি হত্যার আগে শুরু করা হলে তাদেরও ডাকা হয়। আপনি ক্রিয়াকলাপে আরও পড়তে পারেন ।
এখানে কোনো সরাসরি পদ্ধতির ব্যাকগ্রাউন্ড বা পুরোভূমিতে অ্যাপ্লিকেশানটি অবস্থা জানতে হয় না, কিন্তু এমনকি আমি এই বিষয় সম্মুখীন এবং সমাধান পাওয়া যায় onWindowFocusChanged
এবং onStop
।
আরও তথ্যের জন্য এখানে অ্যান্ড্রয়েড চেক করুন : কোনও অ্যান্ড্রয়েড অ্যাপ কখন ব্যাকগ্রাউন্ডে যায় তা সনাক্ত করার সমাধান এবং getRunningTasks বা getRunningAppProcesses ছাড়াই অগ্রভাগে ফিরে আসার সমাধান ।
মার্চ 2018 আপডেট : এখন আরও একটি ভাল সমাধান আছে। প্রসেসলিফিসাইকেল ওভনার দেখুন । আপনাকে নতুন আর্কিটেকচার উপাদানগুলি 1.1.0 ব্যবহার করতে হবে (এই মুহূর্তে সর্বশেষ) তবে এটি বিশেষভাবে এটি করার জন্য ডিজাইন করা হয়েছে।
এই উত্তরে একটি সাধারণ নমুনা সরবরাহ করা আছে তবে আমি এটি সম্পর্কে একটি নমুনা অ্যাপ এবং একটি ব্লগ পোস্ট লিখেছি ।
২০১৪ সালে আমি যখন এটি লিখেছিলাম তখন থেকেই বিভিন্ন সমাধান দেখা দিয়েছে। কিছু কাজ করেছে, কেউ কেউ কাজ করছে বলে ধারণা করা হয়েছিল , তবে ত্রুটিগুলি ছিল (আমার সহ!) এবং আমরা একটি সম্প্রদায় হিসাবে (অ্যান্ড্রয়েড) পরিণতিগুলি নিয়ে বাঁচতে শিখেছি এবং বিশেষ মামলার জন্য কাজগুলি লিখেছি।
কখনও কোডের একক স্নিপেটকে সলিউশন হিসাবে সন্ধান করবেন বলে মনে করবেন না, এটি ক্ষেত্রে সম্ভাবনা কম; আরও ভাল, এটি কী করে এবং কেন এটি করে তা বোঝার চেষ্টা করুন।
MemoryBoss
শ্রেণী কখনোই আসলে যেমন এখানে লেখা আমার দ্বারা ব্যবহৃত হয়, এটা ঠিক ছদ্ম কোড যে কাজ ঘটেছে এক টুকরা ছিল।
আপনি যদি নতুন আর্কিটেকচার উপাদানগুলি ব্যবহার না করার জন্য বৈধ কারণ না থাকে (এবং কিছু রয়েছে, বিশেষত যদি আপনি সুপার ওল্ড এপিআইকে লক্ষ্য করেন) তবে এগিয়ে যান এবং সেগুলি ব্যবহার করুন। তারা নিখুঁত থেকে অনেক দূরে, কিন্তু ছিল না ComponentCallbacks2
।
আপডেট / নোটস (নভেম্বর ২০১৫) : লোকেরা দুটি মন্তব্য করছে, প্রথমটি তার >=
পরিবর্তে ব্যবহার করা উচিত ==
কারণ ডকুমেন্টেশনে বলা হয়েছে যে আপনাকে সঠিক মানগুলি পরীক্ষা করা উচিত নয় । এই অধিকাংশ ক্ষেত্রে জন্য জরিমানা, কিন্তু ভালুক মনে যে আপনি যদি হয় শুধুমাত্র করছেন যত্নশীল কিছু যখন অ্যাপ্লিকেশন পটভূমি গিয়েছিলাম, আপনি ব্যবহার == করতে হবে এবং অন্য সমাধান (কার্যকলাপ লাইফ সাইকল callbacks মত) সঙ্গে এটি একত্রিত করা, অথবা আপনি আপনার পছন্দসই প্রভাব নাও পেতে পারে। উদাহরণটি (এবং আমার কাছে এটি ঘটেছে) হ'ল যদি আপনি লক করতে চানপাসওয়ার্ড স্ক্রিন সহ আপনার অ্যাপ্লিকেশনটি যখন ব্যাকগ্রাউন্ডে যায় (যেমন আপনি 1 প্যাসওয়ার্ডের সাথে পরিচিত হন তবে) আপনি যদি মেমরি কম রাখেন এবং হঠাৎ পরীক্ষা করে দেখেন তবে আপনি আপনার অ্যাপ্লিকেশনটি লক করতে পারেন >= TRIM_MEMORY
কারণ অ্যান্ড্রয়েড একটি LOW MEMORY
কল ট্রিগার করবে এবং এটিই তোমার চেয়ে উঁচু সুতরাং আপনি কীভাবে / কী পরীক্ষা করেন তা যত্নবান হন।
অতিরিক্তভাবে, কিছু লোক আপনি ফিরে এলে কীভাবে সনাক্ত করবেন সে সম্পর্কে জিজ্ঞাসা করেছেন।
আমি যে সহজ উপায়টি ভাবতে পারি তার নীচে ব্যাখ্যা করা হয়েছে তবে কিছু লোক যেহেতু এটির সাথে অপরিচিত তাই আমি এখানে কিছু ছদ্ম কোড যুক্ত করছি। আপনার YourApplication
এবং MemoryBoss
ক্লাসগুলি ধরে রেখে , আপনার class BaseActivity extends Activity
(আপনার যদি না থাকে তবে আপনাকে একটি তৈরি করতে হবে)।
@Override
protected void onStart() {
super.onStart();
if (mApplication.wasInBackground()) {
// HERE YOU CALL THE CODE YOU WANT TO HAPPEN ONLY ONCE WHEN YOUR APP WAS RESUMED FROM BACKGROUND
mApplication.setWasInBackground(false);
}
}
আমি স্টার্টটির পরামর্শ দিচ্ছি কারণ ডায়ালগগুলি কোনও ক্রিয়াকলাপ বিরতি দিতে পারে তাই আমি বাজি ধরছি আপনার অ্যাপটি "ব্যাকগ্রাউন্ডে চলে গেছে" এমনটি ভাবতে চান না যদি আপনি সমস্ত কিছু পূর্ণ পর্দার কথোপকথন প্রদর্শন করেন তবে আপনার মাইলেজ আলাদা হতে পারে।
এবং যে সব. যদি ব্লক কোড হবে শুধুমাত্র একবার কার্যকর করা , এমনকি যদি আপনি অন্য কার্যকলাপে যান, নতুন এক (যে extends BaseActivity
) রিপোর্ট হবে wasInBackground
হয় false
, তাই এটি কোড এক্সিকিউট করা হবে না যতক্ষণ না onMemoryTrimmed
বলা হয় এবং পতাকাঙ্কিত আবার সত্যতে সেট করা থাকে ।
আশা করি এইটি কাজ করবে.
আপডেট / নোটস (এপ্রিল 2015) : আপনি এই কোডটিতে সমস্ত অনুলিপি করুন এবং আটকানোর আগে নোট করুন যে আমি কয়েকটি দৃষ্টান্ত খুঁজে পেয়েছি যেখানে এটি 100% নির্ভরযোগ্য নাও হতে পারে এবং সেরা ফলাফল অর্জনের জন্য অবশ্যই অন্যান্য পদ্ধতির সাথে একত্রিত হতে হবে । উল্লেখযোগ্যভাবে, দুটি জ্ঞাত উদাহরণ রয়েছে যেখানে onTrimMemory
কল ব্যাকটি কার্যকর হওয়ার নিশ্চয়তা নেই:
যদি আপনার ফোনটি আপনার অ্যাপ্লিকেশনটি দৃশ্যমান অবস্থায় স্ক্রিনটিকে লক করে রাখে (বলুন আপনার ডিভাইসটি এনএন মিনিটের পরে লক হয়ে যায়), এই কলব্যাকটি কল করা হয় না (বা সর্বদা নয়) কারণ লকস্ক্রিনটি কেবল শীর্ষে রয়েছে, তবে আপনার অ্যাপ্লিকেশনটি এখনও আচ্ছাদিত "চলমান" রয়েছে is
যদি আপনার ডিভাইসটি মেমরির তুলনায় তুলনামূলকভাবে কম থাকে (এবং মেমরির চাপের মধ্যে থাকে) তবে অপারেটিং সিস্টেমটি এই কলটিকে উপেক্ষা করে সরাসরি আরও সমালোচনামূলক স্তরে চলে যাবে বলে মনে হচ্ছে।
এখন আপনার অ্যাপটি কখন ব্যাকগ্রাউন্ডে চলেছে তা আপনার পক্ষে জানা কতটুকু গুরুত্বপূর্ণ তা নির্ভর করে আপনার ক্রিয়াকলাপের জীবনচক্র এবং কী কী না ট্র্যাক করে এই সমাধানটি একসাথে বাড়িয়ে নিতে বা প্রয়োজন হতে পারে not
কেবল উপরের বিষয়টি মাথায় রাখুন এবং একটি ভাল QA দল রাখুন;)
আপডেট শেষ
এটি দেরিতে হতে পারে তবে আইসক্রিম স্যান্ডউইচ (এপিআই 14) এবং উপরে একটি নির্ভরযোগ্য পদ্ধতি রয়েছে ।
দেখা যাচ্ছে যে যখন আপনার অ্যাপ্লিকেশনটির আর দৃশ্যমান UI নেই, তখন একটি কলব্যাক ট্রিগার হয়। কলব্যাক, যা আপনি একটি কাস্টম ক্লাসে প্রয়োগ করতে পারেন, তাকে কম্পোনেন্টক্যালব্যাকস 2 (হ্যাঁ, একটি দুটি সহ) বলা হয়। এই কলব্যাকটি কেবলমাত্র API স্তরের 14 (আইসক্রিম স্যান্ডউইচ) এবং তারপরে উপলব্ধ।
আপনি মূলত পদ্ধতিতে একটি কল পান:
public abstract void onTrimMemory (int level)
স্তরটি 20 বা আরও নির্দিষ্টভাবে
public static final int TRIM_MEMORY_UI_HIDDEN
আমি এটি পরীক্ষা করে চলেছি এবং এটি সর্বদা কার্যকর হয়, কারণ স্তর 20 কেবলমাত্র একটি "পরামর্শ" যা আপনার অ্যাপ্লিকেশনটি আর দৃশ্যমান না হওয়ায় আপনি কিছু সংস্থান প্রকাশ করতে চাইতে পারেন।
সরকারী দস্তাবেজের উদ্ধৃতি দিতে:
OnTrimMemory (int) এর স্তর: প্রক্রিয়াটি একটি ইউজার ইন্টারফেস দেখায় এবং এখন আর তা করে না। মেমরির আরও ভাল পরিচালিত হওয়ার জন্য ইউআই সহ বড় বরাদ্দগুলি এই মুহুর্তে ছেড়ে দেওয়া উচিত।
অবশ্যই এটি বাস্তবায়িতভাবে যা বলা হয়েছে তা করার জন্য আপনার এটি প্রয়োগ করা উচিত (নির্দিষ্ট সময়ে ব্যবহৃত হয়নি এমন মেমরির শুদ্ধি, অবকাশহীন বসে থাকা কিছু সংগ্রহ সাফ করুন ইত্যাদি সম্ভাবনাগুলি অন্তহীন (আরও সম্ভাব্য আরও কিছু সরকারী দস্তাবেজ দেখুন সমালোচনামূলক স্তর)।
তবে, মজার বিষয় হ'ল ওএস আপনাকে বলে দিচ্ছে: ওহে, আপনার অ্যাপটি ব্যাকগ্রাউন্ডে চলে গেছে!
আপনি প্রথমে যা জানতে চেয়েছিলেন তা হ'ল।
আপনি কখন ফিরে আসবেন তা নির্ধারণ করবেন?
আচ্ছা এটি সহজ, আমি নিশ্চিত যে আপনার একটি "বেসঅ্যাক্টিভিটি" রয়েছে যাতে আপনি নিজের অনারিউম () ব্যবহার করতে পারেন যাতে আপনি ফিরে এসেছেন তা চিহ্নিত করতে। কারণ আপনি কেবল যখন ফিরে আসবেন না কেবল যখন আপনি উপরের onTrimMemory
পদ্ধতিটিতে কল পাবেন তখনই আপনি যাবেন ।
এটা কাজ করে। আপনি মিথ্যা ধনাত্মক পাবেন না। যদি কোনও ক্রিয়াকলাপ আবার শুরু হয় তবে আপনি 100% বার ফিরে এসেছেন। যদি ব্যবহারকারী আবার পিছনে যায়, আপনি অন্য onTrimMemory()
কল পান।
আপনাকে আপনার ক্রিয়াকলাপগুলি (বা আরও ভাল, একটি কাস্টম ক্লাস) গ্রাহক করতে হবে।
আপনি সর্বদা এটি পাওয়ার গ্যারান্টি দেওয়ার সবচেয়ে সহজ উপায় হ'ল এর মতো একটি সাধারণ ক্লাস তৈরি করা:
public class MemoryBoss implements ComponentCallbacks2 {
@Override
public void onConfigurationChanged(final Configuration newConfig) {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
// We're in the Background
}
// you might as well implement some memory cleanup here and be a nice Android dev.
}
}
এটি প্রয়োগ করার জন্য, আপনার অ্যাপ্লিকেশন বাস্তবায়নে ( আপনার একটি আছে, সঠিক? ), এর মতো কিছু করুন:
MemoryBoss mMemoryBoss;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
mMemoryBoss = new MemoryBoss();
registerComponentCallbacks(mMemoryBoss);
}
}
আপনি একটি তৈরি করেন Interface
আপনি একটি যোগ করতে পারিনি else
যে if
ও বাস্তবায়ন ComponentCallbacks
(ছাড়া 2) এপিআই 14. নিচের কিছু ব্যবহৃত কলব্যাক শুধুমাত্র করেছেন যে onLowMemory()
পদ্ধতি এবং বলা না হয়ে যায় যখন আপনি পটভূমিতে যাওয়ার , কিন্তু আপনি ট্রিম মেমরি ব্যবহার করা উচিত ।
এখন আপনার অ্যাপটি চালু করুন এবং হোম টিপুন। আপনার onTrimMemory(final int level)
পদ্ধতিটি কল করা উচিত (ইঙ্গিত: লগিং যোগ করুন)।
শেষ পদক্ষেপটি কলব্যাক থেকে নিবন্ধন করা। সম্ভবত সেরা জায়গাটি হ'ল onTerminate()
আপনার অ্যাপের পদ্ধতি, তবে , সেই পদ্ধতিটি কোনও আসল ডিভাইসে কল করা যায় না:
/** * This method is for use in emulated process environments. It will * never be called on a production Android device, where processes are * removed by simply killing them; no user code (including this callback) * is executed when doing so. */
সুতরাং আপনার যদি সত্যিই এমন পরিস্থিতি না থাকে যেখানে আপনি আর নিবন্ধভুক্ত হতে চান না, তবে আপনি এটির নিরাপত্তা এড়াতে পারবেন, যেহেতু আপনার প্রক্রিয়া যেভাবেই ওএস স্তরে মারা যাচ্ছে।
যদি আপনি কোনও পর্যায়ে নিবন্ধভুক্ত হওয়ার সিদ্ধান্ত নেন (যদি আপনি, উদাহরণস্বরূপ, আপনার অ্যাপ্লিকেশনটি পরিষ্কার এবং মরে যাওয়ার জন্য একটি শাটডাউন প্রক্রিয়া সরবরাহ করেন), আপনি এটি করতে পারেন:
unregisterComponentCallbacks(mMemoryBoss);
এবং এটাই.
level >= ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
যা আপনার আপডেট, পয়েন্ট ২ এ সমস্যাটি এড়ায় 1 পয়েন্ট 1 সম্পর্কিত, এটি আমার পক্ষে উদ্বেগের বিষয় নয়, যেহেতু অ্যাপটি সত্যই ব্যাকগ্রাউন্ডে যায় নি, সুতরাং এটি কাজ করার কথা।
আমি কীভাবে এটি সমাধান করতে পেরেছি তা এখানে। এটি এই ভিত্তিতে কাজ করে যে ক্রিয়াকলাপ স্থানান্তরগুলির মধ্যে সময়ের রেফারেন্স ব্যবহার করা সম্ভবত কোনও অ্যাপ্লিকেশন "ব্যাকগ্রাউন্ড" হয়েছে কিনা তা পর্যাপ্ত প্রমাণ সরবরাহ করবে।
প্রথমত, আমি একটি android.app. অ্যাপ্লিকেশন উদাহরণ ব্যবহার করেছি (আসুন একে মাই অ্যাপ্লিকেশন বলা যাক) যার একটি টাইমার, একটি টাইমার টাস্ক রয়েছে, যা একটি ক্রিয়াকলাপ থেকে অন্য ক্রিয়াকলাপে যুক্ত হতে পারে এমন সর্বাধিক সংখ্যক মিলিসেকেন্ডের প্রতিনিধিত্ব করার ধ্রুবক (আমি গিয়েছিলাম) 2s এর মান সহ) এবং অ্যাপ্লিকেশনটি "ব্যাকগ্রাউন্ডে" ছিল কিনা তা বোঝাতে একটি বুলিয়ান:
public class MyApplication extends Application {
private Timer mActivityTransitionTimer;
private TimerTask mActivityTransitionTimerTask;
public boolean wasInBackground;
private final long MAX_ACTIVITY_TRANSITION_TIME_MS = 2000;
...
অ্যাপ্লিকেশনটি টাইমার / টাস্ক শুরু এবং বন্ধ করার জন্য দুটি পদ্ধতিও সরবরাহ করে:
public void startActivityTransitionTimer() {
this.mActivityTransitionTimer = new Timer();
this.mActivityTransitionTimerTask = new TimerTask() {
public void run() {
MyApplication.this.wasInBackground = true;
}
};
this.mActivityTransitionTimer.schedule(mActivityTransitionTimerTask,
MAX_ACTIVITY_TRANSITION_TIME_MS);
}
public void stopActivityTransitionTimer() {
if (this.mActivityTransitionTimerTask != null) {
this.mActivityTransitionTimerTask.cancel();
}
if (this.mActivityTransitionTimer != null) {
this.mActivityTransitionTimer.cancel();
}
this.wasInBackground = false;
}
এই সমাধানের শেষ টুকরোটি হ'ল অনক্রমে () এবং অনপস () সমস্ত ক্রিয়াকলাপের ইভেন্টগুলি থেকে বা এই পদ্ধতিতে প্রতিটিটিতে একটি কল যুক্ত করা বা সম্ভবত, আপনার সমস্ত কংক্রিটের ক্রিয়াকলাপের উত্তরাধিকার সূত্রে একটি বেস ক্রিয়াকলাপ:
@Override
public void onResume()
{
super.onResume();
MyApplication myApp = (MyApplication)this.getApplication();
if (myApp.wasInBackground)
{
//Do specific came-here-from-background code
}
myApp.stopActivityTransitionTimer();
}
@Override
public void onPause()
{
super.onPause();
((MyApplication)this.getApplication()).startActivityTransitionTimer();
}
সুতরাং যখন ব্যবহারকারী কেবল আপনার অ্যাপের ক্রিয়াকলাপগুলির মধ্যে সরাসরি নেভিগেট করছেন, তখন প্রস্থানের ক্রিয়াকলাপের অনপজ () টাইমার শুরু করে, তবে তত্ক্ষণাত নতুন ক্রিয়াকলাপটি প্রবেশ করানো টাইমারটিকে সর্বাধিক সংক্রমণের সময় পৌঁছানোর আগেই বাতিল করে দেয়। এবং তাই wasInBackground হবে মিথ্যা ।
অন্যদিকে একটি ভ্রমণ লঞ্চার থেকে ফোরগ্রাউন্ড, ডিভাইসের জেগে ওঠা, শেষ ফোন কল, ইত্যাদি সম্ভাবনা বেশি চেয়ে টাইমার টাস্ক এই ইভেন্টে পূর্বে মৃত্যুদন্ড কার্যকর, এবং এইভাবে বিষয় আসে, তখন wasInBackground সেট ছিল সত্য ।
সম্পাদনা: নতুন স্থাপত্য উপাদান প্রতিশ্রুতি কিছু এসেছে: ProcessLifecycleOwner দেখুন @ vokilam এর উত্তর
class YourApplication : Application() {
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(AppLifecycleTracker())
}
}
class AppLifecycleTracker : Application.ActivityLifecycleCallbacks {
private var numStarted = 0
override fun onActivityStarted(activity: Activity?) {
if (numStarted == 0) {
// app went to foreground
}
numStarted++
}
override fun onActivityStopped(activity: Activity?) {
numStarted--
if (numStarted == 0) {
// app went to background
}
}
}
হ্যাঁ. আমি জানি যে আমাদের সহজ সমাধান এখানে কার্যকর কারণ এই সহজ সমাধান কাজ করে বিশ্বাস করা কঠিন।
তবে আশা আছে।
ProcessLifecycleOwner
এটি একটি প্রতিশ্রুতিবদ্ধ সমাধান বলে মনে হয়।
প্রসেসলিফাইসাইকেলওয়ানার প্রথম ক্রিয়াকলাপটি এই ইভেন্টগুলির মধ্য দিয়ে প্রেরণ
ON_START
,ON_RESUME
ইভেন্টগুলি প্রেরণ করবে ।ON_PAUSE
,,ON_STOP
ইভেন্টগুলি একটি শেষ কার্যকলাপ তাদের মধ্য দিয়ে যাওয়ার পরে বিলম্বের সাথে প্রেরণ করা হবে ।ProcessLifecycleOwner
কোনও কনফিগারেশন পরিবর্তনের কারণে ক্রিয়াকলাপ ধ্বংস এবং পুনরায় তৈরি করা হলে কোনও অনুষ্ঠান প্রেরণ করবে না এই গ্যারান্টিটি দেওয়ার জন্য এই বিলম্বটি যথেষ্ট দীর্ঘ ।
একটি বাস্তবায়ন হিসাবে সহজ হতে পারে
class AppLifecycleListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() { // app moved to foreground
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() { // app moved to background
}
}
// register observer
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifecycleListener())
উত্স কোড অনুসারে, বর্তমান বিলম্ব মান 700ms
।
এছাড়াও এই বৈশিষ্ট্যটি ব্যবহার করার জন্য dependencies
:
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
implementation "android.arch.lifecycle:extensions:1.0.0"
এবং annotationProcessor "android.arch.lifecycle:compiler:1.0.0"
গুগলের সংগ্রহস্থল থেকে (যেমন google()
)
মার্টন মার্কনকিনিস উত্তরের ভিত্তিতে (ধন্যবাদ!) আমি শেষ পর্যন্ত একটি নির্ভরযোগ্য (এবং খুব সহজ) সমাধান পেয়েছি found
public class ApplicationLifecycleHandler implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
private static final String TAG = ApplicationLifecycleHandler.class.getSimpleName();
private static boolean isInBackground = false;
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(isInBackground){
Log.d(TAG, "app went to foreground");
isInBackground = false;
}
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int i) {
if(i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN){
Log.d(TAG, "app went to background");
isInBackground = true;
}
}
}
তারপরে এটি আপনার অ্যাপ্লিকেশন ক্লাসের আপনারআনক্রিট () এ যুক্ত করুন
public class MyApp extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
ApplicationLifeCycleHandler handler = new ApplicationLifeCycleHandler();
registerActivityLifecycleCallbacks(handler);
registerComponentCallbacks(handler);
}
}
আমরা এই পদ্ধতিটি ব্যবহার করি। এটি কাজ করা খুব সহজ দেখায়, তবে এটি আমাদের অ্যাপ্লিকেশনটিতে ভালভাবে পরীক্ষা করা হয়েছিল এবং বাস্তবে "হোম" বোতামে, "রিটার্ন" বোতাম দ্বারা বা স্ক্রীন লক করার পরে হোম স্ক্রিনে যাওয়াসহ সমস্ত ক্ষেত্রে আশ্চর্যজনকভাবে ভাল কাজ করে। একবার চেষ্টা করে দেখো.
আইডিয়াটি হ'ল, যখন অগ্রভাগে হয়, অ্যান্ড্রয়েড সর্বদা নতুন ক্রিয়াকলাপ শুরু করার ঠিক আগে শুরু করে। এটি গ্যারান্টিযুক্ত নয়, তবে এটি কীভাবে কাজ করে। বিটিডাব্লু, ফ্লুরি একই যুক্তি ব্যবহার করে বলে মনে হচ্ছে (কেবলমাত্র অনুমান, আমি এটি চেক করি নি, তবে এটি একই ঘটনাগুলিতে দেখায়)।
public abstract class BaseActivity extends Activity {
private static int sessionDepth = 0;
@Override
protected void onStart() {
super.onStart();
sessionDepth++;
if(sessionDepth == 1){
//app came to foreground;
}
}
@Override
protected void onStop() {
super.onStop();
if (sessionDepth > 0)
sessionDepth--;
if (sessionDepth == 0) {
// app went to background
}
}
}
সম্পাদনা করুন: মতামত অনুসারে আমরা কোডের পরবর্তী সংস্করণগুলিতে অন স্টার্ট () এও চলে এসেছি। এছাড়াও, আমি সুপার কলগুলি যুক্ত করছি, যা আমার প্রাথমিক পোস্টটি থেকে অনুপস্থিত ছিল, কারণ এটি একটি ওয়ার্কিং কোডের চেয়ে বেশি ধারণা ছিল।
onStop is called when the activity is no longer visible to the user
,।
যদি আপনার অ্যাপ্লিকেশনটিতে একাধিক অ্যাক্টিভেটস এবং / অথবা ট্যাব বার উইজেটের মতো সজ্জিত অ্যাক্টিভেটস থাকে তবে অনপস () এবং onResume () কাজ করে না। অর্থাত্ একটি নতুন ক্রিয়াকলাপ শুরু করার সাথে সাথে নতুন ক্রিয়াকলাপ তৈরি হওয়ার আগে বর্তমান অ্যাক্টিভিটিগুলি বিরতি দেওয়া হবে। কোনও ক্রিয়াকলাপ শেষ করার পরে ("ফিরে" বোতামটি ব্যবহার করে) একই প্রয়োগ হয়।
আমি দুটি পদ্ধতি খুঁজে পেয়েছি যা মনে হয় হিসাবে কাজ করে।
প্রথমটির জন্য GET_TASKS অনুমতি প্রয়োজন এবং এটি একটি সাধারণ পদ্ধতি নিয়ে গঠিত যা প্যাকেজের নামগুলির সাথে তুলনা করে ডিভাইসে শীর্ষস্থানীয় ক্রিয়াকলাপটি অ্যাপ্লিকেশন সম্পর্কিত কিনা তা পরীক্ষা করে:
private boolean isApplicationBroughtToBackground() {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
return true;
}
}
return false;
}
এই পদ্ধতিটি ড্রড-ফু (বর্তমানে ইগনিশন নামে পরিচিত) কাঠামোর মধ্যে পাওয়া গেছে।
আমি যে দ্বিতীয় পদ্ধতিটি আমার নিজের প্রয়োগ করেছি তা GET_TASKS অনুমতি প্রয়োজন হয় না, যা ভাল। পরিবর্তে এটি বাস্তবায়নের জন্য আরও কিছুটা জটিল।
আপনার মেইন অ্যাপ্লিকেশন ক্লাসে আপনার একটি ভেরিয়েবল রয়েছে যা আপনার আবেদনে চলমান ক্রিয়াকলাপ ট্র্যাক করে। প্রতিটি ক্রিয়াকলাপের জন্য অনারিউম () -এ আপনি পরিবর্তনশীল বৃদ্ধি করেন এবং অনপেজ () এ আপনি এটি হ্রাস করেন।
চলমান ক্রিয়াকলাপের সংখ্যা 0 এ পৌঁছানোর পরে, নিম্নলিখিত শর্তগুলি সত্য হলে অ্যাপ্লিকেশনটিকে ব্যাকগ্রাউন্ডে রেখে দেওয়া হয়েছে:
আপনি যখন সনাক্ত করতে পারবেন যে অ্যাপ্লিকেশনটি পটভূমিতে পদত্যাগ করেছে, যখন এটি অগ্রভাগেও ফিরিয়ে আনা হবে তখন এটি সহজেই সনাক্ত করা যায়।
প্রসারিত একটি বর্গ তৈরি করুন Application
। তারপরে এটির ওভাররাইড পদ্ধতিটি আমরা ব্যবহার করতে পারি onTrimMemory()
।
অ্যাপ্লিকেশনটি ব্যাকগ্রাউন্ডে গেছে কিনা তা সনাক্ত করতে আমরা ব্যবহার করব:
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) { // Works for Activity
// Get called every-time when application went to background.
}
else if (level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE) { // Works for FragmentActivty
}
}
FragmentActivity
আপনার কাছে জুড়তে চাইতে পারেন level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
খুব।
OnUserLeaveHint ব্যবহার বিবেচনা করুন। আপনার অ্যাপটি ব্যাকগ্রাউন্ডে গেলেই এটি ডাকা হবে। অনপেজের পরিচালনা করার জন্য কোণার কেস থাকবে, যেহেতু এটি অন্যান্য কারণে ডাকা যেতে পারে; উদাহরণস্বরূপ যদি ব্যবহারকারী আপনার অ্যাপ্লিকেশান যেমন আপনার সেটিংস পৃষ্ঠা হিসাবে অন্য কোনও ক্রিয়াকলাপ খোলে, আপনার মূল ক্রিয়াকলাপের অনপেজ পদ্ধতিটি এখনও আপনার অ্যাপে থাকা সত্ত্বেও কল করা হবে; যা চলছে তা ট্র্যাক করার ফলে আপনি বাগের দিকে নিয়ে যাবে যখন আপনি পরিবর্তে অনারউজারলিভ হিন্ট কলব্যাকটি ব্যবহার করতে পারবেন যা আপনি যা জিজ্ঞাসা করছেন তা করে।
যখন ইউজারলিভহিন্টকে বলা হয়, আপনি বুলেয়ান ইনব্যাকগ্রাউন্ড পতাকাটি সত্য হিসাবে সেট করতে পারেন। অনারিউমেকে যখন ডাকা হয়, কেবল তখনই ধরে নিন যে ইনব্যাকগ্রাউন্ড পতাকাটি সেট করা থাকলে আপনি অগ্রভাগে ফিরে এসেছেন। এর কারণ এটি হল যে ব্যবহারকারী যদি আপনার সেটিংস মেনুতে থাকেন এবং কখনও অ্যাপ্লিকেশনটি না ফেলে থাকেন তবে অনুরুপটিকে আপনার মূল ক্রিয়াকলাপেও ডাকা হবে।
মনে রাখবেন যে ব্যবহারকারী যদি আপনার সেটিংসের স্ক্রিনে থাকাকালীন হোম বোতামটি হিট করে তবে আপনার সেটিংস ক্রিয়াকলাপে ইউজারলিভ হিন্ট কল করা হবে এবং তারা যখন ফিরে আসবে তখন আপনার সেটিংস কার্যকলাপে ডাকা হবে। আপনার প্রধান ক্রিয়াকলাপে যদি কেবলমাত্র এই সনাক্তকরণ কোডটি থাকে তবে আপনি এই ব্যবহারের ক্ষেত্রে মিস করবেন। সদৃশ কোড ছাড়াই আপনার সমস্ত ক্রিয়াকলাপে এই কোডটি রাখতে, একটি বিমূর্ত ক্রিয়াকলাপ শ্রেণি রয়েছে যা ক্রিয়াকলাপ প্রসারিত করে এবং এতে আপনার সাধারণ কোডটি রেখে দেয়। তারপরে আপনার প্রতিটি ক্রিয়াকলাপ এই বিমূর্ত ক্রিয়াকে প্রসারিত করতে পারে।
উদাহরণ স্বরূপ:
public abstract AbstractActivity extends Activity {
private static boolean inBackground = false;
@Override
public void onResume() {
if (inBackground) {
// You just came from the background
inBackground = false;
}
else {
// You just returned from another activity within your own app
}
}
@Override
public void onUserLeaveHint() {
inBackground = true;
}
}
public abstract MainActivity extends AbstractActivity {
...
}
public abstract SettingsActivity extends AbstractActivity {
...
}
ActivityLifecycleCallbacks আগ্রহী হতে পারে তবে এটি নথিবদ্ধ নয়।
যদিও, আপনি যদি রেজিস্টারএকটিভিটি লাইফেসাইকেলক্যালব্যাকস () কল করেন তবে ক্রিয়াকলাপগুলি তৈরি করা, ধ্বংস হওয়া ইত্যাদির জন্য আপনার কলব্যাক পেতে সক্ষম হওয়া উচিত get ক্রিয়াকলাপের জন্য আপনি getComp घटकName () কল করতে পারেন ।
Android.arch.lifecycle প্যাকেজ ক্লাস এবং ইন্টারফেস যে আপনি জীবনচক্র-সচেতন উপাদান গড়ে তুলতে দেয় উপলব্ধ
আপনার অ্যাপ্লিকেশনটির লাইফাইসাইক্লসবার্স ইন্টারফেসটি প্রয়োগ করা উচিত:
public class MyApplication extends Application implements LifecycleObserver {
@Override
public void onCreate() {
super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(this);
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private void onAppBackgrounded() {
Log.d("MyApp", "App in background");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
private void onAppForegrounded() {
Log.d("MyApp", "App in foreground");
}
}
এটি করতে, আপনার বিল্ড.gradle ফাইলটিতে এই নির্ভরতা যুক্ত করতে হবে:
dependencies {
implementation "android.arch.lifecycle:extensions:1.1.1"
}
গুগল দ্বারা প্রস্তাবিত হিসাবে, আপনার ক্রিয়াকলাপের আজীবন পদ্ধতিতে সম্পাদিত কোডটি ছোট করা উচিত:
একটি সাধারণ প্যাটার্ন হ'ল ক্রিয়াকলাপ এবং খণ্ডগুলির জীবনকালীন পদ্ধতিতে নির্ভরশীল উপাদানগুলির ক্রিয়াটি বাস্তবায়ন করা। যাইহোক, এই প্যাটার্নটি কোডের একটি দুর্বল সংস্থার এবং ত্রুটিগুলির প্রসারকে বাড়ে। লাইফাইসাইকেল-সচেতন উপাদানগুলি ব্যবহার করে আপনি নির্ভরশীল উপাদানগুলির কোডটি জীবনচক্র পদ্ধতি থেকে এবং উপাদানগুলিতে নিজেই স্থানান্তর করতে পারেন।
আপনি এখানে আরও পড়তে পারেন: https://developer.android.com/topic/libraries/architecture/lifecycle
আপনার অ্যাপ্লিকেশনটিতে কলব্যাক যুক্ত করুন এবং এর মতো রুট ক্রিয়াকলাপটি পরীক্ষা করুন:
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (activity.isTaskRoot() && !(activity instanceof YourSplashScreenActivity)) {
Log.e(YourApp.TAG, "Reload defaults on restoring from background.");
loadDefaults();
}
}
});
}
আমি গিথুব অ্যাপ-ফোরগ্রাউন্ড-ব্যাকগ্রাউন্ড- শোনোতে একটি প্রকল্প তৈরি করেছি
আপনার অ্যাপ্লিকেশনটিতে সমস্ত ক্রিয়াকলাপের জন্য একটি বেসঅ্যাক্টিভিটি তৈরি করুন।
public class BaseActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public static boolean isAppInFg = false;
public static boolean isScrInFg = false;
public static boolean isChangeScrFg = false;
@Override
protected void onStart() {
if (!isAppInFg) {
isAppInFg = true;
isChangeScrFg = false;
onAppStart();
}
else {
isChangeScrFg = true;
}
isScrInFg = true;
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
if (!isScrInFg || !isChangeScrFg) {
isAppInFg = false;
onAppPause();
}
isScrInFg = false;
}
public void onAppStart() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in foreground", Toast.LENGTH_LONG).show();
// Your code
}
public void onAppPause() {
// Remove this toast
Toast.makeText(getApplicationContext(), "App in background", Toast.LENGTH_LONG).show();
// Your code
}
}
এখন এই বেসএক্টিভিটিটিকে আপনার সমস্ত ক্রিয়াকলাপের সুপার ক্লাস হিসাবে ব্যবহার করুন যেমন মেইনএকটিভিটি বেসঅ্যাক্টিভিটি প্রসারিত করে এবং আপনি যখন অ্যাপ্লিকেশন শুরু করবেন তখন অনপস্টার্ট কল হবে এবং অ্যাপ্লিকেশনটি কোনও পর্দা থেকে ব্যাকগ্রাউন্ডে চলে গেলে অনপপস () কল হবে।
এটি সঙ্গে খুব সহজ প্রসেসলিফাইসাইক্লওয়ানারের
এই নির্ভরতা যুক্ত করুন
implementation "android.arch.lifecycle:extensions:$project.archLifecycleVersion"
kapt "android.arch.lifecycle:compiler:$project.archLifecycleVersion"
ইন Kotlin :
class ForegroundBackgroundListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startSomething() {
Log.v("ProcessLog", "APP IS ON FOREGROUND")
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopSomething() {
Log.v("ProcessLog", "APP IS IN BACKGROUND")
}
}
তারপরে আপনার বেস ক্রিয়াকলাপে:
override fun onCreate() {
super.onCreate()
ProcessLifecycleOwner.get()
.lifecycle
.addObserver(
ForegroundBackgroundListener()
.also { appObserver = it })
}
এই বিষয়টিতে আমার নিবন্ধটি দেখুন: https://medium.com/@egek92/how-to-actually-detect-fireground-background-changes-in-your-android-application-without-anting-9719cc822c48
আপনি এটিতে একটি জীবনচক্র পর্যবেক্ষক সংযুক্ত করে প্রসেসলিফিসাইকেলওয়ানার ব্যবহার করতে পারেন ।
public class ForegroundLifecycleObserver implements LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
public void onAppCreated() {
Timber.d("onAppCreated() called");
}
@OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppStarted() {
Timber.d("onAppStarted() called");
}
@OnLifecycleEvent(Event.ON_RESUME)
public void onAppResumed() {
Timber.d("onAppResumed() called");
}
@OnLifecycleEvent(Event.ON_PAUSE)
public void onAppPaused() {
Timber.d("onAppPaused() called");
}
@OnLifecycleEvent(Event.ON_STOP)
public void onAppStopped() {
Timber.d("onAppStopped() called");
}
}
তারপরে onCreate()
আপনার অ্যাপ্লিকেশন ক্লাসে আপনি এটি কল করুন:
ProcessLifecycleOwner.get().getLifecycle().addObserver(new ForegroundLifecycleObserver());
এই সঙ্গে তোমাদের ঘটনা ক্যাপচার করতে সক্ষম হবে ON_PAUSE
এবং ON_STOP
আপনার আবেদনের যে ঘটতে যখন এটি ব্যাকগ্রাউন্ডে চলে যায়।
পুরো অ্যাপ্লিকেশনটি পটভূমিতে / অগ্রভাগে কখন চলে যায় তা বলার জন্য কোনও সরল লাইফসাইকেল পদ্ধতি নেই।
আমি সহজ পদ্ধতিতে এটি করেছি। অ্যাপ্লিকেশন পটভূমি / অগ্রভূমি ফেজ সনাক্ত করতে নীচের নির্দেশাবলী অনুসরণ করুন।
একটু workaround সঙ্গে, এটি সম্ভব। এখানে, অ্যাক্টিভিটি লাইফিসাইকেলক্যালব্যাকস উদ্ধার করতে আসে। আমাকে ধাপে ধাপে চলতে দিন।
প্রথমে, এমন একটি শ্রেণি তৈরি করুন যা অ্যান্ড্রয়েড.এপ. প্রয়োগ করে এবং অ্যাক্টিভিটিফাইসাইকেলক্যালব্যাকস ইন্টারফেসটি প্রয়োগ করে । অ্যাপ্লিকেশন.অনক্রিয়েট () এ কলব্যাকটি নিবন্ধ করুন।
public class App extends Application implements
Application.ActivityLifecycleCallbacks {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
}
নীচে হিসাবে ম্যানিফেস্টে "অ্যাপ" ক্লাসটি নিবন্ধ করুন <application android:name=".App"
।
অ্যাপ্লিকেশনটি অগ্রভাগে থাকা অবস্থায় শুরু হওয়া অবস্থায় কমপক্ষে একটি ক্রিয়াকলাপ থাকবে এবং অ্যাপটি ব্যাকগ্রাউন্ডে থাকা অবস্থায় শুরু হওয়া অবস্থায় কোনও ক্রিয়াকলাপ থাকবে না।
নীচে “অ্যাপ” শ্রেণিতে 2 টি ভেরিয়েবল ঘোষণা করুন।
private int activityReferences = 0;
private boolean isActivityChangingConfigurations = false;
activityReferences
শুরু হওয়া অবস্থায় ক্রিয়াকলাপের সংখ্যা গণনা রাখে । isActivityChangingConfigurations
বর্তমান ক্রিয়াকলাপটি ওরিয়েন্টেশন সুইচের মতো কনফিগারেশন পরিবর্তনের মধ্য দিয়ে চলছে কিনা তা নির্দেশ করার জন্য এটি একটি পতাকা।
নিম্নলিখিত কোড ব্যবহার করে আপনি সনাক্ত করতে পারবেন অ্যাপটি অগ্রভাগে আসে কিনা।
@Override
public void onActivityStarted(Activity activity) {
if (++activityReferences == 1 && !isActivityChangingConfigurations) {
// App enters foreground
}
}
অ্যাপটি ব্যাকগ্রাউন্ডে চলে যায় কীভাবে এটি সনাক্ত করতে হয়।
@Override
public void onActivityStopped(Activity activity) {
isActivityChangingConfigurations = activity.isChangingConfigurations();
if (--activityReferences == 0 && !isActivityChangingConfigurations) {
// App enters background
}
}
কিভাবে এটা কাজ করে:
লাইফাইসাইক্যাল পদ্ধতিগুলি ক্রম অনুসারে যেভাবে বলা হয় এটি দিয়ে এটি একটি সামান্য কৌশল। আমাকে একটি দৃশ্যে হাঁটতে দাও।
ধরে নিন যে ব্যবহারকারী অ্যাপটি চালু করে এবং লঞ্চার অ্যাক্টিভিটি এ চালু করা হয়েছে। লাইফাইসাইকেল কলগুলি হবে,
A.onCreate ()
এ। স্টার্ট () (++ ক্রিয়াকলাপের উল্লেখসমূহ == 1) (অ্যাপ ফোরগ্রাউন্ডে প্রবেশ করে)
A.onResume ()
এখন অ্যাক্টিভিটি এ কার্যক্রম বি শুরু করে B.
A.onPause ()
B.onCreate ()
বি স্টার্ট () (++ ক্রিয়াকলাপের উল্লেখসমূহ == 2)
B.onResume ()
এ.অনস্টপ () (--অ্যাক্টিভিটি রিফারেন্সস == 1)
তারপরে ব্যবহারকারী ক্রিয়াকলাপ বি থেকে আবার নেভিগেট করে,
B.onPause ()
এ স্টার্ট () (++ ক্রিয়াকলাপের উল্লেখসমূহ == 2)
A.onResume ()
বি। স্টপ () (--অ্যাক্টিভিটি রিফারেন্সস == 1)
B.onDestroy ()
তারপরে ব্যবহারকারী হোম বোতাম টিপুন,
A.onPause ()
এঅনস্টপ () (--অ্যাক্টিভিটি রেফারেন্সগুলি == 0) (অ্যাপ্লিকেশন পটভূমিতে প্রবেশ করে)
যদি ব্যবহারকারী ব্যাক বোতামের পরিবর্তে অ্যাক্টিভিটি বি থেকে হোম বোতাম টিপেন, তবুও এটি একই হবে এবং ক্রিয়াকলাপের উল্লেখগুলি হবে 0
। সুতরাং, আমরা অ্যাপ্লিকেশনটিকে পটভূমিতে প্রবেশ করার সময় সনাক্ত করতে পারি।
তো, এর ভূমিকা কী isActivityChangingConfigurations
? উপরের দৃশ্যে, ধরুন ক্রিয়াকলাপ বিটি ওরিয়েন্টেশনকে পরিবর্তন করে। কলব্যাক ক্রম হবে,
B.onPause ()
বি.এস.এস.পি () (--অ্যাক্টিভিটি রেফারেন্সস == 0) (অ্যাপ্লিকেশন পটভূমিতে প্রবেশ করেছে ??)
B.onDestroy ()
B.onCreate ()
বি। স্টার্ট () (++ ক্রিয়াকলাপের উল্লেখসমূহ == 1) (অ্যাপ ফোরগ্রাউন্ডে প্রবেশ করেছে ??)
B.onResume ()
এই কারণে isActivityChangingConfigurations
যখন কার্যকলাপটি কনফিগারেশনের পরিবর্তনের মধ্য দিয়ে চলছে তখন দৃশ্যপট এড়াতে আমাদের একটি অতিরিক্ত চেক রয়েছে ।
অগ্রভাগ বা পটভূমি প্রবেশ করুক না কেন অ্যাপ্লিকেশন সনাক্ত করার জন্য আমি একটি ভাল পদ্ধতি পেয়েছি। এখানে আমার কোড । আশা করি এটি আপনাকে সহায়তা করবে।
/**
* Custom Application which can detect application state of whether it enter
* background or enter foreground.
*
* @reference http://www.vardhan-justlikethat.blogspot.sg/2014/02/android-solution-to-detect-when-android.html
*/
public abstract class StatusApplication extends Application implements ActivityLifecycleCallbacks {
public static final int STATE_UNKNOWN = 0x00;
public static final int STATE_CREATED = 0x01;
public static final int STATE_STARTED = 0x02;
public static final int STATE_RESUMED = 0x03;
public static final int STATE_PAUSED = 0x04;
public static final int STATE_STOPPED = 0x05;
public static final int STATE_DESTROYED = 0x06;
private static final int FLAG_STATE_FOREGROUND = -1;
private static final int FLAG_STATE_BACKGROUND = -2;
private int mCurrentState = STATE_UNKNOWN;
private int mStateFlag = FLAG_STATE_BACKGROUND;
@Override
public void onCreate() {
super.onCreate();
mCurrentState = STATE_UNKNOWN;
registerActivityLifecycleCallbacks(this);
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
// mCurrentState = STATE_CREATED;
}
@Override
public void onActivityStarted(Activity activity) {
if (mCurrentState == STATE_UNKNOWN || mCurrentState == STATE_STOPPED) {
if (mStateFlag == FLAG_STATE_BACKGROUND) {
applicationWillEnterForeground();
mStateFlag = FLAG_STATE_FOREGROUND;
}
}
mCurrentState = STATE_STARTED;
}
@Override
public void onActivityResumed(Activity activity) {
mCurrentState = STATE_RESUMED;
}
@Override
public void onActivityPaused(Activity activity) {
mCurrentState = STATE_PAUSED;
}
@Override
public void onActivityStopped(Activity activity) {
mCurrentState = STATE_STOPPED;
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
mCurrentState = STATE_DESTROYED;
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
if (mCurrentState == STATE_STOPPED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidEnterBackground();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}else if (mCurrentState == STATE_DESTROYED && level >= TRIM_MEMORY_UI_HIDDEN) {
if (mStateFlag == FLAG_STATE_FOREGROUND) {
applicationDidDestroyed();
mStateFlag = FLAG_STATE_BACKGROUND;
}
}
}
/**
* The method be called when the application been destroyed. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidDestroyed();
/**
* The method be called when the application enter background. But when the
* device screen off,this method will not invoked.
*/
protected abstract void applicationDidEnterBackground();
/**
* The method be called when the application enter foreground.
*/
protected abstract void applicationWillEnterForeground();
}
সম্পাদনা 2: আমি নীচে যা লিখেছি তা বাস্তবে কার্যকর হবে না। গুগল এমন একটি অ্যাপ্লিকেশনকে প্রত্যাখ্যান করেছে যা ক্রিয়াকলাপে ম্যানেজ.আরজিটরুনিংটাস্কস () এ কল অন্তর্ভুক্ত করে। ডকুমেন্টেশন থেকে , এটি স্পষ্ট যে এই API টি কেবলমাত্র ডিবাগিং এবং বিকাশের উদ্দেশ্যে purposes নীচে গিটহাব প্রকল্পটি নতুন স্কিম যা টাইমার ব্যবহার করে এবং প্রায় ভাল হিসাবে আপডেট করার সময় পাওয়ার সাথে সাথে আমি এই পোস্টটি আপডেট করব।
সম্পাদনা করুন 1: আমি একটি আপ লিখেছি ব্লগ পোস্ট এবং তৈরি একটি সহজ GitHub সংগ্রহস্থলের এই সত্যিই সহজ করতে।
গৃহীত এবং শীর্ষ রেট দেওয়া উত্তর দুটিই সত্যই সেরা পদ্ধতির নয়। শীর্ষ অ্যাপ্লিকেশনটির প্রধান ক্রিয়াকলাপ একই অ্যাপ্লিকেশনে সংজ্ঞায়িত এমন ক্রিয়াকলাপের জন্য যে অ্যাপ্লিকেশনটির প্রধান ক্রিয়াকলাপটি অর্জন করছে তা হ্যান্ডেল করে না অ্যাপলিকেশনব্রোটটোব্যাকগ্রাউন্ড () এর শীর্ষ রেটেড উত্তরটির বাস্তবায়ন, তবে এর আলাদা জাভা প্যাকেজ রয়েছে। আমি এটি করার একটি উপায় নিয়ে এসেছি যা সেই ক্ষেত্রে কাজ করবে।
এটিকে অনপেজ () এ কল করুন এবং এটি আপনাকে বলবে যে আপনার অ্যাপ্লিকেশনটি পটভূমিতে চলে যাচ্ছে কারণ অন্য কোনও অ্যাপ্লিকেশন শুরু হয়েছে, বা ব্যবহারকারী হোম বোতাম টিপছেন।
public static boolean isApplicationBroughtToBackground(final Activity activity) {
ActivityManager activityManager = (ActivityManager) activity.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = activityManager.getRunningTasks(1);
// Check the top Activity against the list of Activities contained in the Application's package.
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
try {
PackageInfo pi = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_ACTIVITIES);
for (ActivityInfo activityInfo : pi.activities) {
if(topActivity.getClassName().equals(activityInfo.name)) {
return false;
}
}
} catch( PackageManager.NameNotFoundException e) {
return false; // Never happens.
}
}
return true;
}
সঠিক উত্তর এখানে
নীচের মত মাই অ্যাপ নামের সাথে ক্লাস তৈরি করুন:
public class MyApp implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
private Context context;
public void setContext(Context context)
{
this.context = context;
}
private boolean isInBackground = false;
@Override
public void onTrimMemory(final int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
isInBackground = true;
Log.d("status = ","we are out");
}
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
if(isInBackground){
isInBackground = false;
Log.d("status = ","we are in");
}
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
}
তারপরে, আপনি যেদিকেই চান (অ্যাপ্লিকেশনে আরও ভাল প্রথম ক্রিয়াকলাপটি চালু করা হয়েছে), নীচের কোডটি যুক্ত করুন:
MyApp myApp = new MyApp();
registerComponentCallbacks(myApp);
getApplication().registerActivityLifecycleCallbacks(myApp);
সম্পন্ন! এখন যখন অ্যাপটি ব্যাকগ্রাউন্ডে রয়েছে status : we are out
তখন আমরা লগ পাই এবং যখন আমরা অ্যাপে যাই তখন আমরা লগ পাইstatus : we are out
আমার সমাধান @ d60402 এর উত্তর দ্বারা অনুপ্রাণিত হয়েছিল এবং সময়-উইন্ডোতেও নির্ভর করে, তবে এটি ব্যবহার করে না Timer
:
public abstract class BaseActivity extends ActionBarActivity {
protected boolean wasInBackground = false;
@Override
protected void onStart() {
super.onStart();
wasInBackground = getApp().isInBackground;
getApp().isInBackground = false;
getApp().lastForegroundTransition = System.currentTimeMillis();
}
@Override
protected void onStop() {
super.onStop();
if( 1500 < System.currentTimeMillis() - getApp().lastForegroundTransition )
getApp().isInBackground = true;
}
protected SingletonApplication getApp(){
return (SingletonApplication)getApplication();
}
}
যেখানে ক্লাসের SingletonApplication
এক্সটেনশন Application
:
public class SingletonApplication extends Application {
public boolean isInBackground = false;
public long lastForegroundTransition = 0;
}
আমি গুগল অ্যানালিটিকস ইজিট্র্যাকার দিয়ে এটি ব্যবহার করছিলাম এবং এটি কার্যকর হয়েছিল। আপনি একটি সাধারণ পূর্ণসংখ্যা ব্যবহার করে যা করতে চান তা করার জন্য এটি বাড়ানো যেতে পারে।
public class MainApplication extends Application {
int isAppBackgrounded = 0;
@Override
public void onCreate() {
super.onCreate();
appBackgroundedDetector();
}
private void appBackgroundedDetector() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStart(activity);
}
@Override
public void onActivityResumed(Activity activity) {
isAppBackgrounded++;
if (isAppBackgrounded > 0) {
// Do something here
}
}
@Override
public void onActivityPaused(Activity activity) {
isAppBackgrounded--;
}
@Override
public void onActivityStopped(Activity activity) {
EasyTracker.getInstance(MainApplication.this).activityStop(activity);
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
আমি এটি কিছুটা দেরিতে জানি তবে আমি মনে করি নীচের মত এটি করার সময় এই সমস্ত উত্তরগুলির কিছু সমস্যা আছে এবং এটি নিখুঁতভাবে কাজ করে।
এই জাতীয় কার্যকলাপ চক্র কলব্যাক তৈরি করুন:
class ActivityLifeCycle implements ActivityLifecycleCallbacks{
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
}
Activity lastActivity;
@Override
public void onActivityResumed(Activity activity) {
//if (null == lastActivity || (activity != null && activity == lastActivity)) //use this condition instead if you want to be informed also when app has been killed or started for the first time
if (activity != null && activity == lastActivity)
{
Toast.makeText(MyApp.this, "NOW!", Toast.LENGTH_LONG).show();
}
lastActivity = activity;
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}
এবং এটি নীচের মত আপনার অ্যাপ্লিকেশন ক্লাসে নিবন্ধন করুন:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(new ActivityLifeCycle());
}
এটি এন্ড্রয়েডের মধ্যে অন্যতম জটিল প্রশ্ন হিসাবে দেখা যাচ্ছে যেহেতু (এই লেখা হিসাবে) অ্যান্ড্রয়েডের আইওএস সমতুল্যতা applicationDidEnterBackground()
বা applicationWillEnterForeground()
কলব্যাক নেই। আমি একটি অ্যাপস্টেট লাইব্রেরি ব্যবহার করেছি যা @ জেনজ একসাথে রেখেছিল ।
[অ্যাপস্টেট হ'ল) একটি সহজ, বিক্রিয়াশীল অ্যান্ড্রয়েড লাইব্রেরি আরএক্সজাভা ভিত্তিক যা অ্যাপের রাজ্যের পরিবর্তনগুলি পর্যবেক্ষণ করে। অ্যাপটি পটভূমিতে যায় এবং অগ্রভাগে ফিরে আসে প্রতিবার এটি গ্রাহকদের জানিয়ে দেয়।
দেখা গেল এটি ঠিক আমার যা প্রয়োজন, বিশেষত কারণ আমার অ্যাপ্লিকেশনটিতে একাধিক ক্রিয়াকলাপ ছিল তাই কেবল চেক করা onStart()
বা onStop()
কোনও ক্রিয়াকলাপ এটিকে কাটছে না।
প্রথমে আমি এই নির্ভরতাগুলি গ্রেডে যুক্ত করেছি:
dependencies {
compile 'com.jenzz.appstate:appstate:3.0.1'
compile 'com.jenzz.appstate:adapter-rxjava2:3.0.1'
}
তাহলে আপনার কোডের একটি উপযুক্ত জায়গায় এই লাইনগুলি যুক্ত করা সহজ বিষয় ছিল:
//Note that this uses RxJava 2.x adapter. Check the referenced github site for other ways of using observable
Observable<AppState> appState = RxAppStateMonitor.monitor(myApplication);
//where myApplication is a subclass of android.app.Application
appState.subscribe(new Consumer<AppState>() {
@Override
public void accept(@io.reactivex.annotations.NonNull AppState appState) throws Exception {
switch (appState) {
case FOREGROUND:
Log.i("info","App entered foreground");
break;
case BACKGROUND:
Log.i("info","App entered background");
break;
}
}
});
আপনি কীভাবে পর্যবেক্ষণযোগ্যতে সাবস্ক্রাইব করেছেন তার উপর নির্ভর করে মেমরি ফাঁস এড়াতে আপনাকে এ থেকে সদস্যতা ছাড়তে হতে পারে। গিথুব পৃষ্ঠায় আরও তথ্য ।
এটি @ d60402 এর উত্তরের পরিবর্তিত সংস্করণ: https://stackoverflow.com/a/15573121/4747587
সেখানে উল্লিখিত সমস্ত কিছু করুন। তবে এটি না Base Activity
করে এবং প্রতিটি ক্রিয়াকলাপের জন্য পিতামাতা হিসাবে তৈরি করা এবং ওভাররাইডিং onResume()
এবংonPause
, নীচের করুন:
আপনার অ্যাপ্লিকেশন শ্রেণিতে, লাইনটি যুক্ত করুন:
রেজিস্টারঅ্যাক্টিভিটি লাইফিসাইকেলক্যালব্যাকস (অ্যাপ্লিকেশন.অ্যাক্টিভিটি লাইফসাইকেলক্যালব্যাকস কলব্যাক);
এই callback
সমস্ত কার্যকলাপ জীবনচক্র পদ্ধতি হয়েছে এবং আপনি এখন ওভাররাইড করতে পারে onActivityResumed()
এবংonActivityPaused()
।
এই গিস্টটি একবার দেখুন: https://gist.github.com/thsaravana/1fa576b6af9fc8fff20acfb2ac79fa1b
আপনি নীচের মত ActivityLifecycleCallbacks
এবং এর সাহায্যে সহজেই এটি অর্জন ComponentCallbacks2
করতে পারেন।
AppLifeCycleHandler
উপরে বর্ণিত ইন্টারফেস প্রয়োগকারী একটি শ্রেণি তৈরি করুন ।
package com.sample.app;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.res.Configuration;
import android.os.Bundle;
/**
* Created by Naveen on 17/04/18
*/
public class AppLifeCycleHandler
implements Application.ActivityLifecycleCallbacks, ComponentCallbacks2 {
AppLifeCycleCallback appLifeCycleCallback;
boolean appInForeground;
public AppLifeCycleHandler(AppLifeCycleCallback appLifeCycleCallback) {
this.appLifeCycleCallback = appLifeCycleCallback;
}
@Override
public void onActivityResumed(Activity activity) {
if (!appInForeground) {
appInForeground = true;
appLifeCycleCallback.onAppForeground();
}
}
@Override
public void onTrimMemory(int i) {
if (i == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
appInForeground = false;
appLifeCycleCallback.onAppBackground();
}
}
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
@Override
public void onConfigurationChanged(Configuration configuration) {
}
@Override
public void onLowMemory() {
}
interface AppLifeCycleCallback {
void onAppBackground();
void onAppForeground();
}
}
আপনার শ্রেণিতে যা অ্যাপ্লিকেশনটি অগ্রভূমি এবং পটভূমির মধ্যে স্যুইচ করলে কলব্যাকগুলি পাওয়ার জন্য Application
প্রয়োগকে প্রসারিত করে AppLifeCycleCallback
। নীচের মত কিছু।
public class BaseApplication extends Application implements AppLifeCycleHandler.AppLifeCycleCallback{
@Override
public void onCreate() {
super.onCreate();
AppLifeCycleHandler appLifeCycleHandler = new AppLifeCycleHandler(this);
registerActivityLifecycleCallbacks(appLifeCycleHandler);
registerComponentCallbacks(appLifeCycleHandler);
}
@Override
public void onAppBackground() {
Log.d("LifecycleEvent", "onAppBackground");
}
@Override
public void onAppForeground() {
Log.d("LifecycleEvent", "onAppForeground");
}
}
আশাকরি এটা সাহায্য করবে.
সম্পাদনা করুন বিকল্প হিসাবে আপনি এখন লাইফ চক্র সচেতন আর্কিটেকচার উপাদান ব্যবহার করতে পারেন।
যেহেতু আমি এমন কোনও পদ্ধতির সন্ধান পাইনি, যা টাইম স্ট্যাম্পগুলি পরীক্ষা না করেও ঘূর্ণন পরিচালনা করে, তাই আমি ভেবেছিলাম যে আমরা এখন কীভাবে এটি আমাদের অ্যাপে করি। এই উত্তরটির একমাত্র সংযোজন https://stackoverflow.com/a/42679191/5119746 হ'ল, আমরা সেই দিকটিও বিবেচনায় রাখি।
class MyApplication : Application(), Application.ActivityLifecycleCallbacks {
// Members
private var mAppIsInBackground = false
private var mCurrentOrientation: Int? = null
private var mOrientationWasChanged = false
private var mResumed = 0
private var mPaused = 0
তারপরে, কলব্যাকের জন্য আমাদের প্রথমে পুনরায় শুরু করুন:
// ActivityLifecycleCallbacks
override fun onActivityResumed(activity: Activity?) {
mResumed++
if (mAppIsInBackground) {
// !!! App came from background !!! Insert code
mAppIsInBackground = false
}
mOrientationWasChanged = false
}
এবংঅ্যাক্টিভিটিসটপড:
override fun onActivityStopped(activity: Activity?) {
if (mResumed == mPaused && !mOrientationWasChanged) {
// !!! App moved to background !!! Insert code
mAppIsInBackground = true
}
এবং তারপরে, এখানে সংযোজনটি আসে: ওরিয়েন্টেশন পরিবর্তনের জন্য পরীক্ষা করা:
override fun onConfigurationChanged(newConfig: Configuration) {
if (newConfig.orientation != mCurrentOrientation) {
mCurrentOrientation = newConfig.orientation
mOrientationWasChanged = true
}
super.onConfigurationChanged(newConfig)
}
এটাই. আশা করি এটি কাউকে সাহায্য করবে :)
আমরা এই সমাধানটি ব্যবহার করে প্রসারিত করতে পারি LiveData
:
class AppForegroundStateLiveData : LiveData<AppForegroundStateLiveData.State>() {
private var lifecycleListener: LifecycleObserver? = null
override fun onActive() {
super.onActive()
lifecycleListener = AppLifecycleListener().also {
ProcessLifecycleOwner.get().lifecycle.addObserver(it)
}
}
override fun onInactive() {
super.onInactive()
lifecycleListener?.let {
this.lifecycleListener = null
ProcessLifecycleOwner.get().lifecycle.removeObserver(it)
}
}
internal inner class AppLifecycleListener : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onMoveToForeground() {
value = State.FOREGROUND
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onMoveToBackground() {
value = State.BACKGROUND
}
}
enum class State {
FOREGROUND, BACKGROUND
}
}
এখন আমরা এই লাইভ ডেটাতে সাবস্ক্রাইব করতে এবং প্রয়োজনীয় ইভেন্টগুলি ধরতে পারি। উদাহরণ স্বরূপ:
appForegroundStateLiveData.observeForever { state ->
when(state) {
AppForegroundStateLiveData.State.FOREGROUND -> { /* app move to foreground */ }
AppForegroundStateLiveData.State.BACKGROUND -> { /* app move to background */ }
}
}
এই উত্তরগুলি সঠিক বলে মনে হচ্ছে না। এই ক্রিয়াকলাপগুলি যখন অন্য ক্রিয়াকলাপ শুরু হয় এবং শেষ হয় তখন তাকে ডাকা হয়। আপনি যা করতে পারেন তা হ'ল একটি বিশ্ব পতাকা রাখা (হ্যাঁ, গ্লোবালগুলি খারাপ) :) এবং আপনি যখনই কোনও নতুন ক্রিয়াকলাপ শুরু করবেন প্রতিবার এটি সত্যে সেট করুন। প্রতিটি ক্রিয়াকলাপের অনক্রিট এটিকে মিথ্যাতে সেট করুন। তারপরে, অনপজে আপনি এই পতাকাটি পরীক্ষা করে দেখুন। যদি এটি মিথ্যা হয়, আপনার অ্যাপ্লিকেশনটি পটভূমিতে চলেছে বা এটি মারা যাচ্ছে।