Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีรัน Android Service ทำงานใน Background ตลอดเวลา

ภาพรวม

ตัวอย่างนี้สาธิตการสร้าง Service ที่ทำงานในพื้นหลัง (Background) และ Restart อัตโนมัติเมื่อถูกระบบ Kill ด้วย START_STICKY และ onTaskRemoved

⚠️ ข้อจำกัด Android 8.0+ (API 26+)

Background Service มีข้อจำกัดเข้มงวด:

  • ไม่สามารถรัน Background Service ได้นานเกิน 1-2 นาที (ยกเว้น Foreground Service)
  • ต้องใช้ Foreground Service พร้อม Notification สำหรับงานที่ต้องรันนาน
  • START_STICKY และ onTaskRemoved ไม่รับประกันการ Restart ใน Doze Mode / App Standby
  • ใช้ WorkManager สำหรับงานที่ต้องการความน่าเชื่อถือและ Deferrable

ขั้นตอนที่ 1: สร้างโปรเจกต์ใหม่

Android Studio → File → New Project → Empty Activity

ขั้นตอนที่ 2: จัดรูปแบบหน้าจอ (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Start Background Service"
        android:textSize="16sp"
        android:textStyle="bold"/>

</RelativeLayout>

ขั้นตอนที่ 3: สร้าง Service (MyService.java)

package app.com.sample;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class MyService extends Service {

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // แสดง Toast ว่า Service เริ่มทำงาน
        Toast.makeText(getApplicationContext(), 
            "Service running in background", Toast.LENGTH_SHORT).show();

        // ทำงาน Background ที่นี่ (Download, Sync, Music Player, etc.)

        // START_STICKY: ระบบจะ Restart Service หากถูก Kill (เมื่อหน่วยความจำพอ)
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        // ไม่รองรับ Binding
        return null;
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        // เรียกเมื่อ User สวายปิดแอปจาก Recent Apps
        // Restart Service เอง
        Intent restartIntent = new Intent(getApplicationContext(), this.getClass());
        restartIntent.setPackage(getPackageName());
        startService(restartIntent);
        
        super.onTaskRemoved(rootIntent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Toast.makeText(getApplicationContext(), "Service destroyed", Toast.LENGTH_SHORT).show();
    }
}

ขั้นตอนที่ 4: เขียนโค้ด MainActivity.java

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // เริ่ม Service
                startService(new Intent(getApplicationContext(), MyService.class));
            }
        });
    }
}

ขั้นตอนที่ 5: AndroidManifest.xml (สำคัญ: ต้องประกาศ Service)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="app.com.sample">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!-- ประกาศ Service ที่นี่ -->
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="false"> <!-- exported="false" ปลอดภัยกว่า -->
        </service>

        <activity android:name=".MainActivity">
            <intent-filter>
                
                
            </intent-filter>
        </activity>

    </application>
</manifest>

การทดสอบ

รันแอป → กดปุ่ม "Start Background Service" → ดู Toast → สวายปิดแอปจาก Recent Apps → Service จะ Restart เอง (ใน Android เก่า)

วิธีรัน Android Service ทำงานใน Background ตลอดเวลา วิธีรัน Android Service ทำงานใน Background ตลอดเวลา

แนวทางปัจจุบันที่แนะนำ (Best Practice 2024+)

Use CaseSolution ที่แนะนำ
Sync ข้อมูลเป็นระยะWorkManager (PeriodicWorkRequest)
Download/Upload ไฟล์ใหญ่WorkManager + Foreground Service
Play Music / VideoMediaSession + Foreground Service
Location TrackingForeground Service + FusedLocationProvider
Real-time ChatFirebase Cloud Messaging (FCM)
Sensor MonitoringForeground Service

Foreground Service Template (Android 8.0+)

public class MyForegroundService extends Service {
    private static final int NOTIFICATION_ID = 1;
    private static final String CHANNEL_ID = "ForegroundServiceChannel";

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // สร้าง Notification
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Service Running")
            .setContentText("Background task in progress...")
            .setSmallIcon(R.drawable.ic_notification)
            .setOngoing(true)
            .build();

        // เริ่ม Foreground Service
        startForeground(NOTIFICATION_ID, notification);
        
        // ทำงานจริงที่นี่...
        
        return START_NOT_STICKY; // หรือ START_STICKY
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(
                CHANNEL_ID, "Foreground Service", NotificationManager.IMPORTANCE_LOW);
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
    }
}

WorkManager Example (แทน Background Service)

// build.gradle
implementation "androidx.work:work-runtime-ktx:2.8.1"

// Worker Class
class SyncWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
    override suspend fun doWork(): Result {
        // ทำงาน Background ที่นี่
        return Result.success()
    }
}

// Schedule งาน
val request = PeriodicWorkRequestBuilder(15, TimeUnit.MINUTES)
    .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
    .build()

WorkManager.getInstance(context).enqueueUniquePeriodicWork(
    "sync_work", ExistingPeriodicWorkPolicy.KEEP, request)