Android 学习笔记多媒体技术之 AsyncTask+实现音频播放...

PS:今天搞了一下如何实现音频播放...结果被坑了,看书上写的代码是挺简单的,但是有个函数就是死活没看懂,这真是受不了...最后才弄明白,原来是一个实现异步任务的一个类...这个类使用java.util.concurrent这个高效框架来管理线程以及任务的执行...可以解决匿名线程存在的问题...

学习内容:

1.理解AsyncTask

2.如何实现音频播放...

1.AsyncTask

  AsyncTask的特点就是实现一个任务在另一个线程内执行,而不是在主函数中进行执行,这样就不会导致主线程的任务形成阻塞..从而可以一直执行下去,耗时的操作可以在另一个异步的线程中执行...

  AsyncTask定义了三种泛型类型...Params,Progress,Result...这三种类型...

i.Params表示的是启动任务执行的参数...

ii.Progress表示当前这个线程执行任务的百分比...

iii.Result表示的是线程执行完毕后的结果,然后返回给主线程...

 使用AsyncTask还需要覆盖其中的方法,必须要进行覆盖的方法就是protected String doInBackground(Integer... params) {}这个方法,这个方法表示的是我们在后台执行另外一个线程...还有一个常需要覆盖的方法就是protected void onPostExecute(String result){}...这个表示将执行完毕的结果返回给主线程...

 这个类的调用时必须要在主线程中完成调用的...使用new updateseekbar.execute(uri1,uri2,uri3);来完成调用,updateseekbar是我自己定义的类...

 private class updateseekbar extends AsyncTask<Integer, Integer, String>{

        protected void onPostExecute(String result){}
        protected void onProgressUpdate(Integer...progress){
            seekbar.setProgress(progress[0]);
        }
        @Override
        protected String doInBackground(Integer... params) {
            // TODO Auto-generated method stub
            while(MainActivity.this.playflag){
                try {
                      Thread.sleep(params[0]);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                this.publishProgress(MainActivity.this.media.getCurrentPosition());
            }
            return null;
        }      
    }

  完成了调用,那么就说一下它的执行顺序,首先是执行onPreExecute()函数,这个函数可有可无,然后就是执行doInbackground()函数,来完成耗时的操作...这里的params[0]表示的是第一个参数uri1,并且我们可以调用publishProgress()来更新进度...然后是onProgressUpdate()函数的执行,这个函数的执行是在doInbackground()方法没有执行完时才执行的,执行的时间是不定的,用这个函数里可以加入一个进度条或者是文本显示来告知用户程序执行的进度...最后当doInbackground()函数彻底执行完毕之后,调用onPostExecute()函数来返回程序执行完毕后的结果...

  注意:使用这个类是要遵守一些规则的...

i.任务实例的创建必须要在UI线程里执行,说白了就是实例化这个对象时必须要在UI中完成...

ii.必须在UI线程里调用execute(Params...)方法...

iii.我们不能手动的去调用上面的那四种方法...

iv.这个任务只能执行一次,否则会抛出异常信息...

 再简单阐述一下这个类的工作原理...我先粘贴一下其中如何实现的源代码...

//当在UI线程中调用execute()方法以后,下面这个方法将被调用... 
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);//
    }
    /**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     * 
     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
     * allow multiple tasks to run in parallel on a pool of threads managed by
     * AsyncTask, however you can also use your own {@link Executor} for custom
     * behavior.
     * 
     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
     * a thread pool is generally <em>not</em> what one wants, because the order
     * of their operation is not defined.  For example, if these tasks are used
     * to modify any state in common (such as writing a file due to a button click),
     * there are no guarantees on the order of the modifications.
     * Without careful work it is possible in rare cases for the newer version
     * of the data to be over-written by an older one, leading to obscure data
     * loss and stability issues.  Such changes are best
     * executed in serial; to guarantee such work is serialized regardless of
     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
     *
     * <p>This method must be invoked on the UI thread.
     *
     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
     *              convenient process-wide thread pool for tasks that are loosely coupled.
     * @param params The parameters of the task.
     *
     * @return This instance of AsyncTask.
     *
     * @throws IllegalStateException If {@link #getStatus()} returns either
     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
     */
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            }
        }

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }
    /**
     * Convenience version of {@link #execute(Object...)} for use with
     * a simple Runnable object.
     */
    public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
    }

  这就是如何实现的源代码,那么这个到底是如何实现的呢,就是当我们在UI启动execute()方法时,会直接调用最上面的那个方法,在这个方法内部自行调用下一个方法...这个方法内部会自行调用onPreExecute()方法...然后在内部调用最下面的这个方法,这个方法来开启另外一个异步线程来完成工作...最后返回...然后另一个异步线程就会执行耗时的工作,最后把最终工作完成的结果进行返回...这就是使用AsyncTask类的使用目的...

  2.实现音乐播放...

  Android多媒体技术应用的就非常的广泛了...我才学习到音乐播放...简单的介绍一下如何实现音乐的播放...这个音乐的播放就使用到了上面的类...目的是实现音乐播放的时候与一个进度条实现同步...当拖拉进度条的时候,音乐的播放也会完成同步...

