[AndroidAdvance] Bài 2- Bound Service trong Android sử dụng extend Binder

Phần trước các bạn đã hiểu về cách sử dụng Service. Thật đơn giản chỉ với ba phương thức onStartCommand(), onCreate(), onDestroy() là ta đã tạo được một Service chạy ngầm trong hệ thống. Chúng ta thử đi phân tích ví dụ này xem nhé!

Ta xây dựng một app chơi nhạc bằng Service (Demo phần 1), tại MainActivity ta gọi startService(intent) thì hệ thống gọi onCreate và onStartCommand của MyService, bài hát bắt đầu được phát từ đây cho tới khi Activity gọi stopService(intent). Bây giờ tôi muốn trong lúc bài hát đang phát tức là giữa khoảng gọi startService() và stopService() tôi sẽ gọi hàm fastFoward(pos) để tua bài hát tới vị trí pos. Giả sử trong class MyPlayer ta xây dựng thêm hàm fastForward như code dưới:


class MyPlayer {
	// đối tượng này giúp phát một bài nhạc
	private MediaPlayer mediaPlayer;

	public MyPlayer(Context context) {
		// Nạp bài nhạc vào mediaPlayer
		mediaPlayer = MediaPlayer.create(context, R.raw.nhatkydoitoi);
		// Đặt chế độ phát lặp lại liên tục
		mediaPlayer.setLooping(true);
	}

	// Tua bài hát (phát tiếp bài hát từ vị trí pos trở đi)
	public void fastForward(int pos){
		mediaPlayer.seekTo(pos);
	}
	// phát nhạc
	public void play() {
		if (mediaPlayer != null) {
			mediaPlayer.start();
		}
	}

	// dừng phát nhạc
	public void stop() {
		if (mediaPlayer != null) {
			mediaPlayer.stop();
		}
	}
}

Bạn thử nghĩ xem ta làm cách nào để tại ActivityMain.java ta có thể gọi được phương thức fastForward() này đây?
Với cách khởi tạo Service UnBound thì không thể nào làm được.Vấn đề ở chỗ trong toàn bộ code không có lệnh nào khởi tạo MyService theo cách thông thường cả:

MyService mService = new MyService();

Thực tế khởi tạo một Service ta sử dụng intent = new Intent(Context, MyService.class). Do đó, vấn đề đặt ra phải làm sao lấy được đối tượng MyService này để có thể gọi myService.fastForward() bất kỳ lúc nào trong khoảng thời gian hoạt động của Service.

Thực tế đó, cách khởi tạo Service thứ hai là BoundService đã ra đời (Các bạn xem hình ảnh chu trình hoạt động ở bài trước nhé!).

Với cách thứ hai này Service sẽ giúp chúng ta thực hiện điều đó, nó không dùng onStartCommand để khởi động nữa mà nó thay thế bằng hai phương thức mới là onBind() và onUnBind(), tại Activity sẽ không dùng startService mà dùng bindService và unbindService. Hiện thực cách thứ hai có 3 phương pháp:

+ extend lớp Binder

+ Sử dụng Messenger

+ Sử dụng AIDL

Bài hôm nay mình sẽ giới thiệu các bạn sử dụng phương pháp thứ nhất extend lớp Binder:

Screenshot from 2014-07-10 15:09:42

 

 

Nhìn vào hình trên chắc hẳn các bạn cũng đã hiểu phần nào cơ chế hoạt động. Trước hết tại ActivityMain ta khai báo một đối tượng ServerConnection và viết đè hai phương thức onServiceConnected và onServiceDisconnect. Đối tượng này sẽ giúp chúng ta thực hiện kết nối tới Service.

Ở Activity Main để bắt đầu chạy Service ta sẽ gọi phương thức bindService() phương thức này mang theo đối tượng ServerConnection, ngay sau đó hệ thống sẽ gọi onCreate() rồi onBind() từ MyService, khi onBind được gọi nó sẽ trả về một đối tượng kiểu Binder, ngay lúc này phương thức onServiceConnected của đối tưọng ServerConnection sẽ được gọi và nhận đối tượng kiểu Binder, ta thực hiện lệnh mService = binder.getService() lệnh này sẽ trả về đối tượng MyService, vậy bây giờ các bạn có thể dùng mService để gọi các phương thức trong Service rồi. ví dụ như mService.tuaNhac();

Khi gọi unBindService() tại ActivityMain thì phương thức onUnBind tại Service sẽ tự động được gọi, đồng thời phương thức onDisconnect()  của đối tượng ServerConnection cũng sẽ được gọi, nó sẽ đóng kết nối lại.

Nhận xét: Mô hình hoạt động này giống như Client-Server trong lập trình mạng java, trong đó Activity đóng vai trò là Client, Service đóng vai trò là Server.

Theo cơ chế họat động trên ta sẽ đi vào phát triển demo chơi nhạc của bài trước.

Tại file giao diện ta sẽ tạo thêm một button mới dùng để tua nhạc.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="Service trong Android"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/btOn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="135dp"
        android:text="Mở" />

    <Button
        android:id="@+id/btOff"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btOn"
        android:layout_centerHorizontal="true"
        android:text="Tắt" />

    <Button
        android:id="@+id/btTua"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/btOff"
        android:layout_below="@+id/btOff"
        android:layout_marginTop="19dp"
        android:text="Tua tới 1'00 " />

</RelativeLayout>

Tại file MyService.java ta xây dựng như sau:

package clbtinhoc.ictu.serviceonbind;

