مدیریت چرخه زندگی با اجزای مربوط به چرخه حیات بخشی از Android Jetpack .

مولفه های آگاه از چرخه حیات، اقداماتی را در پاسخ به تغییر وضعیت چرخه حیات یک جزء دیگر، مانند فعالیت ها و قطعات، انجام می دهند. این مولفه ها به شما کمک می کنند کدی با سازماندهی بهتر و اغلب سبک تر تولید کنید که نگهداری آن آسان تر است.

یک الگوی رایج این است که اقدامات اجزای وابسته را در روش‌های چرخه حیات فعالیت‌ها و قطعات پیاده‌سازی کنیم. با این حال، این الگو منجر به سازماندهی ضعیف کد و گسترش خطاها می شود. با استفاده از کامپوننت‌های مربوط به چرخه حیات، می‌توانید کد مولفه‌های وابسته را از روش‌های چرخه حیات خارج کرده و به خود مؤلفه‌ها منتقل کنید.

بسته androidx.lifecycle کلاس‌ها و رابط‌هایی را ارائه می‌کند که به شما امکان می‌دهد اجزای مربوط به چرخه زندگی را بسازید - که اجزایی هستند که می‌توانند به طور خودکار رفتار خود را بر اساس وضعیت چرخه حیات فعلی یک فعالیت یا قطعه تنظیم کنند.

اکثر اجزای برنامه که در چارچوب Android تعریف شده اند دارای چرخه حیات هستند. چرخه های عمر توسط سیستم عامل یا کد فریمورک در حال اجرا در فرآیند شما مدیریت می شوند. آنها هسته اصلی نحوه عملکرد Android هستند و برنامه شما باید به آنها احترام بگذارد. عدم انجام این کار ممکن است باعث نشت حافظه یا حتی خرابی برنامه شود.

تصور کنید فعالیتی داریم که مکان دستگاه را روی صفحه نمایش می دهد. یک پیاده سازی رایج ممکن است مانند موارد زیر باشد:

کاتلین

internal class MyLocationListener(
        private val context: Context,
        private val callback: (Location) -> Unit
) {

    fun start() {
        // connect to system location service
    }

    fun stop() {
        // disconnect from system location service
    }
}

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        myLocationListener.start()
        // manage other components that need to respond
        // to the activity lifecycle
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

جاوا

class MyLocationListener {
    public MyLocationListener(Context context, Callback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }
}

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    @Override
    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, (location) -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        myLocationListener.start();
        // manage other components that need to respond
        // to the activity lifecycle
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
        // manage other components that need to respond
        // to the activity lifecycle
    }
}

حتی اگر این نمونه خوب به نظر می رسد، در یک برنامه واقعی، در نهایت تماس های زیادی دارید که رابط کاربری و سایر اجزا را در پاسخ به وضعیت فعلی چرخه حیات مدیریت می کنند. مدیریت چندین مؤلفه، مقدار قابل توجهی از کد را در متدهای چرخه حیات، مانند onStart() و onStop() قرار می دهد که نگهداری آنها را دشوار می کند.

علاوه بر این، هیچ تضمینی وجود ندارد که مؤلفه قبل از توقف فعالیت یا قطعه شروع شود. این امر به ویژه در صورتی صادق است که ما نیاز به انجام یک عملیات طولانی مدت، مانند بررسی تنظیمات در onStart() . این می تواند شرایط مسابقه ای ایجاد کند که در آن متد onStop() قبل از onStart() به پایان می رسد و کامپوننت را بیشتر از زمان مورد نیاز زنده نگه می دارد.

کاتلین

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this) { location ->
            // update UI
        }
    }

    public override fun onStart() {
        super.onStart()
        Util.checkUserStatus { result ->
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start()
            }
        }
    }

    public override fun onStop() {
        super.onStop()
        myLocationListener.stop()
    }

}

