Xử lý nội dung đa phương tiện

Khóa học lập trình Android cơ bản

0.0 (0 đánh giá)
Tạo bởi Kteam Cập nhật lần cuối 17:15 14-08-2020 49.008 lượt xem 3 bình luận
Tác giả/Dịch giả: Kteam
Học nhanh

Danh sách bài học

Xử lý nội dung đa phương tiện

Dẫn nhập

Ở các bài học trước, chúng ta đã cùng nhau tìm hiểu về HỆ THỐNG PHÂN QUYỀN ỨNG DỤNG nói chung và những thay đổi của hệ thống này từ phiên bản Android 6 Marshmallow.

Ở bài học này, chúng ta tiếp tục tìm hiểu về các loại nội dung đa phương tiện trong Android như hình ảnh, nhạc, video,…  và cơ chế ContentProvider mà Android sử dụng để quản lý chúng.


Nội dung

Để đọc hiểu bài này tốt nhất các bạn nên có kiến thức cơ bản về các phần:

Trong bài học này, chúng ta sẽ cùng tìm hiểu các vấn đề:

  • Các định dạng đa phương tiện được hỗ trợ trong Android.
  • Viết một chương trình nhỏ để xem ảnh, chơi nhạc có sẵn trong máy.

Các định dạng đa phương tiện được hỗ trợ trong Android

Chúng ta đều biết rằng các thông tin đa phương tiện như hình ảnh, âm thanh, video,… được lưu trong máy tính dưới dạng các file. Chúng có thể tồn tại dưới nhiều định dạng khác nhau nữa như nhạc thì có OGG, MP3, FLAC, WAV; ảnh thì phổ biến nhất là JPG và PNG, GIF.

Phần nội dung dưới đây được sơ lược khá nhiều để các bạn có cái nhìn tổng quan là chính. Để tìm hiểu sâu hơn, các bạn có thể đọc cuốn Android Studio New Media Fundamentals của tác giả Wallace Jackson.

Android cũng có hỗ trợ rất nhiều định dạng phổ biến nói trên. Các bạn có thể tham khảo bảng sau để biết sơ qua các định dạng và các đời hệ điều hành hỗ trợ (dấu x là có hỗ trợ):

Xử lý nội dung đa phương tiện

Xử lý nội dung đa phương tiện

Cũng khá là dài phải không? Trên thực tế việc sử dụng các API có sẵn của Android để viết ứng dụng chơi nhạc hay xem hình khá là dễ dàng. Chúng ta có thể thử làm với ví dụ ngay sau đây.


Ứng dụng nghe nhạc trong máy

Bước 1: Chúng ta tạo một project mới như thường lệ, lấy tên là MediaExample:

Xử lý nội dung đa phương tiện

Bước 2: Để ứng dụng có thể mở được nhiều định dạng đa phương tiện thì chúng ta cũng phải cân nhắc mức API. Ở đây mình chọn 4 để hỗ trợ cho nhiều.

Xử lý nội dung đa phương tiện

  • Chọn loại Activity Empty và tiếp tục:

Xử lý nội dung đa phương tiện

Bước 3: Để đơn giản hoá toàn bộ kiến thức, chúng ta sẽ thống nhất là viết ứng dụng chơi tất cả những bài nhạc đang có trong máy, hiển thị danh sách bài hát ra ListView. Tạo một model có tên là Song.java:

Xử lý nội dung đa phương tiện

  • Song.java

package com.howkteam.mediaexample;

public class Song {
  private long id;
  private String title;
  private String artist;

  public Song(long songID, String songTitle, String songArtist) {
    id = songID;
    title = songTitle;
    artist = songArtist;
  }

  public long getID() {
    return id;
  }

  public String getTitle() {
    return title;
  }

  public String getArtist() {
    return artist;
  }
}

Bước 4: Để hiển thị dạng danh sách thì hiển nhiên là chúng ta sẽ cần đến Adapter layout cho từng item. Nếu bạn đã quên cách làm thì ngó lại bài LISTVIEWRECYCLERVIEW trước đó nhé. Đầu tiên chúng ta tạo layout:

  • song.xml
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:onClick="songPicked"
              android:orientation="vertical"
              android:padding="5dp">

    <TextView
        android:id="@+id/song_title"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="#2952a3"
        android:textSize="20sp"
        android:textStyle="bold"/>

    <TextView
        android:id="@+id/song_artist"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:textColor="#3366cc"
        android:textSize="18sp"/>

</LinearLayout>

Bước 5: Tạo Adapter cho danh sách bài hát:

  • SongAdapter.java
package com.howkteam.mediaexample;


import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.ArrayList;

