Hướng dẫn làm một app nghe nhạc online và offline đơn giản (Part 2-1)

Như ở phần I mình đã nói qua những kiến thức cơ bản để tạo ra một ứng dụng nghe nhạc.

Bây giờ mình xin giới thiệu các bạn bài viết làm thế nào để lấy ra list nhạc mà các bạn download trên thẻ nhớ. Để làm được điều đó các bạn nên đọc qua SQL Lite trong android và ContentResolver trong android . Mình sẽ giới thiệu qua hai mục này cho các bạn đọc để các bạn nắm được mục đích của hai phần này là gì .

Content Provider

Content Provider là 1 trong 4 thành phần cơ bản của 1 ứng dụng Android thường có bao gồm:

1. Activity 2. Service 3. Broadcast Receiver 4. Content Provider

Một Content Provider cung cấp một tập chi tiết dữ liệu ứng dụng đến các ứng dụng khác. Thường được sử dụng khi chúng ta muốn tạo cơ sở dữ liệu dưới dạng public (các ứng dụng khác có thể truy xuất ).

Dữ liệu thường được lưu trữ ở file hệ thống, hoặc trong một SQLite database.

Đơn giản để các bạn có thể hình dung như : Danh bạ, Call log, cấu hình cài đặt...trên điện thoại là dữ liệu dưới dạng Content Provider.

Content Provider hiện thực một tập phương thức chuẩn mà các ứng dụng khác có thể truy xuất và lưu trữ dữ liệu của loại nó điều khiển.

Tuy nhiên, những ứng dụng không thể gọi các phương thức trực tiếp. Hơn thế chúng dùng lớp Content Resolver và gọi những phương thức đó. Một Content Resolver có thể giao tiếp đến nhiều content provider; nó cộng tác với các provider để quản lý bất kỳ giao tiếp bên trong liên quan.

Đơn giản hơn, chúng ta có thể làm 1 ứng dụng nhỏ để lấy tất cả các thông tin cấu hình trong máy load lên listview. Các bạn có thể chạy Project ContentProviderDemo1 trong SourcecodeDemo.

Chúng ta có thể tìm hiểu sơ qua về code của demo này, rất ngắn gọn.

      ContentResolver cr = getContentResolver();
      Cursor cursor = cr.query(Settings.System.CONTENT_URI, null, null, null, null);
      startManagingCursor(cursor);

      ListView listView = (ListView) findViewById(R.id.listView);
      String[] from = { Settings.System.NAME, Settings.System.VALUE };
      int[] to = { R.id.textName, R.id.textValue };
      SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.row, cursor, from, to);
      listView.setAdapter(adapter);

Như các bạn thấy, chỉ cần 2 dòng code đơn giản để lấy được con trỏ thao tác trên tập dữ liệu cần lấy:

      ContentResolver cr = getContentResolver();
      Cursor cursor = cr.query(Settings.System.CONTENT_URI, null, null, null, null);

Lớp Content Resolver cung cấp các phương thức xử lý dữ liệu thông qua các Uri, mỗi Content Provider có 1 Uri cụ thể , ở đây Uri Settings.System.CONTENT_URI sẽ trả lại tập dữ liệu là thông tin cấu hình của thiết bị. Sau khi lấy được con trỏ tới tập dữ liệu, việc còn lại đơn giản là bind data lên listview để hiển thị:

      startManagingCursor(cursor);
      ListView listView = (ListView) findViewById(R.id.listView);
      String[] from = { Settings.System.NAME, Settings.System.VALUE };
      int[] to = { R.id.textName, R.id.textValue };
      SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.row, cursor, from, to);
      listView.setAdapter(adapter)

2. Tạo và sử dụng 1 Content Provider do người dùng tự định nghĩa

Để dễ hiểu hơn các bạn mở Project ContentProviderDemo trong Sourcecode đã down về. Trong Project đó mình tạo 1 Content Provider Books, mỗi bản ghi Book bao gồm 2 trường : ID và Title.

Sau đây là các bước để tạo 1 Content Provider cơ bản ( cụ thể là tạo ContentProvider Book)

1. Tạo 1 class thừa kế lớp ContentProvider

     public class BookProvider extends ContentProvider

2. Định nghĩa 1 biến Uri (public static final ) được gọi CONTENT_URI.

Các xâu này luôn được bắt đầu bằng “content://” tiếp theo đó là nội dung của mà ContentProvider xử lý. Xâu này phải có đặc tính là duy nhất.

    public static final String PROVIDER_NAME =            "com.app.provider.Books";
	public static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/books");

3. Khai báo các xâu để định nghĩa cho từng thuộc tính tương ứng với các cột giá trị từ Cursor.

    public static final String _ID = "_id";
    public static final String TITLE = "title";

4. Chúng ta cần tạo hệ thống chứa dữ liệu cho