جاوا

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, location -> {
            // update UI
        });
    }

    @Override
    public void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            // what if this callback is invoked AFTER activity is stopped?
            if (result) {
                myLocationListener.start();
            }
        });
    }

    @Override
    public void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

بسته androidx.lifecycle کلاس‌ها و رابط‌هایی را ارائه می‌کند که به شما کمک می‌کند این مشکلات را به روشی انعطاف‌پذیر و ایزوله حل کنید.

چرخه زندگی

Lifecycle کلاسی است که اطلاعات مربوط به وضعیت چرخه حیات یک جزء (مانند یک فعالیت یا یک قطعه) را در خود نگه می دارد و به اشیاء دیگر اجازه می دهد تا این حالت را مشاهده کنند.

Lifecycle از دو شمارش اصلی برای ردیابی وضعیت چرخه حیات برای جزء مرتبط خود استفاده می کند:

رویداد
رویدادهای چرخه حیات که از چارچوب و کلاس Lifecycle ارسال می شوند. این رویدادها به رویدادهای برگشتی در فعالیت ها و قطعات نگاشت می شوند.
ایالت
وضعیت فعلی جزء ردیابی شده توسط شی Lifecycle .
نمودار حالت های چرخه حیات
شکل 1. وضعیت ها و رویدادهایی که چرخه حیات فعالیت Android را تشکیل می دهند

حالت ها را به عنوان گره های یک گراف و رویدادها را به عنوان لبه های بین این گره ها در نظر بگیرید.

یک کلاس می تواند با اجرای DefaultLifecycleObserver و نادیده گرفتن متدهای مربوطه مانند onCreate ، onStart و غیره، وضعیت چرخه حیات کامپوننت را نظارت کند. سپس می توانید با فراخوانی متد addObserver() از کلاس Lifecycle و ارسال نمونه ای از مشاهدهگر خود، همانطور که نشان داده شده است، یک مشاهدهگر اضافه کنید. در مثال زیر:

کاتلین

class MyObserver : DefaultLifecycleObserver {
    override fun onResume(owner: LifecycleOwner) {
        connect()
    }

    override fun onPause(owner: LifecycleOwner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(MyObserver())

جاوا

public class MyObserver implements DefaultLifecycleObserver {
    @Override
    public void onResume(LifecycleOwner owner) {
        connect()
    }

    @Override
    public void onPause(LifecycleOwner owner) {
        disconnect()
    }
}

myLifecycleOwner.getLifecycle().addObserver(new MyObserver());

در مثال بالا، شی myLifecycleOwner رابط LifecycleOwner را پیاده سازی می کند که در قسمت زیر توضیح داده شده است.

Lifecycle Owner

LifecycleOwner یک رابط متد واحد است که نشان می دهد کلاس دارای Lifecycle است. این یک متد دارد، getLifecycle() که باید توسط کلاس پیاده سازی شود. اگر به جای آن می‌خواهید چرخه حیات یک فرآیند برنامه کاربردی را مدیریت کنید، به ProcessLifecycleOwner مراجعه کنید.

این رابط مالکیت یک Lifecycle را از کلاس‌های جداگانه، مانند Fragment و AppCompatActivity ، انتزاع می‌کند و اجازه می‌دهد اجزایی را بنویسد که با آنها کار می‌کنند. هر کلاس برنامه سفارشی می تواند رابط LifecycleOwner را پیاده سازی کند.

مؤلفه‌هایی که DefaultLifecycleObserver را پیاده‌سازی می‌کنند با مؤلفه‌هایی که LifecycleOwner را پیاده‌سازی می‌کنند یکپارچه کار می‌کنند، زیرا مالک می‌تواند چرخه حیاتی را ارائه دهد که ناظر می‌تواند آن را برای تماشا ثبت کند.

برای مثال ردیابی مکان، می‌توانیم کلاس MyLocationListener را وادار کنیم DefaultLifecycleObserver پیاده‌سازی کند و سپس آن را با Lifecycle فعالیت در متد onCreate() مقداردهی کنیم. این به کلاس MyLocationListener اجازه می دهد تا خودکفا باشد، به این معنی که منطق واکنش به تغییرات در وضعیت چرخه حیات به جای فعالیت در MyLocationListener اعلام می شود. داشتن اجزای جداگانه ذخیره منطق خود، مدیریت فعالیت ها و منطق قطعات را آسان تر می کند.

کاتلین

class MyActivity : AppCompatActivity() {
    private lateinit var myLocationListener: MyLocationListener

    override fun onCreate(...) {
        myLocationListener = MyLocationListener(this, lifecycle) { location ->
            // update UI
        }
        Util.checkUserStatus { result ->
            if (result) {
                myLocationListener.enable()
            }
        }
    }
}

جاوا

class MyActivity extends AppCompatActivity {
    private MyLocationListener myLocationListener;

    public void onCreate(...) {
        myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
            // update UI
        });
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
  }
}

