Panduan ini mencakup konfigurasi dan penerapan yang diperlukan untuk menjalankan layanan yang dapat dirasakan Android (Layanan Latar Depan, atau FGS) dalam proses pribadi dari aplikasi Unity.
1. Mengonfigurasi dukungan layanan yang dapat dirasakan
Bagian ini menjelaskan cara menyiapkan izin yang diperlukan dan mendeklarasikan layanan dalam manifes project Anda.
1.1 Izin dan pernyataan layanan
Assets/Plugins/Android/AndroidManifest.xml
kustom harus mendeklarasikan Aktivitas peluncur, izin layanan yang dapat dirasakan,
izin notifikasi, izin jaringan, dan layanan:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.INTERNET" />
<application>
<activity
android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
<service
android:name="com.sample.fgs.DownloadService"
android:process=":downloader"
android:exported="false"
android:stopWithTask="false"
android:foregroundServiceType="dataSync" />
</application>
</manifest>
foregroundServiceType
harus cocok dengan pekerjaan sebenarnya: dataSync untuk transfer, mediaPlayback untuk
pemutaran, location untuk pelacakan lokasi. Harus sesuai dengan izin FOREGROUND_SERVICE_<TYPE> yang sesuai dan panggilan startForeground. Ketiganya harus konsisten, atau startup layanan akan gagal.
1.2 Menambahkan Java service-process ke project Unity
Proses layanan FGS menjalankan Java. Kemasi kode Java implementasi layanan ke dalam jar dan tempatkan di bawah Assets/Plugins/Android/. Unity secara otomatis menyertakan JAR di direktori tersebut ke dalam input libs/Gradle dan memaketkannya ke dalam APK; proses layanan memuat class ini saat runtime. Untuk mengetahui informasi selengkapnya tentang cara menerapkan layanan di Java atau Kotlin, lihat Penerapan Java.
2. Mengonfigurasi proses terpisah
Batas proses diaktifkan oleh satu atribut manifes:
android:process=":downloader"
Titik dua di awal membuat proses khusus aplikasi bernama
your.package.name:downloader. Proses ini memiliki PID yang berbeda dari proses utama
dan dapat bertahan setelah proses utama dihentikan.
2.1 Proses layanan tidak memiliki Unity
Untuk menghindari penggunaan libunity.so, runtime IL2CPP, dan sejenisnya dari Unity dalam proses layanan, Java sisi layanan tidak boleh menggunakan class Unity apa pun (termasuk UnityPlayer.currentActivity), dan hanya boleh bergantung pada Android Context, Intent ekstra, dan API platform. Runtime mesin Unity dimuat
hanya melalui UnityPlayerActivity proses utama; proses
:downloader pribadi tidak memuat library ini, sehingga mereferensikan class Unity
tidak berfungsi dalam proses layanan.
2.2 Titik entri startup proses
Setelah proses utama memanggil startForegroundService, sistem membuat fork proses layanan :downloader:
- Membuat instance
Applicationdan memanggilApplication.onCreate— ini berjalan di setiap proses, sehingga inisialisasi yang diperlukan oleh proses layanan harus dilakukan lagi di sini. Untuk mengetahui informasi selengkapnya, lihat bagian 2.3. - Membuat
DownloadServicedan memanggilonCreate— ini adalah titik entri service-process. - Memanggil kembali
onStartCommanddengan Intent yang dibuat saat startup. Layanan membuat dirinya menjadi latar depan di sini dan memulai pekerja. Untuk mengetahui informasi selengkapnya, lihat bagian 3.1.
2.3 Status ada satu kali per proses
Kode Dex dibagikan hanya baca, tetapi status runtime tidak:
Application.attachBaseContextdanApplication.onCreateberjalan di setiap proses yang menghosting komponen aplikasi.- Penginisialisasi statis dan kolom statis ada secara independen di setiap proses. Menetapkan kolom statis dalam proses utama tidak berkomunikasi dengan layanan.
- Unity, C#, dan Aktivitas tetap berada di proses utama.
3. Penerapan Java
Bagian ini membahas sisi Java dari modul FGS: 3.1 dan 3.2 berada
DownloadService dalam proses layanan; 3.3 berada FgsBridge dalam proses
utama (panggilan C# titik entri menggunakan JNI).
3.1 Promosikan ke latar depan terlebih dahulu
public class DownloadService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
startForegroundCompat();
// Start the download thread; must come after promoting to foreground
// ...
} catch (Exception e) {
Log.e(TAG, "onStartCommand() failed [errorType="
+ e.getClass().getSimpleName() + "]: " + e.getMessage(), e);
stopSelf();
}
return START_NOT_STICKY;
}
private void startForegroundCompat() {
Notification notification = buildNotification(0L);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(NOTIFICATION_ID, notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
} else {
startForeground(NOTIFICATION_ID, notification);
}
}
...
3.2 Menambahkan notifikasi
Layanan yang dapat disimak memerlukan notifikasi berkelanjutan di saluran notifikasi untuk melaporkan progres. Notifikasi ini membawa tindakan Berhenti yang mengirimkan
ACTION_STOP ke layanan itu sendiri menggunakan PendingIntent.getService untuk menghentikannya.
private Notification buildNotification(long progressBytes) {
int progressMib = (int) (progressBytes / MIB);
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(getPackageName());
PendingIntent contentIntent = launchIntent != null
? PendingIntent.getActivity(this, 0, launchIntent,
PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT)
: null;
Intent stopIntent = new Intent(this, DownloadService.class)
.setAction(ACTION_STOP);
PendingIntent stopPendingIntent = PendingIntent.getService(this, 0,
stopIntent, PendingIntent.FLAG_IMMUTABLE
| PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this,
CHANNEL_ID)
.setContentTitle("Download service")
.setContentText("Downloaded " + progressMib + " MB / " + TOTAL_MIB + " MB")
.setSmallIcon(android.R.drawable.stat_sys_download)
.setProgress(TOTAL_MIB, progressMib, false)
.setOngoing(true)
.setOnlyAlertOnce(true)
.addAction(new Notification.Action.Builder(null,
"Stop", stopPendingIntent).build());
if (contentIntent != null) {
builder.setContentIntent(contentIntent);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
builder.setForegroundServiceBehavior(
Notification.FOREGROUND_SERVICE_IMMEDIATE);
}
return builder.build();
}
3.3 Titik entri host Java
FgsBridge adalah titik entri Java yang digunakan proses utama untuk mengontrol FGS
(berjalan di proses utama, bukan proses layanan). C# memanggil metode statis ini menggunakan JNI untuk memulai dan menghentikan layanan serta menangani izin notifikasi:
public class FgsBridge {
// Start the :downloader perceptible service and begin downloading
public static void startDownloadService(Context context) {
try {
context.startForegroundService(new Intent(context, DownloadService.class));
} catch (Exception e) {
Log.e(TAG, "startDownloadService() failed [errorType="
+ e.getClass().getSimpleName() + "]: " + e.getMessage(), e);
}
}
// Stop the service: sends a stop intent; the service removes its
// notification before exiting
public static void stopDownloadService(Context context) {
try {
context.startService(new Intent(context, DownloadService.class)
.setAction(ACTION_STOP));
} catch (Exception e) {
Log.e(TAG, "stopDownloadService() failed [errorType="
+ e.getClass().getSimpleName() + "]: " + e.getMessage(), e);
}
}
// Request notification permission (only needed on API 33+; if already
// granted, no dialog is shown and it returns immediately)
public static void requestNotificationPermission(Activity activity) {
if (Build.VERSION.SDK_INT < 33) {
return;
}
try {
activity.requestPermissions(
new String[] { POST_NOTIFICATIONS }, NOTIFICATION_PERMISSION_REQUEST);
} catch (Exception e) {
Log.e(TAG, "requestNotificationPermission() failed [errorType="
+ e.getClass().getSimpleName() + "]: " + e.getMessage(), e);
}
}
}
4. Mulai dan kontrol FGS dari Unity (C#)
AndroidBridge adalah wrapper C# yang memanggil FgsBridge menggunakan JNI. Tiga implementasi
metode:
public static void StartDownloadService()
{
#if UNITY_ANDROID && !UNITY_EDITOR
CallStatic("startDownloadService");
#else
Debug.Log("StartDownloadService() no-op outside Android");
#endif
}
public static void StopDownloadService()
{
#if UNITY_ANDROID && !UNITY_EDITOR
CallStatic("stopDownloadService");
#else
Debug.Log("StopDownloadService() no-op outside Android");
#endif
}
public static void RequestNotificationPermission()
{
#if UNITY_ANDROID && !UNITY_EDITOR
CallStatic("requestNotificationPermission");
#else
Debug.Log("RequestNotificationPermission() no-op outside Android");
#endif
}
CallStatic adalah helper JNI internal yang menyelesaikan class FgsBridge dan memanggil metode statis Java yang sesuai. RequestNotificationPermission
harus dipanggil saat aplikasi dimulai agar notifikasi muncul
segera saat proses layanan dimulai.