<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:orientation="vertical"
    tools:context=".MainActivity" >
    <TextView 
        android:id="@+id/info"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        android:text="等待播放"/>
    <LinearLayout 
        android:orientation="horizontal"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content">
        <ImageButton 
            android:id="@+id/play"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/play"/>
        <ImageButton 
            android:id="@+id/pause"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/pause"/>
        <ImageButton 
            android:id="@+id/stop"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:src="@drawable/stop"/>
    </LinearLayout>
    <SeekBar 
        android:id="@+id/seekbar"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"/>
</LinearLayout>

  布局文件相对的也非常的简单...就是三个图片按钮和一个进度条与一个文本显示控件...每一个音乐播放器都是用图片按钮和进度条组成的...我这里也就弄了个简单的...都看得懂就不解释了...

  在触发图片按钮的时候我们进行监听...获取监听后我们就可以在内部执行一些操作...

package com.example.exam7_2;

import java.io.IOException;

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.SeekBar;
import android.widget.TextView;

public class MainActivity extends Activity implements View.OnClickListener, SeekBar.OnSeekBarChangeListener{
    private MediaPlayer media;
    private boolean playflag=true;
    private boolean pauseflag=false;
    private SeekBar seekbar=null;
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv=(TextView) findViewById(R.id.info);
        findViewById(R.id.play).setOnClickListener(this);
        findViewById(R.id.pause).setOnClickListener(this);
        findViewById(R.id.stop).setOnClickListener(this);
        seekbar=(SeekBar) findViewById(R.id.seekbar);
        
    }
    private class updateseekbar extends AsyncTask<Integer, Integer, String>{

        protected void onPostExecute(String result){}
        protected void onProgressUpdate(Integer...progress){
            seekbar.setProgress(progress[0]);
        }
        @Override
        protected String doInBackground(Integer... params) {
            // TODO Auto-generated method stub
            while(MainActivity.this.playflag){
                try {
                    Thread.sleep(params[0]);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                this.publishProgress(MainActivity.this.media.getCurrentPosition());
            }
            return null;
        }
        
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub
        switch(v.getId()){
        case R.id.play:
        {
            media=MediaPlayer.create(MainActivity.this, R.raw.wind);
            MainActivity.this.media.setOnCompletionListener(new OnCompletionListener() {
                
                @Override
                public void onCompletion(MediaPlayer arg0) {
                    // TODO Auto-generated method stub
                    playflag=false;
                    media.release();
                }
            });
            seekbar.setMax(MainActivity.this.media.getDuration());
            updateseekbar update=new updateseekbar();
            //执行者execute
            update.execute(1000);
            seekbar.setOnSeekBarChangeListener(this);
            if(MainActivity.this.media!=null){
                MainActivity.this.media.stop();
            }
            try {
                MainActivity.this.media.prepare();
                MainActivity.this.media.start();
                tv.setText("正在播放文件...");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                tv.setText("文件出现异常...");
                e.printStackTrace();
            } 
            break;
        }
        case R.id.pause:
            if(media!=null){
                if(pauseflag){
                    media.start();
                    pauseflag=false;
                }else{
                    media.pause();
                    pauseflag=true;
                }
            }
            break;
        case R.id.stop:
            if(media!=null){
                media.stop();
                tv.setText("停止播放文件...");
            }
            break;
        }
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress,
            boolean fromUser) {
        // TODO Auto-generated method stub
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        // TODO Auto-generated method stub
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        // TODO Auto-generated method stub
        media.seekTo(seekbar.getProgress());
    }
   
}

  非常简单的代码...在这里我就使用了一个类来继承AsyncTask...然后在这个类的内部来完成一些操作...有了上面的基础这个代码就变得很容易懂了...就是另外开了一个异步线程来控制进度条与主线程中的音乐播放实现同步...只有实现了这两者的同步才能够完成一个简单音乐播放器的实现...