ContentProvider, có thể chưa dưới nhiều hình thức : sử dụng XML, thông qua CSDL SQLite, hay thậm chí là WebService. Trong Demo này chúng ta sử dụng cách phổ biến nhất đó là SQLite:

    private SQLiteDatabase bookDB;
	private static final String DATABASE_NAME = "Books";
	private static final String DATABASE_TABLE = "titles";
	private static final int DATABASE_VERSION = 1;

5. Định nghĩa tên của các cột mà chúng ta sẽ trả lại giá trị cho các clients.

Nếu chúng ta đang sử dụng Database ContentProvider hay các lớp SQLiteOpenHelper, tên các cột này chính là id của các cột trong cơ sở dữ liệu SQL. Trong trường hợp này, chúng ta phải gộp cả cột có giá trị là số nguyên được gọi “_id” để định nghĩa id của mỗi bản ghi.

Nếu đang sử dụng cơ sở dữ liệu SQLite, nó sẽ là INTEGER PRIMARY KEY AUTOINCREMENT. Tùy chọn AUTOINCREMENT không bắt buộc, có tác dụng tự động tăng ID của mỗi bản ghi lên nếu người dùng không nhập. Android cung cấp SQLiteOpenHelper giúp tạo và quản lý các phiên bản của cơ sở dữ liệu.

Mã:

private static final String DATABASE_CREATE =
		"create table " + DATABASE_TABLE +
		" (_id integer primary key autoincrement, "
		+ "title text not null);";