public class SongAdapter extends BaseAdapter {
  // Song list và layout
  private ArrayList<Song> songs;
  private LayoutInflater songInf;

  // Constructor
  public SongAdapter(Context c, ArrayList<Song> theSongs) {
    songs = theSongs;
    songInf = LayoutInflater.from(c);
  }

  @Override
  public int getCount() {
    return songs.size();
  }

  @Override
  public Object getItem(int position) {
    return null;
  }

  @Override
  public long getItemId(int position) {
    return 0;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    // Ánh xạ đến layout mỗi bài
    LinearLayout songLayout = (LinearLayout) songInf.inflate(R.layout.song, parent, false);

    TextView songView = (TextView) songLayout.findViewById(R.id.song_title);
    TextView artistView = (TextView) songLayout.findViewById(R.id.song_artist);

    // Lấy bài hát hiện
    Song currentSong = songs.get(position);

    // Lấy tên tiêu đề và tác
    songView.setText(currentSong.getTitle());
    artistView.setText(currentSong.getArtist());

    // Cài đặt tag cho mỗi bài là vị trí của mỗi
    songLayout.setTag(position);
    return songLayout;
  }
}

Bước 6: Để có thể chơi nhạc, chúng ta cần một “Bộ điều khiển” (controller) để phát nhạc. Việc này có thể dễ dàng thực hiện bằng cách tạo lớp kế thừa từ lớp MediaController.

  • MusicController.java
package com.howkteam.mediaexample;

import android.content.Context;
import android.widget.MediaController;

public class MusicController extends MediaController {
  public MusicController(Context context) {
    super(context);
  }
}

Bước 7: Hẳn các bạn cũng muốn là sau khi đóng ứng dụng, chương trình phát nhạc vẫn chạy như bình thường đúng không? Rất nhiều ứng dụng nghe nhạc khác cũng làm tương tự, để khi tắt màn hình thì nhạc vẫn chạy, chúng ta có thể làm điều đó bằng cách tạo ra một Service như sau:

Xử lý nội dung đa phương tiện

  • MusicService.java

package com.howkteam.mediaexample;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ContentUris;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.ArrayList;
import java.util.Random;

public class MusicService extends Service implements
        MediaPlayer.OnPreparedListener,
        MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {

  private MediaPlayer player;
  private ArrayList<Song> songs;
  private int songPos;
  private final IBinder musicBind = new MusicBinder();
  private String songTitle = "";
  private static final int NOTIFY_ID = 1;
  private boolean shuffle = false;
  private Random rand;

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

  @Override
  public void onCreate() {
    super.onCreate();
    songPos = 0;
    // Khởi tạo một bộ phát đa phương tiện mới.
    player = new MediaPlayer();
  }

  public void initMusicPlayer() {
    player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
    player.setAudioStreamType(AudioManager.STREAM_MUSIC);
    player.setOnPreparedListener(this);
    player.setOnErrorListener(this);
    player.setOnCompletionListener(this);
  }

  public void setList(ArrayList<Song> theSongs) {
    songs = theSongs;
  }

  public class MusicBinder extends Binder {
    MusicService getService() {
      return MusicService.this;
    }
  }

  @Override
  public void onCompletion(MediaPlayer mp) {
    if (player.getCurrentPosition() > 0) {
      mp.reset();
      playNext();
    }
  }

  @Override
  public boolean onError(MediaPlayer mp, int what, int extra) {
    mp.reset();
    return false;
  }

  @Override
  public void onPrepared(MediaPlayer mp) {
    mp.start();
    Intent notificationIntent = new Intent(this, MainActivity.class);
    notificationIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    PendingIntent pendingIntent = PendingIntent.getActivity(
            this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT
    );

    Notification.Builder nBuilder = new Notification.Builder(this);
    nBuilder.setContentIntent(pendingIntent)
            .setTicker(songTitle)
            .setSmallIcon(R.drawable.play)
            .setOngoing(true)
            .setContentTitle("Playing")
            .setContentText(songTitle);
    Notification notif = nBuilder.getNotification();
    startForeground(NOTIFY_ID, notif);
  }

  @Override
  public boolean onUnbind(Intent intent) {
    player.stop();
    player.release();
    return false;
  }

  public void playSong() {
    player.reset();
    Song playSong = songs.get(songPos);
    songTitle = playSong.getTitle();
    Long currentSong = playSong.getID();
    Uri trackUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, currentSong);
    try {
      player.setDataSource(getApplicationContext(), trackUri);
    } catch (Exception e) {
      Log.e("MUSIC SERVICE", "Error starting data source", e);
    }
    player.prepareAsync();
  }

  public void setSong(int songIndex) {
    songPos = songIndex;
  }

  public int getSongPos() {
    return player.getCurrentPosition();
  }

  public int getDur() {
    return player.getDuration();
  }

  public boolean isPlaying() {
    return player.isPlaying();
  }

  public void pausePlayer() {
    player.pause();
  }

  public void seek(int pos) {
    player.seekTo(pos);
  }

  public void go() {
    player.start();
  }

  public void playPrev() {
    songPos--;
    if (songPos < 0) songPos = songs.size() - 1;
    playSong();
  }

  public void playNext() {
    if (shuffle) {
      int newSongPos = songPos;
      while (newSongPos == songPos) {
        newSongPos = rand.nextInt(songs.size());
      }
      songPos = newSongPos;
    } else {
      songPos++;
      if (songPos >= songs.size()) songPos = 0;
    }
    playSong();
  }

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

  public void setShuffle() {
    if (shuffle) shuffle = false;
    else shuffle = true;
  }
}