یک مورد معمول استفاده این است که اگر Lifecycle در حال حاضر وضعیت خوبی ندارد، از فراخوانی تماس های خاص اجتناب کنید. به عنوان مثال، اگر callback یک تراکنش قطعه را پس از ذخیره وضعیت فعالیت اجرا کند، باعث خرابی می شود، بنابراین ما هرگز نمی خواهیم آن تماس را فراخوانی کنیم.

برای آسان کردن این مورد، کلاس Lifecycle به اشیاء دیگر اجازه می دهد تا وضعیت فعلی را جستجو کنند.

کاتلین

internal class MyLocationListener(
        private val context: Context,
        private val lifecycle: Lifecycle,
        private val callback: (Location) -> Unit
): DefaultLifecycleObserver {

    private var enabled = false

    override fun onStart(owner: LifecycleOwner) {
        if (enabled) {
            // connect
        }
    }

    fun enable() {
        enabled = true
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            // connect if not connected
        }
    }

    override fun onStop(owner: LifecycleOwner) {
        // disconnect if connected
    }
}

جاوا

class MyLocationListener implements DefaultLifecycleObserver {
    private boolean enabled = false;
    public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
       ...
    }

    @Override
    public void onStart(LifecycleOwner owner) {
        if (enabled) {
           // connect
        }
    }

    public void enable() {
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @Override
    public void onStop(LifecycleOwner owner) {
        // disconnect if connected
    }
}

با این پیاده سازی، کلاس LocationListener ما کاملاً از چرخه حیات آگاه است. اگر لازم است از LocationListener خود از یک اکتیویتی یا قطعه دیگر استفاده کنیم، فقط باید آن را مقداردهی اولیه کنیم. تمام عملیات راه اندازی و حذف توسط خود کلاس مدیریت می شود.

اگر کتابخانه‌ای کلاس‌هایی را ارائه می‌دهد که باید با چرخه حیات Android کار کنند، توصیه می‌کنیم از مؤلفه‌های مربوط به چرخه حیات استفاده کنید. مشتریان کتابخانه شما می توانند به راحتی آن اجزا را بدون مدیریت چرخه عمر دستی در سمت مشتری یکپارچه کنند.

پیاده سازی LifecycleOwner سفارشی

Fragments and Activities in Support Library نسخه 26.1.0 و نسخه های جدیدتر از قبل رابط LifecycleOwner را پیاده سازی کرده است.

اگر یک کلاس سفارشی دارید که می‌خواهید LifecycleOwner بسازید، می‌توانید از کلاس LifecycleRegistry استفاده کنید، اما باید رویدادها را به آن کلاس ارسال کنید، همانطور که در مثال کد زیر نشان داده شده است:

کاتلین

class MyActivity : Activity(), LifecycleOwner {

    private lateinit var lifecycleRegistry: LifecycleRegistry

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        lifecycleRegistry = LifecycleRegistry(this)
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }

    public override fun onStart() {
        super.onStart()
        lifecycleRegistry.markState(Lifecycle.State.STARTED)
    }

    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

