نمایه‌های کاری

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

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

نمای کلی

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

برای رسیدگی به این وضعیت، Android 5.0 (سطح میانای برنامه‌سازی کاربردی ۲۱) به سازمان‌ها اجازه می‌دهد نمایه‌های کاری را راه‌اندازی کنند. اگر دستگاهی نمایه کاری داشته باشد، تنظیمات نمایه تحت کنترل سرپرست فناوری اطلاعات است. سرپرست فناوری اطلاعات می‌تواند انتخاب کند کدام برنامه‌ها برای آن نمایه مجاز باشند و می‌تواند کنترل کند که دقیقاً کدام ویژگی‌های دستگاه برای آن نمایه دردسترس باشند.

اگر دستگاهی نمایه کاری داشته باشد، این امر بر برنامه‌هایی که در دستگاه اجرا می‌شوند تأثیر می‌گذارد، صرف‌نظر از اینکه برنامه تحت کدام نمایه اجرا می‌شود:

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

جلوگیری از اهداف ناموفق

در دستگاهی با نمایه کاری، محدودیت‌هایی برای اینکه آیا هدف‌ها می‌توانند از یک نمایه به نمایه دیگر منتقل شوند وجود دارد. در بیشتر موارد، وقتی قصدی اجرا می‌شود، در همان نمایه‌ای که اجرا شده است مدیریت می‌شود. اگر هیچ گرداننده‌ای برای هدف در آن نمایه وجود نداشته باشد، هدف گردانده نمی‌شود و برنامه‌ای که آن را راه‌اندازی کرده است ممکن است به‌طور غیرمنتظره‌ای بسته شود—حتی اگر گرداننده‌ای برای هدف در نمایه دیگر وجود داشته باشد.

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

قبل‌از اینکه برنامه‌تان فعالیتی را شروع کند، باید تأیید کنید که وضوح مناسبی وجود دارد. می‌توانید با تماس با Intent.resolveActivity() مطمئن شوید که راه‌حل قابل‌قبولی وجود دارد. اگر راهی برای حل‌وفصل کردن قصدواره وجود نداشته باشد، روش null را برمی‌گرداند. اگر روش مقدار غیرتهی برگرداند، حداقل یک راه برای حل کردن هدف وجود دارد و راه‌اندازی هدف ایمن است. در این حالت، هدف می‌تواند قابل‌حل باشد، یا به‌دلیل وجود یک گرداننده در نمایه فعلی، یا به‌دلیل اینکه هدف مجاز است به گرداننده‌ای در نمایه دیگر منتقل شود. (برای اطلاعات بیشتر درباره حل کردن قصدها، قصدهای رایج را ببینید.)

برای مثال، اگر برنامه‌تان نیاز به تنظیم زمان‌سنج داشته باشد، باید بررسی کند که گرداننده معتبری برای هدف ACTION_SET_TIMER وجود داشته باشد. اگر برنامه نتواند قصد را حل‌وفصل کند، باید اقدام مناسبی انجام دهد (مثلاً نمایش پیام خطا).

کاتلین

fun startTimer(message: String, seconds: Int) {

    // Build the "set timer" intent
    val timerIntent = Intent(AlarmClock.ACTION_SET_TIMER).apply {
        putExtra(AlarmClock.EXTRA_MESSAGE, message)
        putExtra(AlarmClock.EXTRA_LENGTH, seconds)
        putExtra(AlarmClock.EXTRA_SKIP_UI, true)
    }

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(packageManager) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent)

    }
}

جاوا

public void startTimer(String message, int seconds) {

    // Build the "set timer" intent
    Intent timerIntent = new Intent(AlarmClock.ACTION_SET_TIMER)
            .putExtra(AlarmClock.EXTRA_MESSAGE, message)
            .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
            .putExtra(AlarmClock.EXTRA_SKIP_UI, true);

    // Check if there's a handler for the intent
    if (timerIntent.resolveActivity(getPackageManager()) == null) {

        // Can't resolve the intent! Fail this operation cleanly
        // (perhaps by showing an error message)

    } else {
        // Intent resolves, it's safe to fire it off
        startActivity(timerIntent);

    }
}

هم‌رسانی فایل‌ها در نمایه‌ها

