In-depth analysis of Android Service (6) (End)

Article Directory

    • In-depth Analysis of Android Service (End)
    • 1. Service Lifecycle Management
    • 2. Service lifecycle approach
      • 2.1 onCreate()
      • 2.2 onStartCommand(Intent intent, int flags, int startId)
      • 2.3 onBind(Intent intent)
      • 2.4 onUnbind(Intent intent)
      • 2.5 onRebind(Intent intent)
      • 2.6 onDestroy()
    • 3. Service Restart Policy
    • 4. Use Service for foreground tasks
    • 5. Implementing the Foreground Service Example
      • 5.1 Create Front Office Service
      • 5.2 Start the front desk service
    • 6. Service Optimization and Debugging
      • 6.1Replace traditional Service with JobS scheduler
      • 6.2 Use WorkManager to handle background tasks
      • 6.3 Commissioning and monitoring
    • 7. Summary of sample code
      • 7.1 Server code (Messenger)
      • 7.2 Client Code (Messenger)
      • 7.3 Server Code (AIDL)
      • 7.4 Client Code (AIDL)
    • 8. Sum up

In-depth Analysis of Android Service (End)

1. Service Lifecycle Management

Service Lifecycle management is key to ensuring that Service resources can be started, run, stopped, and cleaned up correctly. An understood Service lifecycle approach can help developers better manage and optimize Service.

2. Service lifecycle approach

The Service main life cycle methods of include:

  1. onCreate()
  2. onStartCommand(Intent intent, int flags, int startId)
  3. onBind(Intent intent)
  4. onUnbind(Intent intent)
  5. onRebind(Intent intent)
  6. onDestroy()

2.1 onCreate()

Called when Service is created. Any necessary resources are typically initialized here.

@Override
public void onCreate() {
    super.onCreate();
    // Initialize resources,Such as thread pool、Database connection, etc.
} 

2.2 onStartCommand(Intent intent, int flags, int startId)

Called each time startService() the method is started Service. This method is the main entry point to handle the actual business logic. The return value determines how the system restarts the service after it is killed due to insufficient memory.

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // Handle business logic,Such as downloading files、Play music etc.
    return START_STICKY;  // START_NOT_STICKY, START_REDELIVER_INTENT, or START_STICKY_COMPATIBILITY
} 

2.3 onBind(Intent intent)

Called when the client is Service bound. Returns an IBinder object for communicating with the client.

@Override
public IBinder onBind(Intent intent) {
    // Return communication interface
    return binder;
} 

2.4 onUnbind(Intent intent)

Called when all clients are unbound. This is where binding-related resources are typically cleaned up.

@Override
public boolean onUnbind(Intent intent) {
    // Clean up binding related resources
    return super.onUnbind(intent);
} 

2.5 onRebind(Intent intent)

Called when the new client is bound Service again after the previous call onUnbind().

@Override
public void onRebind(Intent intent) {
    super.onRebind(intent);
} 

2.6 onDestroy()

Called before Service being destroyed. Usually clean up all resources here.

@Override
public void onDestroy() {
    super.onDestroy();
    // Release resources
} 

3. Service Restart Policy

The return value of the onStartCommand method determines how the system restarts it after it is killed Service due to insufficient system memory:

  • START_STICKY : The service will be restarted automatically after being stopped by the system. The Intent is not preserved, meaning that the data is not preserved, but the service continues to run.
  • START_NOT_STICKY : The service will not restart automatically after being stopped by the system unless there is a new Intent.
  • START_REDELIVER_INTENT : After the service is terminated by the system, it will be restarted automatically and the last Intent will be retransmitted.
  • START_STICKY_COMPATIBILITY : Similar to START_STICKY, but for compatibility with lower versions.

4. Use Service for foreground tasks

To ensure that Service it is not terminated by the system when running in the background, it can be Service promoted to a foreground service. This is done by calling startForeground() the method and providing a persistent notification.

5. Implementing the Foreground Service Example