جاوا

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        lifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

بهترین روش ها برای اجزای آگاه از چرخه حیات

  • کنترل‌کننده‌های رابط کاربری (فعالیت‌ها و قطعات) خود را تا حد امکان نازک نگه دارید. آنها نباید سعی کنند داده های خود را به دست آورند. در عوض، از یک ViewModel برای انجام این کار استفاده کنید، و یک شی LiveData را مشاهده کنید تا تغییرات را به نماها منعکس کند.
  • سعی کنید UIهای مبتنی بر داده بنویسید که در آن مسئولیت کنترلر UI شما این است که نماها را با تغییر داده ها به روز کند یا اقدامات کاربر را به ViewModel اطلاع دهد.
  • منطق داده های خود را در کلاس ViewModel خود قرار دهید. ViewModel باید به عنوان رابط بین کنترلر UI و بقیه برنامه شما عمل کند. اما مراقب باشید، وظیفه ViewModel نیست که داده ها را واکشی کند (مثلاً از یک شبکه). در عوض، ViewModel باید مؤلفه مناسب را برای واکشی داده ها فراخوانی کند، سپس نتیجه را به کنترلر UI ارائه دهد.
  • از Data Binding برای حفظ یک رابط تمیز بین نماهای خود و کنترلر UI استفاده کنید. این به شما امکان می دهد تا دیدگاه های خود را شفاف تر کنید و کد به روز رسانی مورد نیاز برای نوشتن در فعالیت ها و قطعات خود را به حداقل برسانید. اگر ترجیح می دهید این کار را در زبان برنامه نویسی جاوا انجام دهید، از کتابخانه ای مانند Butter Knife استفاده کنید تا از کدهای boilerplate اجتناب کنید و انتزاع بهتری داشته باشید.
  • اگر UI شما پیچیده است، ایجاد یک کلاس ارائه کننده برای مدیریت تغییرات UI را در نظر بگیرید. ممکن است این یک کار پر زحمت باشد، اما می‌تواند آزمایش اجزای رابط کاربری شما را آسان‌تر کند.
  • از ارجاع به یک View یا زمینه Activity در ViewModel خود اجتناب کنید. اگر ViewModel بیشتر از فعالیت خود ادامه دهد (در صورت تغییر پیکربندی)، فعالیت شما نشت می کند و به درستی توسط زباله جمع کن دفع نمی شود.
  • از کوروتین های Kotlin برای مدیریت کارهای طولانی مدت و سایر عملیاتی که می توانند به صورت ناهمزمان اجرا شوند استفاده کنید.

از کیس برای اجزای آگاه از چرخه حیات استفاده کنید

مولفه های آگاه از چرخه حیات می توانند مدیریت چرخه عمر را در موارد مختلف برای شما آسان تر کنند. چند نمونه عبارتند از:

  • جابه‌جایی بین به‌روزرسانی‌های مکان درشت و ریز. برای فعال کردن به‌روزرسانی‌های دقیق مکان در زمانی که برنامه موقعیت مکانی شما قابل مشاهده است، از مؤلفه‌های مربوط به چرخه حیات استفاده کنید و وقتی برنامه در پس‌زمینه است، به‌روزرسانی‌های درشت دانه را تغییر دهید. LiveData ، یک مؤلفه آگاه از چرخه حیات، به برنامه شما اجازه می دهد تا زمانی که کاربر مکان خود را تغییر می دهد، به طور خودکار رابط کاربری را به روز کند.
  • توقف و شروع بافر ویدیو. برای شروع بافر کردن ویدیو در اسرع وقت از اجزای مربوط به چرخه حیات استفاده کنید، اما پخش را تا شروع کامل برنامه به تعویق بیندازید. همچنین می‌توانید از مؤلفه‌های مربوط به چرخه حیات برای پایان دادن به بافر زمانی که برنامه‌تان از بین می‌رود، استفاده کنید.
  • شروع و توقف اتصال شبکه برای فعال کردن به‌روزرسانی زنده (جریان‌گذاری) داده‌های شبکه در حالی که برنامه در پیش‌زمینه است و همچنین برای توقف خودکار زمانی که برنامه به پس‌زمینه می‌رود، از مؤلفه‌های آگاه از چرخه حیات استفاده کنید.
  • مکث و ازسرگیری طرح‌های متحرک. از مؤلفه‌های آگاه از چرخه حیات برای رسیدگی به ترسیم‌های متحرک در حالت مکث زمانی که برنامه در پس‌زمینه است، استفاده کنید و پس از قرار گرفتن برنامه در پیش‌زمینه، طرح‌ها را از سر بگیرید.