گاهی اوقات برنامه‌ای باید به برنامه‌های دیگر اجازه دهد به فایل‌های خودش دسترسی داشته باشند. برای مثال، یک برنامه گالری تصویر ممکن است بخواهد تصاویر خود را با ویرایشگرهای تصویر هم‌رسانی کند. معمولاً به دو روش می‌توانید فایل را هم‌رسانی کنید: با شناسه منبع یکسان فایل یا شناسه منبع یکسان محتوا.

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

درعوض، باید فایل‌ها را با شناسه منبع یکسان محتوا هم‌رسانی کنید. شناسه‌های منبع یکسان محتوا فایل را به‌شیوه‌ای ایمن‌تر و قابل‌هم‌رسانی شناسایی می‌کنند. شناسه منبع یکسان محتوا حاوی مسیر فایل، و همچنین مرجع ارائه‌دهنده فایل، و شماره شناسه‌ای است که فایل را شناسایی می‌کند. بااستفاده از FileProvider می‌توانید برای هر فایلی Content ID ایجاد کنید. سپس می‌توانید آن شناسه محتوا را با برنامه‌های دیگر (حتی در نمایه دیگر) هم‌رسانی کنید. گیرنده می‌تواند از شناسه محتوا برای دسترسی به فایل واقعی استفاده کند.

برای مثال، در اینجا نحوه دریافت شناسه منبع یکسان محتوا برای یک فایل خاص URI:

کاتلین

// Open File object from its file URI
val fileToShare = File(fileUriToShare)

val contentUriToShare: Uri = FileProvider.getUriForFile(
        context,
        "com.example.myapp.fileprovider",
        fileToShare
)

جاوا

// Open File object from its file URI
File fileToShare = new File(fileUriToShare);

Uri contentUriToShare = FileProvider.getUriForFile(getContext(),
        "com.example.myapp.fileprovider", fileToShare);

وقتی روش getUriForFile() را فراخوانی می‌کنید، باید مرجع ارائه‌دهنده فایل را (در این مثال، "com.example.myapp.fileprovider") که در عنصر <provider> مانیفست برنامه‌تان مشخص شده است اضافه کنید. برای اطلاعات بیشتر درباره هم‌رسانی فایل‌ها با شناسه منبع یکسان محتوا، به هم‌رسانی فایل‌ها مراجعه کنید.

گوش دادن به اعلان‌ها

برنامه معمولاً یک زیرکلاس NotificationListenerService ارائه می‌دهد تا فراخوان‌های برگشتی را از سیستم درباره تغییرات اعلان‌ها دریافت کند. دستگاه‌های دارای نمایه کاری ممکن است بر نحوه عملکرد NotificationListenerService با برنامه‌تان تأثیر بگذارد.

در نمایه کاری

نمی‌توانید از NotificationListenerService برنامه‌ای که در نمایه کاری اجرا می‌شود استفاده کنید. وقتی برنامه‌تان در نمایه کاری اجرا می‌شود، سیستم NotificationListenerService برنامه‌تان را نادیده می‌گیرد. بااین‌حال، برنامه‌هایی که در نمایه شخصی اجرا می‌شوند می‌توانند اعلان‌ها را دریافت کنند.

در نمایه شخصی

وقتی برنامه‌تان در نمایه شخصی اجرا می‌شود، ممکن است برای برنامه‌هایی که در نمایه کاری اجرا می‌شوند اعلان دریافت نکنید. به‌طور پیش‌فرض، همه برنامه‌های نمایه شخصی فراخوان‌های برگشتی را دریافت می‌کنند اما سرپرست فناوری اطلاعات می‌تواند یک یا چند برنامه نمایه شخصی را که اجازه گوش دادن به تغییرات اعلان را دارند در فهرست مجاز قرار دهد. سپس سیستم برنامه‌هایی را که در فهرست مجاز نیستند مسدود می‌کند. در Android 8.0 (سطح API‏ ۲۶) یا نسخه‌های جدیدتر، کنترل‌کننده خط‌مشی دستگاه (DPC) که نمایه کاری را مدیریت می‌کند ممکن است بااستفاده از روش DevicePolicyManager، برنامه شما را از گوش دادن به اعلان‌های نمایه کاری مسدود کندsetPermittedCrossProfileNotificationListeners(). برنامه شما همچنان درباره اعلان‌های پست‌شده در نمایه شخصی، تماس برگشتی دریافت می‌کند.