import clbtinhoc.ictu.intentfilter.R;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
	private MyPlayer myPlayer;
	private IBinder binder;

	// phương thức khởi tạo
	@Override
	public void onCreate() {
		Log.d("ServiceDemo", "Đã gọi onCreate()");

		myPlayer = new MyPlayer(this);
		binder = new MyBinder(); // do MyBinder được extends Binder
		super.onCreate();
	}

	// Bắt đầu một Service
	@Override
	public IBinder onBind(Intent intent) {
		Log.d("ServiceDemo", "Đã gọi onBind()");
		myPlayer.play();
		// trả về đối tượng binder cho ActivityMain
		return binder;
	}

	// Kết thúc một Service
	@Override
	public boolean onUnbind(Intent intent) {
		Log.d("ServiceDemo", "Đã gọi onBind()");
		myPlayer.stop();
		return super.onUnbind(intent);
	}

	// Xây dựng các phương thức thực hiện nhiệm vụ,
	// ở đây mình demo phương thức tua bài hát
	public void fastForward(){
		myPlayer.fastForward(60000); // tua đến giây thứ 60
	}

	public class MyBinder extends Binder{

		// phương thức này trả về đối tượng MyService
		public MyService getService() {
			return MyService.this;
		}
	}
}

// Xây dựng một đối tượng riêng để chơi nhạc
class MyPlayer {
	// đối tượng này giúp phát một bài nhạc
	private MediaPlayer mediaPlayer;

	public MyPlayer(Context context) {
		// Nạp bài nhạc vào mediaPlayer
		mediaPlayer = MediaPlayer.create(context, R.raw.nhatkydoitoi);
		// Đặt chế độ phát lặp lại liên tục
		mediaPlayer.setLooping(true);
	}

	public void fastForward(int pos){
		mediaPlayer.seekTo(pos);
	}
	// phát nhạc
	public void play() {
		if (mediaPlayer != null) {
			mediaPlayer.start();
		}
	}

	// dừng phát nhạc
	public void stop() {
		if (mediaPlayer != null) {
			mediaPlayer.stop();
		}
	}
}
[/code ]

Tại file ActivityMain.java ta xây dựng lại như sau:

package clbtinhoc.ictu.serviceonbind;

import clbtinhoc.ictu.intentfilter.R;
import clbtinhoc.ictu.serviceonbind.MyService.MyBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;

public class ActivityMain extends Activity {

	private MyService myService;
	private boolean isBound = false;
	private ServiceConnection connection;

	@Override
	public void onCreate(Bundle bundle){
		super.onCreate(bundle);
		setContentView(R.layout.layout_main);

		final Button btOn = (Button) findViewById(R.id.btOn);
		final Button btOff = (Button) findViewById(R.id.btOff);
		final Button btFast = (Button) findViewById(R.id.btTua);

		// Khởi tạo ServiceConnection
		connection = new ServiceConnection() {

			// Phương thức này được hệ thống gọi khi kết nối tới service bị lỗi
			@Override
			public void onServiceDisconnected(ComponentName name) {
				isBound = false;
			}

			// Phương thức này được hệ thống gọi khi kết nối tới service thành công
			@Override
			public void onServiceConnected(ComponentName name, IBinder service) {
				MyBinder binder = (MyBinder) service;
				myService = binder.getService(); // lấy đối tượng MyService
				isBound = true;
			}
		};

		// Khởi tạo intent
		final Intent intent = new Intent(ActivityMain.this, MyService.class);

		btOn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// Bắt đầu một service sủ dụng bind
				bindService(intent, connection, Context.BIND_AUTO_CREATE);
				// Đối thứ ba báo rằng Service sẽ tự động khởi tạo
			}
		});

		btOff.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// Nếu Service đang hoạt động
				if(isBound){
					// Tắt Service
					unbindService(connection);
					isBound = false;
				}
			}
		});

		btFast.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// nếu service đang hoạt động
				if(isBound){
					// tua bài hát
					myService.fastForward();
				}else{
					Toast.makeText(ActivityMain.this, "Service chưa hoạt động", Toast.LENGTH_SHORT).show();
				}
			}
		});

	}
}

Bây giờ các bạn chạy sau đó ấn nút Mở để Service hoạt động, sau khi mở thì thử ấn nút Tua xem 🙂

Các bạn có thể down code mẫu ở đây về tham khảo.
https://www.dropbox.com/sh/o8rrpkbbf9o4g0q/AAAgDq3zgQz3GTOdxmHMiGZ3a

6 thoughts on “[AndroidAdvance] Bài 2- Bound Service trong Android sử dụng extend Binder

  1. Chào bạn, bài viết của bạn rất hay.Nhưng sao mình gặp vấn đề với boundService là khi xoay máy thì nhạc bị tắt, bấm nút Back nhạc cũng tắt(cả với unbound).Điều này không đúng với Service, vậy làm sao giải quyết đây?
    Cam ơn, chúc bạn vui vẻ

  2. Bài viết của tác giả rất hay và rõ ràng, tuy nhiên đối với ứng dụng nghe nhạc, trên thực tế cần xử lý phức tạp hơn chút. Bài trên tác giả đang hướng dẫn riêng về boundService. Nhưng nó có một đặc điểm như bạn nói là nếu back lại hay thoát khỏi ứng dụng là sẽ dừng.
    Vì thế, với riêng ứng dụng music player, ta cần sử dụng UnboundService (startService()) trc. Điều này giúp service chạy đc cả khi tắt ứng dụng.
    Sau đó bạn mới tương tác activity của mình với service này bằng bindService() để thực hiện các thao tác như tác giả nói là forward, hoặc là truyền bài hát mới vào khi user chọn một bài hát khác…
    Bạn có thể đọc thêm ở phần note của google: “Binding to a Started Service” bên phải
    http://developer.android.com/guide/components/bound-services.html

  3. Pingback: Service trong Android là gì? - Thiết Kế Web Đô Trịnh

Bình luận về bài viết này