رسیدگی به رویدادهای توقف

هنگامی که Lifecycle به AppCompatActivity یا Fragment تعلق دارد، وضعیت Lifecycle به CREATED تغییر می‌کند و با فراخوانی onSaveInstanceState() AppCompatActivity یا Fragment ، رویداد ON_STOP ارسال می‌شود.

وقتی وضعیت یک Fragment یا AppCompatActivity از طریق onSaveInstanceState() ذخیره می شود، تا زمانی که ON_START فراخوانی نشود، رابط کاربری آن تغییرناپذیر در نظر گرفته می شود. تلاش برای تغییر رابط کاربری پس از ذخیره حالت ممکن است باعث ناهماهنگی در وضعیت پیمایش برنامه شما شود، به همین دلیل است که اگر برنامه پس از ذخیره وضعیت، FragmentTransaction اجرا کند FragmentManager یک استثنا ایجاد می کند. برای جزئیات بیشتر به commit() مراجعه کنید.

LiveData با خودداری از فراخوانی ناظر خود در صورتی که Lifecycle مرتبط ناظر حداقل STARTED نشده باشد، از این حاشیه خارج از جعبه جلوگیری می کند. در پشت صحنه، isAtLeast() قبل از تصمیم به فراخوانی ناظر خود فراخوانی می کند.

متأسفانه، متد onStop() AppCompatActivity پس از onSaveInstanceState() فراخوانی می شود که در آن شکافی ایجاد می شود که در آن تغییرات حالت رابط کاربری مجاز نیست اما Lifecycle هنوز به حالت CREATED منتقل نشده است.

برای جلوگیری از این مشکل، کلاس Lifecycle در نسخه beta2 و پایین‌تر، بدون ارسال رویداد، وضعیت را به‌عنوان CREATED علامت‌گذاری می‌کند تا هر کدی که وضعیت فعلی را بررسی می‌کند، مقدار واقعی را دریافت کند، حتی اگر رویداد تا زمانی که onStop() فراخوانی نشود، مقدار واقعی را دریافت کند. سیستم

متاسفانه این راه حل دو مشکل عمده دارد:

  • در سطح API 23 و پایین تر، سیستم اندروید در واقع وضعیت یک فعالیت را حتی اگر تا حدی تحت پوشش فعالیت دیگری باشد ذخیره می کند. به عبارت دیگر، سیستم اندروید onSaveInstanceState() را فراخوانی می کند اما لزوماً onStop() فراخوانی نمی کند. این یک فاصله بالقوه طولانی ایجاد می کند که در آن ناظر همچنان فکر می کند که چرخه حیات فعال است، حتی اگر حالت رابط کاربری آن قابل تغییر نباشد.
  • هر کلاسی که بخواهد رفتاری مشابه با کلاس LiveData نشان دهد باید راه حل ارائه شده توسط Lifecycle نسخه beta 2 و پایین تر را پیاده سازی کند.

منابع اضافی

برای کسب اطلاعات بیشتر در مورد مدیریت چرخه عمر با اجزای مربوط به چرخه حیات، به منابع اضافی زیر مراجعه کنید.

نمونه ها

Codelabs

وبلاگ ها

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% آخر کلمه %}