Bước 8: Chỉnh sửa file activity_main.xml để thêm ListView vào:

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:paddingBottom="@dimen/activity_vertical_margin"
              android:paddingLeft="@dimen/activity_horizontal_margin"
              android:paddingRight="@dimen/activity_horizontal_margin"
              android:paddingTop="@dimen/activity_vertical_margin"
              android:orientation="vertical"
              android:background="#ffebe5"
              tools:context=".MainActivity">

    <ListView
        android:id="@+id/song_list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
    </ListView>

</LinearLayout>

Và cuối cùng, chỉnh sửa file MainActivity.java để gắn kết các Service, Controller với nhau:

  • MainActivity.java
package com.howkteam.mediaexample;

import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
import android.widget.MediaController;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class MainActivity extends AppCompatActivity implements MediaController.MediaPlayerControl {

  private ArrayList<Song> songList;
  private ListView songView;
  private MusicService musicService;
  private Intent playIntent;
  private boolean musicBound = false;
  private MusicController controller;
  private boolean paused = false;
  private boolean playbackPaused = false;

  @Override
  protected void onStart() {
    super.onStart();
    if (playIntent == null) {
      playIntent = new Intent(this, MusicService.class);
      bindService(playIntent, musicConnection, BIND_AUTO_CREATE);
      startService(playIntent);
    }
  }

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

    // Retrieve list menu
    songView = (ListView) findViewById(R.id.song_list);
    // Instantiate song list
    songList = new ArrayList<Song>();
    // Get songs from device
    getSongList();
    // Sort alphabetically by title
    Collections.sort(songList, new Comparator<Song>() {
      @Override
      public int compare(Song lhs, Song rhs) {
        return lhs.getTitle().compareTo(rhs.getTitle());
      }
    });
    // Create and set adapter
    SongAdapter songAdapter = new SongAdapter(this, songList);
    songView.setAdapter(songAdapter);
    setController();
  }

  // Method to retrieve song infos from device
  public void getSongList() {
    // Query external audio resources
    ContentResolver musicResolver = getContentResolver();
    Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);
    // Iterate over results if valid
    if (musicCursor != null && musicCursor.moveToFirst()) {
      // Get columns
      int titleColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
      int idColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media._ID);
      int artistColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);

      do {
        Long thisId = musicCursor.getLong(idColumn);
        String thisTitle = musicCursor.getString(titleColumn);
        String thisArtist = musicCursor.getString(artistColumn);
        songList.add(new Song(thisId, thisTitle, thisArtist));
      }
      while (musicCursor.moveToNext());
    }
  }

  // Connect with the service
  private ServiceConnection musicConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      MusicService.MusicBinder binder = (MusicService.MusicBinder) service;
      musicService = binder.getService();
      musicService.setList(songList);
      musicBound = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
      musicBound = false;
    }
  };

  public void songPicked(View v) {
    musicService.setSong(Integer.parseInt(v.getTag().toString()));
    musicService.playSong();
    if (playbackPaused) {
      setController();
      playbackPaused = false;
    }
    controller.show(0);
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater menuInflater = getMenuInflater();
    menuInflater.inflate(R.menu.menu_main, menu);
    return true;
    //return super.onCreateOptionsMenu(menu);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case R.id.action_shuffle:
        musicService.setShuffle();
        break;
      case R.id.action_end:
        stopService(playIntent);
        musicService = null;
        System.exit(0);
        break;
    }
    return super.onOptionsItemSelected(item);
  }

  @Override
  protected void onDestroy() {
    stopService(playIntent);
    musicService = null;
    super.onDestroy();
  }

  private void setController() {
    controller = new MusicController(this);
    controller.setPrevNextListeners(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        playNext();
      }
    }, new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        playPrev();
      }
    });
    controller.setMediaPlayer(this);
    controller.setAnchorView(findViewById(R.id.song_list));
    controller.setEnabled(true);
  }

  private void playNext() {
    musicService.playNext();
    if (playbackPaused) {
      setController();
      playbackPaused = false;
    }
    controller.show(0);
  }

  private void playPrev() {
    musicService.playPrev();
    if (playbackPaused) {
      setController();
      playbackPaused = false;
    }
    controller.show(0);
  }

  @Override
  public boolean canPause() {
    return true;
  }

  @Override
  public boolean canSeekBackward() {
    return true;
  }

  @Override
  public boolean canSeekForward() {
    return true;
  }

  @Override
  protected void onPause() {
    super.onPause();
    paused = true;
  }

  @Override
  protected void onResume() {
    super.onResume();
    if (paused) {
      setController();
      paused = false;
    }
  }

  @Override
  protected void onStop() {
    controller.hide();
    super.onStop();
  }

  @Override
  public void start() {
    musicService.go();
  }

  @Override
  public void pause() {
    playbackPaused = true;
    musicService.pausePlayer();
  }

  @Override
  public int getDuration() {
    if (musicService != null && musicBound && musicService.isPlaying()) {
      return musicService.getDur();
    }
    return 0;
  }

  @Override
  public int getCurrentPosition() {
    return 0;
  }

  @Override
  public void seekTo(int pos) {
    musicService.seek(pos);
  }

  @Override
  public boolean isPlaying() {
    if (musicService != null && musicBound) {
      return musicService.isPlaying();
    } else {
      return false;
    }
  }

  @Override
  public int getBufferPercentage() {
    return 0;
  }

  @Override
  public int getAudioSessionId() {
    return 0;
  }

}