5.1 Create Front Office Service

public class ForegroundService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Notification notification = createNotification();
        startForeground(1, notification);
    }

    private Notification createNotification() {
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        String channelId = "foreground_service_channel";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(channelId, "Foreground Service Channel", NotificationManager.IMPORTANCE_DEFAULT);
            notificationManager.createNotificationChannel(channel);
        }
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
                .setContentTitle("Foreground Service")
                .setContentText("Service is running in the foreground")
                .setSmallIcon(R.drawable.ic_service_icon);
        return builder.build();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Handle business logic
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForeground(true);
    }
} 

5.2 Start the front desk service

To start the foreground service in Activity :

Intent intent = new Intent(this, ForegroundService.class);
startService(intent); 

6. Service Optimization and Debugging

To optimize Service the performance and reliability of the, the following methods can be used:

6.1Replace traditional Service with JobS scheduler

Recommended JobScheduler for tasks that need to run under specific conditions. It optimizes the execution time of the task based on system resources and conditions.

JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo jobInfo = new JobInfo.Builder(1, new ComponentName(this, MyJobService.class))
        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
        .setRequiresCharging(true)
        .build();
jobScheduler.schedule(jobInfo); 

6.2 Use WorkManager to handle background tasks

A modern task scheduling mechanism is WorkManager provided, which is suitable for the scenario where task execution needs to be guaranteed. It can automatically handle retries and constraints on tasks.

OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class)
        .setConstraints(new Constraints.Builder().setRequiresCharging(true).build())
        .build();
WorkManager.getInstance(this).enqueue(workRequest); 

6.3 Commissioning and monitoring

Use to StrictMode detect potential performance problems, such as disk reads and writes and network access:

if (BuildConfig.DEBUG) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
            .detectAll()
            .penaltyLog()
            .build());
    StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
            .detectAll()
            .penaltyLog()
            .build());
} 

Utilize Android Studio’s Profiler tool to monitor Service CPU, memory, and network usage to identify and resolve performance bottlenecks.

7. Summary of sample code

7.1 Server code (Messenger)

public class MessengerService extends Service {
    static final int MSG_SAY_HELLO = 1;

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "Hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    final Messenger messenger = new Messenger(new IncomingHandler());

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
} 

7.2 Client Code (Messenger)

public class MainActivity extends AppCompatActivity {
    Messenger messenger = null;
    boolean isBound = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            messenger = new Messenger(service);
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            messenger = null;
            isBound = false;
        }
    };

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

        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);

        Button sendButton = findViewById(R.id.sendButton);
        sendButton.setOnClickListener(v -> {
            if (isBound) {
                Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
                try {
                    messenger.send(msg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBound) {
            unbindService(connection);
            isBound = false;
        }
    }
} 

7.3 Server Code (AIDL)

public class MyAidlService extends Service {
    private final IMyAidlInterface.Stub binder = new IMyAidlInterface.Stub() {
        @Override
        public int add(int a, int b) {
            return a + b;
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
} 

7.4 Client Code (AIDL)

public class MainActivity extends AppCompatActivity {
    IMyAidlInterface myAidlService = null;
    boolean isBound = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myAidlService = IMyAidlInterface.Stub.asInterface(service);
            isBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            myAidlService = null;
            isBound = false;
        }
    };

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

        Intent intent = new Intent(this, MyAidlService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);

        Button addButton = findViewById(R.id.addButton);
        addButton.setOnClickListener(v -> {
            if (isBound) {
                try {
                    int result = myAidlService.add(5, 3);
                    Toast.makeText(MainActivity.this, "Result: " + result, Toast.LENGTH_SHORT).show();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBound) {
            unbindService(connection);
            isBound = false;
        }
    }
} 

8. Sum up

Reasonable design and optimization of Android Service can ensure that applications run efficiently and stably in the background and provide a good user experience. It is hoped that this series of articles can help developers better understand Service the working mechanism and optimization strategies, and provide reference and guidance for the development of high-quality Android applications.