A2UI درحال گذار از مؤلفههای وبمحور به Android بومی است. پردازنده جدید Jetpack Compose برای A2UI نسخه ۰.۹ به توسعهدهندگان امکان میدهد میانای کاربر پویا و تولیدشده توسط مدل زبانی بزرگ را بهصورت بومی پردازش کنند. این کتابخانه بهجای نماهای وب، از چارچوب کنشگر مبتنی بر Coroutine، چرخههای حیات کلاس مهرشده، و ادغام بومی Material 3 بهره میبرد.
این جلسه معماری جدید را بررسی میکند و توضیح میدهد که چگونه درختهای مؤلفه سرورمحور به گرههای Compose واکنشی تبدیل میشوند و چگونه انتقالهای چیدمان داخلی بهروزرسانیهای ساختاری را بهآرامی و بدون مسدود کردن واسط کاربر مدیریت میکنند.
آوانویسی
سلام به همه و به این گفتگو خوش آمدید. در این گفتگو میخواهیم درباره نحوه تولید میانای کاربر در برنامههای Android بااستفاده از کتابخانه کاملاً جدید A2UI صحبت کنیم.
نام من نیک بوچر است، من مدیر محصولی هستم که روی Jetpack Compose کار میکند.
سلام به همگی، من آنتون هستم، مهندس تیم Compose.
من درحال کار روی این موتور پرداز جدید Jetpack Compose A2UI هستم که بسیار هیجانزدهایم امروز درباره آن به شما بگوییم.
بله، اکنون با افزایش قابلیتهای هوش مصنوعی، بسیاری از برنامهها بهدنبال این هستند که چگونه هوش مصنوعی میتواند به شما کمک کند تا تجربههای جدیدی را برای کاربرانشان بهبود بخشید یا بسازید.
یکی از تجربههای هیجانانگیزی که برخی افراد به آن توجه میکنند، میانای کاربر زایا است، که در آن، در زمان اجرا، پسزمینههای عاملی میتوانند میاناهای کاربر کاملاً جدیدی برای کاربران شما ایجاد کنند، که به نظر ما بسیار جالب است.
اما متأسفانه درحالحاضر، بسیاری از پاسخهای باطنهای عاملی در دیوارهایی از نوشتار، مانند این، گیر افتادهاند.
بنابراین، در اینجا مثالی وجود دارد که در آن از یک عامل خواستهام تا ویژگی جدیدی برای رزرو سفر برای من ایجاد کند.
بنابراین، پیامواره این است.
و همانطور که میبینید، درواقع پاسخ بسیار مناسبی داده است، اما در این دیوار متنی گیر افتاده است که نمیتوانم بهراحتی با آن تعامل داشته باشم و اصلاً شبیه بقیه برنامهام نیست.
واسط کاربر زایا به شما امکان میدهد بهجای آن، تجربهای مانند این ایجاد کنید که در آن میتوانید واسط کاربر جدیدی را در پاسخ به ورودی پویا بهسرعت تولید کنید و امکانات بیشتری برای سفارشیسازی یا تجربههای جدیدتر فراهم کنید.
بنابراین، در اینجا نمونهای از یک واسط کاربر آورده شده است که بااستفاده از مؤلفههای استاندارد و بومی ایجاد شده است.
بنابراین، شما قطعاً میتوانید این نوع تجربه را امروز خودتان بسازید، اما باید قالب خودتان را برای تبادل با پسبک، نوع جریان رویداد خودتان، و توابع، و غیره تعریف کنید.
A2UI، که مخفف Agent-to-UI (عامل به واسط کاربر) است، یک پروتکل باز است که قالبی JSON برای تعریف همه این موارد است.
بنابراین، یکی از چیزهایی که واقعاً درباره A2UI دوست دارم این است که یک قالب اعلانی است.
عامل فقط یک نمایش JSON از رابط کاربری برای ارائه برمیگرداند.
هیچ کدی را که کارخواه شما نیاز به اجرای آن داشته باشد برنمیگرداند، که این امر میتواند شما را درمعرض آسیبپذیریهای امنیتی خاصی قرار دهد.
فقط بیانی است.
بنابراین، چه مفاهیم دیگری را باید درباره A2UI بدانیم؟
فقط دو مفهوم اصلی: کاتالوگها و سطوح.
کاتالوگ دارای مؤلفههایی مانند نوشتار، دکمه، ردیف، و ستون است که نماینده میتواند از آنها برای ساختن واسط کاربر استفاده کند.
کاتالوگ همچنین دارای توابعی است که نماینده میتواند از آنها برای قالببندی نوشتار، اعتبارسنجی ورودی کاربر، یا باز کردن نشانیهای وب استفاده کند.
کاتالوگ قراردادی است که مشخص میکند برنامهتان از چه مؤلفهها و عملکردهایی پشتیبانی میکند، و عامل فقط میتواند با آنها بسازد.
این یعنی، همانطور که نیک اشاره کرد، عامل هرگز کد دلخواه را در برنامه شما اجرا نمیکند.
این ابزار میانای کاربر را از واحدهای سازندهای که شما طراحی میکنید و به آنها اعتماد دارید ترکیب میکند.
یک مفهوم مهم دیگر «سطح» است.
سطح بخشی از واسط کاربر است که عامل آن را کنترل میکند، مثل این نمونه برنامهریز سفر در اسلاید.
عامل، میانای کاربر را برای سطحی در JSON بااستفاده از پیامهای پروتکل A2UI، مانند createSurface، updateComponents، و بااستفاده از مؤلفهها و توابع تعریفشده در کاتالوگ شما توصیف میکند.
بنابراین، وقتی صحبت از کاتالوگ میشود، کاتالوگ یک مفهوم بسیار اساسی در A2UI است، زیرا کنترل میکند که عامل از چه مؤلفههایی میتواند برای ساختن یک میانجی استفاده کند.
مشخصات A2UI چیزی بهنام کاتالوگ پایه را تعریف میکند که مجموعهای از مؤلفههای رایج است که میتوانید برای ساختن اکثر واسطهای کاربر استفاده کنید.
پردازنده Jetpack Compose A2UI پیادهسازی کاتالوگ پایه را بااستفاده از مؤلفههای Material 3 ارائه میدهد.
اکثر مؤلفههای کاتالوگ آماده استفاده هستند.
سه مؤلفه رسانه وجود دارد: تصویر، ویدیو، و پخشکننده صدا، که باید کتابخانه انتخابیتان را به آنها متصل کنید.
برای مثال، میتوانید Coil را برای بار کردن تصویر و Media3 را برای پخشکننده ویدیو و صدا انتخاب کنید.
اما قدرت واقعی A2UI و کتابخانه پردازنده جدید ما این است که میتوانید مؤلفهها و کاتالوگهای کاملاً سفارشی خودتان را ایجاد کنید و آنچه را که عامل میتواند برای ساختن واسط کاربر استفاده کند کاملاً کنترل کنید.
بعداً به کاتالوگها برمیگردیم.
بسیارخوب، بیایید ببینیم این چطور بهنظر میرسد.
بنابراین، اگر بتوانیم به دستگاه نمایشی برویم، لطفاً.
این برنامه نمایشی ما است که قرار است از آن استفاده کنیم و میخواهیم موتور پردازنده A2UI را بسازیم تا به شما نشان دهیم چقدر آسان است.
بنابراین، مجموعهای از پیاموارهها است، همانطور که در این تراشههای کوچک در وسط صفحه میبینید، که میتوانید به نماینده ارسال کنید.
بنابراین، اگر روی یکی از پیاموارهها کلیک کنیم، آن پیامواره به یک نماینده واقعی و زنده در پسزمینه ارسال میشود، پس برای این نسخه نمایشی دعا کنید، و علاوهبر پیامواره، برخی دستورالعملهای سیستم را نیز اضافه میکنیم.
بنابراین، ما به بخش پشتیبان درباره پروتکل A2UI، درباره کاتالوگ مؤلفهها، در این مورد، کاتالوگ پایهای که در اینجا استفاده میکنیم، میگوییم که میتواند از آن برای ساختن رابط کاربری استفاده کند، و چند نمونه از نحوه ساختن این را به آن میدهیم.
پیادهسازی جلسه گپ در برنامه نمایشی ما ازقبل میداند که چگونه پیامهای JSON A2UI خام را از پاسخهای نماینده استخراج کند.
بنابراین، در این مورد، نماینده با متنی پاسخ داد که بهدنبال آن پیامهای پروتکل A2UI آمد: createSurface، updateComponents.
اما درحالحاضر، برنامه ما فقط این JSON خام را نشان میدهد و هنوز آن را بهعنوان UI پرداز نمیزند.
بسیار خوب، قبلاز اینکه وارد شویم و کد را پیادهسازی کنیم، اگر بتوانیم فقط برای ۱ ثانیه به اسلایدها برگردیم، بیایید یک قدم به عقب برگردیم و درباره طراحی سیستم که قرار است برای پیادهسازی این استفاده کنیم به شما بگوییم.
اوه، شروع شد.
بله، پس طرح این است.
بنابراین، عامل هوش مصنوعی پاسخی را به برنامه ما جاریسازی میکند که توسط کلاس ChatSession دریافت میشود.
ChatSession پیامهای JSON خام A2UI را از پاسخهای نماینده درحین جاریسازی استخراج میکند و آنها را به پردازشگر پیام A2UI ارسال میکند.
پردازشگر پیام با تجزیهکنندهای پیکربندی شده است که آن پیامهای JSON خام را به نمایشی معنایی تبدیل میکند.
سپس پردازنده پیامها را دربرابر تعریف کاتالوگ اعتبارسنجی میکند و آنها را به مجموعهای از مدلهای سطحی نگاشت میکند.
مدلهای سطحی، اینها چیزهایی هستند که تعریف میکنند، واسط کاربر چگونه بهنظر میرسد و، بخشهایی از واسط کاربر که نماینده کنترل میکند.
سپس آن مدلهای سطح بااستفاده از ترکیببندی سطح A2UI که کتابخانه ما ارائه کرده است بهعنوان رابط کاربری Compose بومی پردازش میشوند.
ارتباط بین عامل و میانای کاربر دوطرفه است، بنابراین وقتی کاربر با میانای کاربر تعامل میکند، کنشها و خطاهای کاربر به عامل برمیگردد.
بنابراین، بهطور خلاصه، سه واحد سازنده اصلی وجود دارد: کاتالوگ، پردازشگر پیام، و سطح.
بیایید کد آن را بنویسیم.
بله، بیایید آن را بسازیم اگر بتوانیم ماشینها را عوض کنیم.
بسیار خوب، اولین قدم اضافه کردن وابستگی است.
در اینجا میبینید که ما قبلاً وابستگی Material 3 A2UI را اضافه کردهایم و پروژه را همگامسازی کردهایم تا مجبور نباشیم منتظر Gradle بمانیم.
این وابستگی سطح بالا است که پیادهسازی کاتالوگ پایه را بااستفاده از آن مؤلفههای Material 3 تعریف میکند، اما وابستگیهای ریزتری نیز در زیر آن وجود دارد.
بنابراین، اگر نمیخواهید از کاتالوگ پایه استفاده کنید، که بعداً دلیل آن را توضیح خواهیم داد، میتوانید از کاتالوگهای دقیقتر استفاده کنید.
اما این کار بقیه را بهصورت متعدی وارد میکند.
بسیارخوب.
بنابراین، گفتیم که سه چیز وجود دارد.
اولین قدم تعریف کاتالوگ بود.
بنابراین در اینجا در برنامه خود، ما قصد داریم یک تابع کارخانه ایجاد کنیم تا به ساختن یک نمونه از آن پیادهسازی Material 3 از کاتالوگ پایه کمک کند.
بنابراین از این تابع کتابخانه در اینجا استفاده میکنیم، کاتالوگ پایه A2UI مواد، که نسخهبندی هم شده است.
و همانطور که گفتیم، این همه مؤلفههای رایج موردنیازتان را ارائه میدهد، با این تفاوت که نمیخواهیم شما را مجبور به استفاده از کتابخانههای خاص بار کردن تصویر یا بازپرداخت رسانه کنیم، بنابراین باید خودتان آنها را ارائه دهید.
بنابراین در اینجا، برای مثال، آنتون قبلاً این را ایجاد کرده است، wrapper در اطراف Coil's AsyncImage composable، که ما از آن برای بارگذاری تصاویر استفاده خواهیم کرد، و همچنین یک منطق اضافی کوچک برای نسخه نمایشی ما برای جستجوی تصاویر از ویکیپدیا.
و بنابراین، خیلی راحت، میتوانیم این را به مؤلفههای کاتالوگ وصل کنیم.
و بهطور مشابه، پیادهسازیهای مشابهی برای پردازش، ویدیو، و صدا داریم که در آنها از کتابخانه Media3 برای پردازش آنها با ExoPlayer استفاده خواهیم کرد.
بنابراین خیلی ساده به این صورت، میتوانیم مؤلفههای خودمان را به آن وصل کنیم.
مورد بعدی که کاتالوگ نیاز دارد، مجموعهای از توابع استاندارد است.
بنابراین، اینها توابعی هستند که مشتری میتواند ارائه دهد.
دوباره، ما پیادهسازی این را همراه با کاتالوگ پایه ارسال میکنیم، اما باید آن را با یک زمینه ارائه دهیم، که شما از آن برای انجام اقدامات خاصی استفاده خواهید کرد، مانند باز کردن یک URI اگر کاربر روی یک مورد ضربه بزند.
و به این ترتیب، اولین مرحله ما تکمیل شد.
کاتالوگی ایجاد کردیم.
مرحله بعدی که باید انجام دهیم راهاندازی پردازشگر پیام بود.
بنابراین، این چیزی است که درواقع پاسخها و غیره را تجزیه میکند.
بنابراین، به مدل نمای برنامه خود میرویم، جایی که ابتدا، در واقع آن کاتالوگی را که قبلاً راهاندازی کرده بودیم، نمونهسازی میکنیم، زیرا به آن نیاز خواهیم داشت.
و سپس این تجزیهکننده پیام، شیء پردازشگر پیام را ایجاد میکنیم که برای اعتبارسنجی پاسخها دربرابر مؤلفههایی که میخواهیم استفاده کنیم، نیاز به دسترسی به کاتالوگ دارد.
جزء دیگری که باید ایجاد کنیم تجزیهکننده است که درواقع شیء مسئول تجزیه پیامهای JSON هنگام بازگشت آنها است.
بنابراین، اینجا جایی است که شیء ChatSession را ایجاد میکنیم.
بنابراین، همانطور که قبلاً اشاره کردم، ChatSession ازقبل پیامهای خام A2UI را از پاسخهای عامل استخراج میکند.
و وقتی هر پیام جدید A2UI آماده شد، این callback onA2uiMessage را فراخوانی میکند.
بنابراین، تنها کاری که باید انجام دهیم این است که هریک از این پیامها را هنگام رسیدن به پردازنده منتقل کنیم.
تابع processInput را فراخوانی میکنیم، یک تجزیهکننده و یک پیام به آن میدهیم.
بنابراین، تابع processInput هر پیام را تجزیه میکند و سپس کار را برای پردازنده در صف قرار میدهد تا آن را دربرابر کاتالوگ اعتبارسنجی کند و مدلهای سطح را بهروز کند.
بنابراین، پردازنده مجموعه فعلی مدلهای سطح فعال را بهعنوان activeSurfaces StateFlow نشان میدهد.
ChatSession منطقی برای افزودن مدلهای سطح به حالتهای میانای کاربر پاسخهای نماینده برای ارائه دارد.
بنابراین، ما فقط این activeSurfaces StateFlow را بهعنوان پارامتر به ChatSession ارسال میکنیم.
همچنین، همانطور که اشاره کردم، تابع processInput کار را برای پردازنده صفبندی میکند.
اما برای شروع پردازش، باید یک کوروتین را در یک رشته پسزمینه راهاندازی کنیم.
بنابراین، از یک دامنه مدل نما برای آن استفاده میکنیم و توزیعکننده پیشفرض را تنظیم میکنیم.
و در آن روال همزمان، فقط باید processor.collectMessages را فراخوانی کنیم.
بنابراین، تا زمانی که این روال همکار فعال است، پردازنده پیامها را از صف پیامها مدیریت خواهد کرد.
بنابراین، کار دیگری که باید انجام دهیم این است که وقتی کاربر سابقه گپ را پاک میکند، باید سطوح فعلی را نیز پاک کنیم.
بنابراین، clearSurfaces را فراخوانی میکنیم.
بسیارخوب.
بنابراین دو مرحله اول را انجام دادهایم.
کاتالوگ را ایجاد کردیم، پردازشگر را برای تجزیه پیامها ایجاد کردیم.
مرحله بعدی که باید انجام دهیم این است که آن پاسخها را در واسط کاربر خودمان رندر کنیم.
بنابراین ما آن سطوح را همانطور که توسط تجزیهکننده تجزیه میشوند جمعآوری میکنیم.
And so now in our UI, we'll pass those on in this agent reply object and we'll simply iterate over them.
و سپس از این ترکیبپذیر سطح A2UI استفاده میکنیم و سپس مدل را به هریک از مدلها منتقل میکنیم، همانطور که در آنها تکرار میکنیم تا محتوا را در جایی که میخواهیم ارائه دهیم.
یک نکته در اینجا این است که اگر نماینده بخواهد، مثلاً، مقدار زیادی محتوا را نمایش دهد، ممکن است انتخاب کند که از چیزی مثل فهرست استفاده کند.
بنابراین باید ارتفاع محدودشده را بهعنوان حداکثر ارتفاع در این مؤلفه تنظیم کنیم تا مطمئن شویم بهدرستی ارائه میشود.
و با این کار، چیزی شبیه به یک اجراکننده A2UI پایه را پیادهسازی کردهایم.
بنابراین اکنون اگر به برنامهمان برگردیم، آن را دوباره راهاندازی کنیم، و دوباره آن پیامواره را بزنیم، امیدواریم که نماینده از آن استفاده کند تا، بله، اینجاست، یک واسط کاربر واقعی را ارائه دهد.
بنابراین ما فقط آن را بهصورت زنده کدگذاری میکنیم.
این مثل دود و آینه نیست.
اما بله، از تصاویر واقعی استفاده میکند.
این برنامه از برگهها، کادرهای گفتگو، انتخابگرهای تاریخ، لغزندهها، و همه مؤلفههای استاندارد Material که میشناسید استفاده میکند تا بهجای نمایش پاسخ متنی، برنامه شما را بسازد.
چون این برنامه واقعی است و از مؤلفههای واقعی «طراحی مواد» استفاده میکند، همانطور که میدانید، کارهایی را انجام میدهد که مؤلفههای واقعی انجام میدهند.
عملکردی شبیه به عملکرد بومی دارد.
این عنصر کارهایی مثل پاسخ دادن به حالت تیره را در اینجا انجام میدهد.
اینها مؤلفههای Material هستند.
بنابراین اگر یک ترکیبپذیر زمینه Material بیرونی دارید که در آن رنگها، شکلها، تایپوگرافی و غیره را سفارشیسازی کردهاید، این مؤلفهها آنها را انتخاب میکنند تا شبیه بقیه برنامه شما بهنظر برسد.
همانطور که نیک اشاره کرد، چون اینها مؤلفههای Compose بومی هستند، معناشناسی بومی دارند.
این یعنی همه ویژگیهای دسترسپذیری داخلی Compose پشتیبانی میشود.
بنابراین چیزهایی مثل TalkBack، مقیاسبندی قلم، و پیمایش صفحهکلید بهطور پیشفرض کار میکنند.
اکنون، چون درحال فراخوانی عاملی برای تولید واسط کاربر جدید در زمان اجرا هستید، ممکن است کمی تأخیر وجود داشته باشد.
میدانید، شاید لازم باشد منتظر بمانید تا نماینده پاسخ دهد.
کتابخانه هر کاری بتواند انجام میدهد تا این تأخیر را به حداقل برساند.
بنابراین اگر از این نوع استفاده کنیم، چند وقفه تزریق میکنیم تا کمی آهستهتر به شما نشان دهیم که چه اتفاقی میافتد و چگونه آن را به حداقل میرسانیم.
بله، چون ممکن است مدتی طول بکشد تا نماینده پاسخ کامل را جاریسازی کند، از چیزی بهنام رندر کردن تدریجی استفاده میکنیم.
بنابراین بهمحض رسیدن مؤلفه ریشه، نمایش واسط کاربر را برای سطح شروع میکنیم.
همه مؤلفههای نگهدارنده، اگر فرزندان هنوز نرسیده باشند، جایبانهای بارگیری را برای فرزندانشان نشان میدهند.
وقتی مؤلفههای فرزند میرسند، بااستفاده از طرح حرکتی Material 3 پویانمایی میشوند.
بسیارخوب.
و اگر بتوانیم به پیامواره بعدی برویم تا ویژگی دیگری از کتابخانه را نشان دهیم.
در اینجا پیامواره دیگری داریم که در آن درحال رزرو میز برای شام هستیم.
و در اینجا نماینده انتخاب میکند که از این مؤلفه لغزنده استفاده کند، بله از آن استفاده کرده است.
بنابراین در اینجا میبینید که میتوانید با میانای کاربری که نماینده در لحظه ایجاد کرده است تعامل داشته باشید و آن را جابهجا کنید.
و توجه کنید که چگونه نه تنها عدد روی مؤلفه را تغییر میدهد، بلکه این رشته قالب نیز در پایینتر وجود دارد که آن را میشمارد.
Like tell me that's not doing a round trip back to the agent just to update that, right?
این یک اتصال دوطرفه است.
بنابراین مؤلفه لغزنده به مدل دادههای درون حافظه این سطح محدود میشود.
همه مؤلفههایی که به یک مسیر در مدل داده متصل هستند، وقتی مقدار تغییر میکند بهطور خودکار بهروز میشوند.
بنابراین لغزنده مقدار را برای نمایش میخواند و همچنین وقتی لغزنده را میکشم، آن را دوباره در مدل مینویسد تا آن را بهروز کند.
بنابراین همه اینها بهصورت محلی و بدون رفتوبرگشت به عامل انجام میشود.
اما میتوانیم از عامل هم بخواهیم دادهها را بهروز کند.
پس بیایید آن را به ۶ نفر برسانیم.
وقتی نماینده پاسخ میدهد، به لغزنده توجه کنید.
تازه به ۶ رسید.
بیایید ببینیم چه چیزی از عامل دریافت کردیم.
بهجای ارسال شرح کاملاً جدیدی از واسط کاربر، نماینده فقط یک پیام کوچک updateDataModel ارسال کرد که مسیر لغزنده را بهروز کرد.
ازآنجاییکه لغزنده به آن مسیر محدود شده است، در همانجا بهروزرسانی شد.
بنابراین مدل داده هر سطح براساس وضعیت عکس آنی Compose ساخته میشود و هر ویژگی مؤلفه که به مسیر محدود شده است یک وضعیت مشتقشده است.
بنابراین، هر زمان که کاربر یا عامل مقدار را در مدل داده بهروزرسانی کند، همه مؤلفههایی که به همان مقدار محدود شدهاند بهطور خودکار بازترکیب میشوند.
بسیارخوب.
بنابراین فرض کنیم میخواهیم اقدام کنیم و میخواهیم دکمه رزرو را بزنیم، چه اتفاقی میافتد، آنتون؟
درحالحاضر اتفاقی نمیافتد.
دکمه کنشی را ارسال میکند، اما هنوز آن را به نماینده ارسال نمیکنیم.
پس بیایید آن را اصلاح کنیم.
بسیار خوب، همانطور که برای پردازش ورودیها باید به پردازنده متصل میشدیم، باید به هر خروجی و هر اقدامی که کاربر در واسط کاربری ایجادشده توسط عامل انجام میدهد نیز متصل شویم.
بنابراین برای انجام این کار، از جریان outboundEvents که پردازنده ارائه میدهد استفاده میکنیم و این اطلاعات را جمعآوری میکنیم.
بنابراین این فرصت شما است که بتوانید هر منطقی را که میخواهید براساس، میدانید، زمانی که کاربر روی کنشی تکضرب میزند یا کاری با واسط کاربر انجام میدهد، متصل کنید.
بنابراین درست در اینجا ما هر کنشی را به نماینده برمیگردانیم تا تصمیم بگیرد چه کاری انجام دهد، اما شما همچنین میتوانید در این مرحله تصمیم بگیرید که آیا به نوعی باطن خود را فراخوانی کنید یا به صفحه دیگری در برنامه خود بروید، این به شما بستگی دارد.
بنابراین در اینجا آنتون آن را وصل کرده است، و بنابراین اکنون وقتی روی اقدام تأیید رزرو کلیک میکنیم، آن اقدامی را که انجام دادهاید به نماینده ارسال میکند.
و اینجاست که، میدانید، میتوانید در بخش پشتی تصمیم بگیرید که سپس برخی عملکردها را فراخوانی کنید.
پس بیایید ببینیم واقعاً چه چیزی برای عامل ارسال میکنیم.
این پیام کنش A2UI است.
این هدف دارای بافت است و بافت حاوی مجموعهای از ویژگیها است که نماینده درخواست کرده است هنگام انجام این کنش ارسال شوند.
بنابراین وقتی روی دکمه کلیک میکنم، این ویژگیهای خاص در آن لحظه براساس مقادیر فعلی در مدل داده حل میشوند و این همان چیزی است که ما به نماینده ارسال میکنیم.
بسیارخوب، و تا اینجا نماینده با ما بسیار همکاری کرده است، اما ما در اینجا درباره هوش مصنوعی صحبت میکنیم و هوش مصنوعی ممکن است اشتباه کند.
خب، بیایید ببینیم چه اتفاقی میافتد اگر نماینده یک، میدانید، یک اشتباه، میدانید، در واسط کاربری خود مرتکب شود که آن را به ما توصیف میکند.
بنابراین در این پیامواره آخر، درواقع از نماینده میخواهیم که اشتباهی انجام دهد تا بتوانیم این را به شما نشان دهیم.
ببینیم چه اتفاقی میافتد.
آره.
همانطور که میبینید، یکی از مؤلفهها با خطا علامتگذاری شده است.
و اکنون مشکل برطرف شده است.
بنابراین این حلقه خوداصلاحی عامل است.
قبلاز اینکه هر مؤلفه به میانای کاربر برسد، آن پیام را با طرحواره کاتالوگ اعتبارسنجی میکنیم.
اگر مؤلفه با طرحواره مطابقت نداشته باشد، بهعنوان خطا علامتگذاری میشود.
پیادهسازی کاتالوگ پایه رندرکننده نشان میدهد که خطا به آن مؤلفه خاص محدود شده است، درحالیکه بقیه میانای کاربر همچنان نمایش داده میشود.
در پشت صحنه، پردازنده پیام خطایی به نماینده ارسال میکند تا به نماینده اطلاع دهد چه مشکلی پیش آمده است.
بنابراین در این مورد، پیامی ارسال کردیم که میگوید نوع مؤلفه نوع ناشناخته که وجود ندارد در کاتالوگ پیدا نشد.
سپس عامل فرصت داشت تا پاسخ دهد و پاسخ پیگیریای ارسال کند که واسط کاربر موجود را بهروز کند و واسط کاربر در جای خود ثابت شد.
آره.
در پیادهسازی فعلی کاتالوگ پایه Material، خطاها را فوراً نشان میدهیم، اما در مؤلفه سفارشیتان، ممکن است تصمیم بگیرید که اگر خطایی وجود دارد، نشانگر بارگیری را برای مؤلفه همچنان نشان دهید تا زمانی که عامل آن را برطرف کند.
و به این ترتیب ممکن است خطا کاملاً از کاربر پنهان شود.
و درباره کاتالوگها و مؤلفههای سفارشی، تاکنون فقط از کاتالوگ پایه بهصورت آماده استفاده کردهایم، اما البته میتوانید آن را مطابق با نیازهای خود سفارشیسازی کنید.
بنابراین میتوانید ببینید که وقتی از تابع کارخانه برای ایجاد کاتالوگ استفاده میکنیم، آنها مقادیر پیشفرض برای همه مؤلفههای مختلف هستند.
بنابراین درصورت تمایل میتوانید بهراحتی مؤلفههای خودتان را متصل کنید.
بنابراین، برای مثال، اگر بخواهیم سیستم طراحی خودمان را در برنامهمان و دکمه مخصوص به آن را متصل کنیم، میتوانیم بهراحتی این کار را انجام دهیم.
بنابراین، وقتی نمونهای از کاتالوگ را ایجاد میکنیم، این پارامتر را لغو میکنیم و این تعریف مؤلفه را ایجاد میکنیم.
بنابراین اگر کلاس جدیدی برای پیادهسازی آن ایجاد کنیم، میبینید که یک میانای اینجا وجود دارد، کاتالوگ پایه A2UI از نوع دکمه.
بنابراین اگر این را پیادهسازی کنیم، میتوانیم مؤلفه خودمان را متصل کنیم.
آره.
بنابراین، چون درحال لغو کردن مؤلفهای در کاتالوگ پایه هستیم، باید این میانای دکمه کاتالوگ پایه A2UI را پیادهسازی کنیم.
بنابراین این میانای کاربری ازقبل اطلاعاتی درباره مؤلفه دارد، مثل نام، شرح، و طرحواره ویژگی.
بنابراین تنها کاری که برای پیادهسازی نسخه خودمان باید انجام دهیم این است که این تابع ترکیبپذیر را پیادهسازی کنیم.
این تابع چند پارامتر میگیرد.
بنابراین این پارامترها مقادیری هستند که پساز اعتبارسنجی از عامل دریافت میشوند.
بنابراین اینها چیزهایی مانند شناسه فرزند هستند که به مؤلفهای ارجاع میدهند که محتوای دکمه خواهد بود.
همچنین دارای متغیری است که نوع دکمه، تعریف کنش، و برخی پارامترهای دیگر را توصیف میکند.
در پیادهسازی تولید دکمه، باید از همه این پارامترها استفاده کنیم، اما برای اهداف نمایشی فقط به فرزند و کنش نیاز داریم.
پس بیایید این ترکیبپذیر را پیادهسازی کنیم.
بسیار خوب، در این تابع ترکیبپذیر، میخواهیم نوع دکمه پسندیدن سفارشی خودمان را در اینجا منتشر کنیم.
و همانطور که میبینید، میتوانیم از پارامترهایی که توسط A2UI ارائه و اعتبارسنجی شدهاند استفاده کنیم.
بنابراین برای مثال، در تابع onClick، میتوانیم آن کنش را ارسال کنیم.
میتوانیم از این نوع کتابخانه DispatchAction استفاده کنیم و کنشی را که A2UI تعریف کرده است به آن ارسال کنیم.
بنابراین ما از دکمه خودمان استفاده میکنیم، اما همچنان میخواهیم محتوای فرزند، برچسب، یا هر چیزی را که عامل تصمیم گرفته استفاده کند، ارائه دهیم.
بنابراین، روشی که ما این کار را انجام میدهیم این است که از این تابع observeA2uiComponentState استفاده میکنیم و childId را به آن منتقل میکنیم تا دسترسی به محتوای فرزند که باید رندر شود را بهدست آوریم.
و سپس کاری که انجام خواهیم داد این است که از این تابع ترکیبپذیر A2uiComponent استفاده خواهیم کرد و سپس آن وضعیت مؤلفه فرزند را برای ارائه آن ارسال خواهیم کرد.
And here you can see we're only rendering that the child content if it loaded successfully, but you can also have your own logic here for if there if it's loading or if there's an error state, you can decide what it is you want to show.
بنابراین اگر به یکی از پیاموارههای نمونه در برنامه نمایشیمان برگردیم و اکنون دکمه را بااستفاده از دکمه استاندارد Material جایگزین کنیم.
خیلی ظریف است، امیدوارم بتوانید ببینید.
میبینید که در اینجا هم از دکمهای بسیار سفارشی استفاده میکنیم.
بسیار خوب، پس این روشی است که میتوانید، میدانید، برخی از انواع کتابخانه را جایگزین کنید، اما اگر بخواهید کتابخانه را با انواع سفارشی خودتان گسترش دهید چه؟
میتوانیم این کار را بهراحتی با افزودن به خود کاتالوگ انجام دهیم.
بنابراین آنتون قصد دارد کلاس جدیدی در اینجا ایجاد کند تا مؤلفه ردهبندی ستارهای را پیادهسازی کند.
بنابراین، میخواهیم چیزی به برنامهمان اضافه کنیم تا بتوانیم در این سفر فرضی به رستورانها ردهبندی بدهیم.
بنابراین، چون ما مؤلفه موجودی را جایگزین نمیکنیم، از این واسط عمومیتر، یعنی واسط مؤلفه A2UI، استفاده میکنیم که همانطور که میبینید، چند پارامتر دارد که باید آنها را لغو کنیم.
بنابراین، دو مورد اول نام و شرح هستند.
بنابراین، این توضیح میدهد که مؤلفه ما چیست و چه کاری انجام میدهد.
اینها بسیار مهم هستند زیرا وقتی کاتالوگ را سریالسازی میکنیم و به پسرانه میفرستیم تا به آن بگوییم از چه چیزی میتواند برای ساختن واسط کاربر استفاده کند، نماینده از این اطلاعات برای تصمیمگیری درباره استفاده یا عدم استفاده از این مؤلفه هنگام پاسخ دادن استفاده خواهد کرد.
بنابراین، باید کاملاً توصیفی باشید و به هوش مصنوعی بگویید که دقیقاً میدانید این کار چه میکند.
ویژگی بعدی که باید لغو کنیم، این ویژگی properties است که اساساً میگوید که اساساً تعریف میکند که این مؤلفه با چه دادههایی سروکار دارد.
بنابراین، ما یک نوار رتبهبندی ستارهای ایجاد میکنیم تا بتوانید به رستوران امتیاز دهید، و دو ویژگی وجود دارد که به آنها نیاز داریم.
این مقدار خواهد بود، مانند تعداد ستارههایی که کاربر انتخاب کرده است، که یک ویژگی قابل تغییر خواهد بود که کاربر میتواند با آن تعامل داشته باشد و آن را تغییر دهد، و حداکثر تعداد ستارهها.
بنابراین، در اینجا میتوانیم ببینیم که آنتون از این ویژگی پویای نوع عدد برای مقدار استفاده میکند.
بنابراین، این نشان میدهد که چند ستاره انتخاب شده است.
که این هم شرح خاص خودش را دارد که به نماینده میگوید این مقدار برای چیست.
و سپس یک ویژگی نوع ایستا داریم، بنابراین ما فقط از این ویژگی A2UI از نوع عدد استفاده میکنیم تا حداکثر تعداد ستارههایی را که این مؤلفه میتواند نشان دهد تعریف کنیم.
و سپس وقتی آن دو ویژگی را تعریف کردیم، فقط باید آنها را برگردانیم.
بنابراین، با لغو این ویژگی properties، فهرستی از آنها را برمیگردانیم.
بنابراین، اینگونه به عامل میگویید که این مؤلفه با چه چیزی سروکار دارد.
آره.
همانطور که نیک اشاره کرد، این تعاریف ویژگی بهعنوان بخشی از طرحواره کاتالوگ ارسال میشوند تا عامل بداند چگونه از این مؤلفه استفاده کند.
اما این ویژگی، همان تعاریف ویژگی، برای استخراج مقادیر تایپشده در زمان اجرا نیز استفاده میشود.
بنابراین، یک منبع واحد حقیقت وجود دارد.
بنابراین، بیایید ببینیم برای پیادهسازی این مورد چه کاری باید انجام دهیم.
بنابراین، ابتدا برای دسترسی به مثلاً ویژگی max، چون ویژگی max یک ویژگی ایستا است، یعنی عامل مقدار آن را بهصورت درونخطی در شیء ویژگیها ارسال میکند.
بنابراین، برای دسترسی به آن، فقط باید آن را از شیء خصوصیات با ارجاع به آن بااستفاده از شیء خصوصیت proper- max بهعنوان کلید دریافت کنیم.
ازآنجاییکه این ویژگی بهعنوان عدد تعریف شده است، باید آن را به عدد صحیح تبدیل کنیم و مقدار پیشفرض را تنظیم کنیم.
مورد بعدی ویژگی مقدار است.
ازآنجاییکه ویژگی مقدار یک ویژگی پویا است، به این معنی است که به چیزی در مدل داده ارجاع میدهد.
بنابراین، برای دسترسی به آن، باید از API properties.bind استفاده کنیم و شیء دارایی را بهعنوان کلید بهعنوان پارامتر ارسال کنیم.
بنابراین، کاری که این انجام میدهد این است که مقدار فعلی ذخیرهشده در مدل داده را برای این ویژگی برمیگرداند و همچنین برای بهروزرسانیهای آینده مشترک میشود.
بنابراین، هر زمان که مدل داده بهروز شود، این مؤلفه بهطور خودکار بازترکیب خواهد شد.
بنابراین، بهطور مشابه، ما نیز به یک int تبدیل کردیم و برخی پیشفرضها را تنظیم کردیم.
کار دیگری که باید انجام دهیم راهاندازی اتصال دوطرفه است تا بتوانیم ردهبندی را تغییر دهیم.
برای این کار، از bindUpdater API استفاده میکنیم.
همان شیء خصوصیت را که خصوصیت را تعریف میکند ارسال میکنیم و درنتیجه، bindUpdater یک لامبدا برمیگرداند که به ما امکان میدهد مقدار این خصوصیت را در مدل داده تغییر دهیم.
اکنون آمادهایم تا مؤلفه ردهبندی ستارهای خود را منتشر کنیم.
بسیارخوب.
بنابراین، اکنون که مؤلفه سفارشی خودمان را تعریف کردهایم، باید آن را به کاتالوگی که استفاده میکنیم اضافه کنیم.
بنابراین، قبلاً ما فقط از کاتالوگ پایه همانطور که بود استفاده میکردیم.
بنابراین، کاری که اکنون باید انجام دهیم این است که یک کاتالوگ جدید ایجاد کنیم که کل کاتالوگ پایه را بگیرد و مؤلفه سفارشی ما را به آن اضافه کند.
بنابراین، کاتالوگ از سه چیز تشکیل شده است.
این یک شناسه، مجموعهای از مؤلفهها، و مجموعهای از توابع است.
برای شناسه، از یک URI برای تعریف، میدانید، یک کاتالوگ با شناسه منحصربهفرد و همچنین نسخهای که با آن کار میکنیم استفاده خواهیم کرد.
و سپس برای مؤلفهها، همه مؤلفهها را از کاتالوگ پایه میگیریم و سپس از عملگر افزودن برای اضافه کردن آن مؤلفه سفارشی که بهتازگی ایجاد کردیم استفاده میکنیم.
و سپس برای توابع، فقط از توابع کاتالوگ پایه استفاده خواهیم کرد.
و به این ترتیب، کاتالوگ پایه را گسترش دادهایم تا نوع مؤلفه سفارشی جدیدمان را اضافه کنیم.
میبینید، ایجاد کاتالوگ سفارشی بسیار آسان است.
بنابراین، نماینده از دستورالعملهای سیستمی که برای آن ارسال میکنیم، نحوه استفاده از کاتالوگ را یاد میگیرد.
بنابراین، چون اکنون کاتالوگ سفارشی ایجاد کردهایم، بیایید دستورالعملهای سیستم را نیز بهروز کنیم.
بنابراین، شیء کاتالوگ را به تابع خود که دستورالعملهای سیستم را میسازد، ارسال میکنیم.
بنابراین، این شما و این هم…
بنابراین، تا کنون، ما تعریف ثابتی از طرحواره را برای کاتالوگ پایه ارسال میکردیم زیرا از این کاتالوگ استاندارد استفاده میکردیم.
اکنون که کاتالوگ سفارشی داریم، میتوانیم بااستفاده از تابع toJsonSchemaString، طرحواره را بهطور پویا تولید کنیم.
بنابراین، این کار یک نمایش JSON کامل و جامع از این کاتالوگ ایجاد میکند، ازجمله مؤلفه رتبهبندی ستارهای که بهتازگی اضافه کردیم.
دستورالعملهای سیستم همچنین شامل چند نمونه پاسخ برای نماینده است.
بنابراین، شناسه کاتالوگ را نیز در اینجا تغییر میدهیم تا نمونهها به کاتالوگ صحیح ارجاع دهند.
بسیارخوب.
و با این کار، کاتالوگ پایه را گسترش دادهایم تا انواع سفارشی خودمان را اضافه کنیم.
بنابراین، اگر آن را دوباره مستقر کنیم و یکی از این پیاموارهها را امتحان کنیم، امیدواریم که یک واسط کاربر ردهبندی به ما بدهد.
و سپس نماینده پاسخ میدهد.
و در اینجا، میتوانید ببینید که این مؤلفه ستاره، مؤلفه کاملاً جدیدی است که عامل تا همین الان که آن را پیادهسازی کردیم هرگز ندیده بود.
این بخشی از کاتالوگ پایه نیست، ما تازه این را ایجاد کردیم.
وقتی روی ستاره کلیک میکنم، bindUpdater lambda فراخوانی میشود و مقدار در مدل داده بهروز میشود.
و اگر ردهبندیها را ارسال کنیم، میتوانیم نگاهی به کنش بیندازیم و بافت کنش شامل مقادیری خواهد بود که بهتازگی تنظیم کردهایم.
بسیار خوب، اگر بتوانیم به اسلایدها برگردیم، اکنون.
خب، این مطالب زیادی بود که خیلی سریع از آنها گذشتیم، اما امیدواریم نشان داده باشیم که چگونه میتوانید بهراحتی از پاسخ دادن عاملها بهصورت یک دیوار متنی به ایجاد واسطهای کاربری بومی واقعی توسط آنها برسید.
امیدواریم نشان داده باشیم که ادغام بسیار آسان است.
سه مؤلفه اصلی وجود دارد که باید انجام دهید: باید کاتالوگی از مؤلفهها ایجاد کنید، منطق تجزیه را پیادهسازی کنید، و سپس آن را در واسط کاربر خود ارائه دهید.
و امیدواریم نشان داده باشیم که این کتابخانه کارهای زیادی را بهطور پیشفرض برای شما انجام میدهد، چیزهایی مثل رندر کردن پیشرونده یا اعتبارسنجی طرحواره، حلقههای خوداصلاحی یا مدیریت خطا، کارهای زیادی انجام میدهد.
و امیدواریم امروز خیلی سریع نشان داده باشیم که سفارشیسازی آن برای شما بسیار آسان است.
نشان دادیم که اگر از Material استفاده میکنید، کاتالوگ پایه چگونه به موضوع Material شما پاسخ میدهد، اما گسترش یا جایگزینی مؤلفهها در کاتالوگ واقعاً ساده است.
اما فکر میکنم اگر واقعاً کاتالوگ خودتان را ایجاد کنید، بیشترین قدرت را خواهید داشت.
اگر میخواهید بیشترین کنترل را بر واسط کاربریای که عامل میتواند استفاده کند داشته باشید، میتوانید مؤلفههای کاملاً سفارشی ایجاد کنید.
اگر بخواهید به آن، مثلاً، آزادی کامل بدهید، میتوانید مؤلفههای بسیار ابتدایی، چیزهایی مثل برچسبهای نوشتاری و دکمهها، به آن بدهید و بگویید، «رابط کاربری را تو بساز.»
اگر کنترل بیشتری میخواهید، میتوانید مؤلفههای دقیقتری ایجاد کنید و بگویید: «اینها تنها چیزهایی هستند که میتوانید استفاده کنید.»
بنابراین، شما کنترل میکنید که نماینده چه کاری میتواند انجام دهد و چه چیزی را به کاربران شما نشان دهد.
بنابراین، این کتابخانه جدید پردازنده A2UI و کتابخانه Jetpack است که بهتازگی راهاندازی کردهایم.
implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha02")درحالحاضر در مرحله آلفا است، آلفا ۰۲ بهتازگی منتشر شده است.
بنابراین، برای تسویهحساب این پیوندها را دنبال کنید.
ما اسنادی در developer.android.com و همچنین چند نمونه داریم.
ما یک نمونه پایه داریم که کاتالوگ پایه را نشان میدهد، همچنین یک نمونه پیچیدهتر، و این برنامه نمونه JetPacker که بهتازگی ایجاد کردهایم، که قرار است دوباره در سخنرانی کلیدی ADS که امروز بعداً برگزار میشود ببینید، که نشان میدهد چگونه میتوانید این نوع مؤلفههای پیچیدهتر را برای کنترل آنچه کاربر میبیند ایجاد کنید.
همانطور که گفتم، ما درحالحاضر در مرحله آلفا هستیم، بنابراین زمان بسیار خوبی است که آن را بررسی کنید و بازخورد و درخواستهای ویژگی خود را به ما بدهید، و به ما بگویید که مثلاً چگونه میخواهید از این استفاده کنید، یا دوست دارم از این استفاده کنم اگر بتواند X یا Y را انجام دهد.
پس لطفاً کتابخانه را بررسی کنید و به ما بازخورد بدهید.
و این چیزی است که ما دریافت کردیم.
و من بیصبرانه منتظرم ببینم شما و عاملهایتان چه چیزی با آن میسازید.
بسیار سپاسگزاریم.
متشکریم.