آزمایش کردن برنامه برای سازگاری با نمایه‌های کاری

باید برنامه‌تان را در محیط نمایه کاری آزمایش کنید تا مشکلاتی را که باعث می‌شود برنامه‌تان در دستگاهی با نمایه کاری ناموفق باشد شناسایی کنید. به‌ویژه، آزمایش در دستگاه دارای نمایه کاری روش خوبی است برای اینکه مطمئن شوید برنامه‌تان هدف‌ها را به‌درستی مدیریت می‌کند: هدف‌هایی را که نمی‌توان مدیریت کرد فعال نمی‌کند، شناسه منبع یکسانی را که در بین نمایه‌ها کار نمی‌کند پیوست نمی‌کند، و غیره.

برنامه نمونه‌ای به‌نام TestDPC ارائه کرده‌ایم که می‌توانید از آن برای راه‌اندازی نمایه کاری در دستگاه Android دارای Android نسخه ۵.۰ (سطح میانای برنامه‌سازی کاربردی ۲۱) و بالاتر استفاده کنید. این برنامه روشی ساده برای آزمایش کردن برنامه‌تان در محیط نمایه کاری ارائه می‌دهد. همچنین می‌توانید از این برنامه برای پیکربندی نمایه کاری به‌صورت زیر استفاده کنید:

  • مشخص کنید کدام برنامه‌های پیش‌فرض در نمایه مدیریت‌شده دردسترس باشند
  • پیکربندی کنید که کدام هدف‌ها مجازند از یک نمایه به نمایه دیگر منتقل شوند

اگر برنامه‌ای را به‌صورت دستی ازطریق کابل USB در دستگاهی که نمایه کاری دارد نصب کنید، برنامه هم در نمایه شخصی و هم در نمایه کاری نصب می‌شود. پس‌از نصب برنامه، می‌توانید برنامه را تحت شرایط زیر آزمایش کنید:

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

آزمایش در نمایه‌های کاری: نکته‌ها و ترفندها

چند ترفند وجود دارد که ممکن است در آزمایش روی دستگاه دارای نمایه کاری برایتان مفید باشد.

  • همان‌طور که اشاره شد، وقتی برنامه‌ای را در دستگاه دارای نمایه کاری جانبی بار می‌کنید، در هر دو نمایه نصب می‌شود. اگر بخواهید، می‌توانید برنامه را از یک نمایه حذف کنید و آن را در نمایه دیگر نگه دارید.
  • اکثر فرمان‌های مدیر فعالیت موجود در پوسته Android Debug Bridge (adb) از پرچم --user پشتیبانی می‌کنند که به شما امکان می‌دهد مشخص کنید به‌عنوان کدام کاربر اجرا شود. با مشخص کردن کاربر، می‌توانید انتخاب کنید که به‌عنوان کاربر اصلی مدیریت‌نشده اجرا شود یا نمایه کاری. برای اطلاعات بیشتر، فرمان‌های پوسته ADB را ببینید.
  • برای پیدا کردن کاربران فعال در دستگاه، از فرمان list users مدیر بسته adb استفاده کنید. اولین عدد در رشته خروجی، شناسه کاربر است که می‌توانید با پرچم --user از آن استفاده کنید. برای اطلاعات بیشتر، به فرمان‌های پوسته ADB مراجعه کنید.

برای مثال، برای پیدا کردن کاربران در دستگاه، این فرمان را اجرا می‌کنید:

$ adb shell pm list users
UserInfo{0:Drew:13} running
UserInfo{10:Work profile:30} running

در این مورد، کاربر اصلی («درو») شناسه کاربر ۰ دارد و نمایه کاری شناسه کاربر ۱۰ دارد. برای اجرای برنامه در نمایه کاری، از فرمانی مانند این استفاده می‌کنید:

$ adb shell am start --user 10 \
-n "com.example.myapp/com.example.myapp.testactivity" \
-a android.intent.action.MAIN -c android.intent.category.LAUNCHER