Bước 9: Do app sử dụng menu và một số thành phần drawable ngoài, các bạn có thể tìm thấy trong thư mục /res/drawable ở file project đính kèm. Ngoài ra các bạn cần tạo thêm menu cho nó bằng cách chuột phải vào thư mục /res > New > Android resource directory

Xử lý nội dung đa phương tiện

  • Tạo folder với thông số như hình:

Xử lý nội dung đa phương tiện

Và tạo bên trong một file menu_main.xml với nội dung như sau:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      tools:context=".MainActivity">
    <item
        android:id="@+id/action_shuffle"
        android:icon="@drawable/rand"
        android:orderInCategory="1"
        android:title="Shuffle"
        app:showAsAction="always"/>

    <item
        android:id="@+id/action_end"
        android:icon="@drawable/end"
        android:orderInCategory="2"
        android:title="End"
        app:showAsAction="always"/>
</menu>

Bước 10: Đừng quên khai báo Permission trong file AndroidManifest, nếu không ứng dụng sẽ bị crash!

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.howkteam.mediaexample">

    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <service android:name="com.howkteam.mediaexample.MusicService"/>
    </application>

</manifest>

Thêm nữa, chúng ta sẽ chỉnh file build.gradle cho Target dưới 23, ở đây mình chọn 22, để tạm thời tránh xung đột với Runtime Permission trên Android 6 trở lên (phần targetSdkVersion):

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.0"
    defaultConfig {
        applicationId "com.howkteam.mediaexample"
        minSdkVersion 14
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.0.0'
    testCompile 'junit:junit:4.12'
}

Nào, chạy app, và chúng ta có được:

Xử lý nội dung đa phương tiện

Chọn bài và nhấn nút Play nhé ^_^


Kết luận

Qua bài này chúng ta đã nắm được Các định dạng đa phương tiện cơ bản trong Android, và sử dụng Service để làm một ứng dụng chơi nhạc nho nhỏ, thuận tiện.

Bài sau chúng ta sẽ tìm hiểu về CÁCH LƯU TRỮ DỮ LIỆU DẠNG KEY - VALUE TRONG ANDROID.

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.


Tải xuống

Tài liệu

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học Xử lý nội dung đa phương tiện dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên likeshare để ủng hộ Kteam và tác giả nhé!


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng.

Nội dung bài viết

Tác giả/Dịch giả

Khóa học

Khóa học lập trình Android cơ bản

Serial tutorial hướng dẫn lập trình Android cơ bản

Đánh giá

Bình luận

Để bình luận, bạn cần đăng nhập bằng tài khoản Howkteam.

Đăng nhập
Adudi đã bình luận 16:32 05-04-2020

Project hoàn chỉnh cho bạn nào cần: Link Project

beehomiez đã bình luận 03:41 08-01-2017
e k thấy file project đính kèm để lấy các file trong drawable a ơi
Không có video.