private static class DatabaseHelper extends SQLiteOpenHelper
	{
		public DatabaseHelper(Context context) {
			super(context, DATABASE_NAME , null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL(DATABASE_CREATE);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			db.execSQL("DROP TABLE IF EXISTS titles");
			onCreate(db);
		}
	}

6. Nếu chúng ta muốn public các dữ liệu kiểu byte như bitmap thì các trường mà chứa dữ liệu này nên là một xâu với 1 content://URI cho file đó. Đây chính là liên kết để các ứng dụng khác có thể truy cập và sử dụng dữ liệu bitmap này.

7. Sử dụng Cursor để thao tác trên tập dữ liệu : query (), update(), insert(), delete()….. Có thể gọi phương thức ContentResolver.notifyChange() để biếtkhi nào dữ liệu được cập nhật.

Add Book

@Override
	public Uri insert(Uri uri, ContentValues values) {
		long rowID = bookDB.insert(DATABASE_TABLE, "", values);
		if(rowID > 0)
		{
			Uri mUri = ContentUris.withAppendedId(CONTENT_URI, rowID);
			getContext().getContentResolver().notifyChange(mUri, null);
			return mUri;

		}
		throw new SQLException("Failed to insert new row into " + uri);
	}

Get All Books


@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder sqlBuilder = new SQLiteQueryBuilder();
		sqlBuilder.setTables(DATABASE_TABLE);
		if(uriMatcher.match(uri) == BOOK_ID)
			sqlBuilder.appendWhere(_ID + "=" + uri.getPathSegments().get(1));
		if(sortOrder == null || sortOrder == "")
			sortOrder = TITLE;
		Cursor c = sqlBuilder.query(bookDB, projection, selection, selectionArgs, null, null, sortOrder);
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

Mình chỉ demo 2 chức năng là thêm sách và lấy toàn bộ bản ghi trong CSDL , ngoài ra các phương thức edit, sửa , update, xóa... các bạn có thể tự làm .

8. Khai báo Content Provider trong file AndroidManifest.xml

    android:authorities="com.app.provider.Books" />

Như vậy chúng ta đã tạo xong ContentProvider Book tự định nghĩa.

9. Test thử thành quả :

     com.app.provider.Books/books

Mỗi content Provider gắn với 1 Uri cụ thể, như trên thì ContentProvider Book có Uri là: Mã: com.app.provider.Books/books

SQL lite trong android .

  1. Giới thiệu: Cũng như trên OS của iphone, Android cũng được gắn một chuẩn chương trình gọi là "sqlite3" dùng để:
  • Khởi tạo mới 1 database
  • Xác định các table trong SQL
  • Các truy vấn (queries)
  • Views
  • Thủ tục (triggers)
  • Chèn dòng trong table
  • Xóa dòng table
  • Cập nhật dòng table
  • Chạy truy vấn quản lý một tập tin database của SQLite.
  1. Sử dụng SQLite trong lập trình android
  • SQLite thực hiện đầy đủ chuẩn SQL-92 của SQL (chuẩn SQL-92 nếu rảnh có thể tự tìm hiểu)
  • Nó có hỗ trợ việc chạy triggers và cho phép hầu hết các truy vấn phức tạp
  • SQLite không thực hiện các ràng buộc toàn vẹn tham chiếu thông qua mô hình ràng buộc khoá ngoại
  • SQLite sử dụng một mô hình dữ liệu đánh máy thoải mái.
  1. Các chú ý:
  • Trong các ứng dụng thực tế, phải chú ý tới việc chia sẽ dữ liệu. Ví dụ như người dùng này không thể truy cập xem dữ liệu người khác được ...
  • Chú ý gắn cờ khi muốn đăng ký việc viết dữ liệu vào data <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  1. Kiểm tra database khởi tạo thành công:
  • Các bạn hãy vào DDMS và theo đường dẫn sau: data/data/<tên package lúc khởi tạo ứng dụng>/databases/
  • Ở đây nếu có tập tin database như tên bạn đặt là đã khởi tạo thành công, có thể kiểm tra ngày giờ lúc khởi tạo nằm ngay ở phía bên phải của tập tin database
  • Hướng dẫn cách xem dữ liệu trong database Vì bài viết của mình khá nhiều mục nên mình xin nói chi tiết code getlist muisic nhạc của mình ở PART 2-2

Dưới đây là các model app nhạc .


import java.io.Serializable;
import java.io.UnsupportedEncodingException;

public class Music implements Serializable {

	private static final long serialVersionUID = 1L;
	private int id;
	private String musicName;
	private String singer;
	private String albumName;
	private String albumPath;
	private String albumkey;
	private String musicPath;
	private String time;
	private String savePath;
	private long music_size;
	private boolean isLoaded;

	public boolean isLoaded() {
		return isLoaded;
	}

	public void setLoaded(boolean isLoaded) {
		this.isLoaded = isLoaded;
	}

	public Music() {
		super();
	}

	public Music(String musicName, String singer, String albumName,
			String savePath,long music_size) {
		super();
		setMusicName(musicName);
		setSinger(singer);
		setAlbumName(albumName);
		setSavePath(savePath);
		setMusicSize(music_size);
	}

	public String getAlbumkey() {
		return albumkey;
	}

	public void setAlbumkey(String albumkey) {
		this.albumkey = albumkey;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getMusicName() {
		return setStr(musicName);
	}

	public void setMusicName(String musicName) {
		this.musicName = musicName;
	}

	public String getSinger() {
		return setStr(singer);
	}

	public void setSinger(String singer) {
		this.singer = singer;
	}

	public String getAlbumName() {
		return setStr(albumName);
	}

	public void setAlbumName(String albumName) {
		this.albumName = albumName;
	}

	public String getAlbumPath() {
		return albumPath;
	}

	public void setAlbumPath(String albumPath) {
		this.albumPath = albumPath;
	}

	public String getMusicPath() {
		return musicPath;
	}

	public void setMusicPath(String musicPath) {
		this.musicPath = musicPath;
	}

	public String getTime() {
		return time;
	}

	public void setTime(String time) {
		this.time = time;
	}

	public String getSavePath() {
		return savePath;
	}

	public void setSavePath(String savePath) {
		this.savePath = savePath;
	}

	public long getMusicSize() {
		return music_size;
	}

	public void setMusicSize(long music_size) {
		this.music_size = music_size;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

	private String setStr(String string) {
		String result = null;
		try {
			result = new String(string.getBytes("ISO-8859-1"), "UTF-8");
			if (result.contains("?")) {
				result = string;
			}
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return result;
	}

}

Dưới đây là các list nhạc từ device .

 public static ArrayList<Music> getMultiDatas(Context context) {
		ArrayList<Music> musics = new ArrayList<Music>();

		ContentResolver resolver = context.getContentResolver();
		Cursor musicCursor = resolver.query(
				MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
				null);
		int musicColumnIndex;
		Music music;

		if (null != musicCursor && musicCursor.getCount() > 0) {
			for (musicCursor.moveToFirst(); !musicCursor.isAfterLast(); musicCursor
					.moveToNext()) {
				music = new Music();

				music.setId(musicCursor.getInt(musicCursor
						.getColumnIndex("_id")));
				musicColumnIndex = musicCursor
						.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE);
				music.setMusicName(musicCursor.getString(musicColumnIndex));
				musicColumnIndex = musicCursor
						.getColumnIndex(MediaStore.Audio.AudioColumns.DATA);
				music.setSavePath(musicCursor.getString(musicColumnIndex));
				musicColumnIndex = musicCursor
						.getColumnIndex(MediaStore.Audio.AudioColumns.ALBUM);
				music.setAlbumName(musicCursor.getString(musicColumnIndex));
				musicColumnIndex = musicCursor
						.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST);
				music.setSinger(musicCursor.getString(musicColumnIndex));
				musicColumnIndex = musicCursor
						.getColumnIndex(MediaStore.Audio.AudioColumns.DURATION);
				music.setTime(musicCursor.getString(musicColumnIndex));
				musicColumnIndex = musicCursor
						.getColumnIndex(MediaStore.Audio.AudioColumns.ALBUM_KEY);
				music.setAlbumkey(musicCursor.getString(musicColumnIndex));
				musics.add(music);
			}
			musicCursor.close();
		}
		return musics;
	}
Mình sẽ nói chi tiết các mục này ở part 2-2 nhé . Thanks các bạn .

All Rights Reserved