作者 : 万境绝尘 (octopus_truth@163.com)

转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38895143

一. 相关模块解析

1. 播放载体 SurfaceView 简介

(1) SurfaceView 与 Surface

SurfaceView 与 Surface 简介 : SurfaceView 中嵌入了一个 Surface, SurfaceView 可以操控 Surface 的 位置, 大小尺寸等;

-- SurfaceView 绘制图像可见性 : Surface 相当与一块画板, 上面绘制了一些东西, 但是不是所有的内容都可见, SurfaceView 相当与 Surface 的窗口, 只有在 SurfaceView 窗口区域内的才是可见的, 其它没有在窗口正下方的内容不可见;

-- Surface 与 SurfaceView 声明周期对应 : SurfaceView 可见的时候 Surface 被创建, SurfaceView 不可见时 Surface 销毁, Surface 创建销毁时机回调方法 SurfaceCreated(SurfaceHolder) 和 SurfaceDestoryed(SurfaceHolder) 方法, 这两个回调方法是 SurfaceHolder.Callback 中设定;

-- Surface 控制 : SurfaceView 通过 SurfaceHolder 控制 Surface;

(2) SurfaceHolder 简介

Surfaceholder 简介 : SurfaceHolder 是 Surface 的控制器, 用于控制 SurfaceView 绘图, 处理画布上的 动画 渲染效果 尺寸等;

-- 添加 Callback 回调接口abstract void addCallback(SurfaceHolder.Callback callback), 添加一个 SurfaceHolder.Callback 接口对象, 监听 Surface 的开始结束绘制大小改变事件;

-- 锁定画布 :  abstract Canvas lockCanvas(), 锁定画布, 可以获得 Canvas 对象, 之后就可以在 Canvas 上绘图了;

(3) SurfaceHolder.Callback 简介

SurfaceHolder.Callback 接口 :

-- Surface 绘图边界 : 所有的绘图操作, 在 Surface 创建后操作, 在 Surface 销毁之前结束;

-- SurfaceHolder.Callback 接口对应 Surface 边界 : SurfaceCreated() 方法在 Surface 创建时回调, SurfaceDestroyed() 方法在 Surface 销毁前回调;

SurfaceHolder.Callback 接口中的方法简介 :

-- SurfaceChanged() : Surface 大小改变前回调;

-- surfaceCreated() : surface 创建时回调;

-- surfaceDestroyed() : surface 销毁时回调;

2. SurfaceView 使用流程

(1) 在布局文件中使用 SurfaceView 组件

布局文件 : 在布局文件中使用 <SurfaceView /> 组件;

-- 示例 :

<SurfaceView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"/>

(2) 设置 SurfaceHolder.Callback 回调接口

创建回调接口子类 : Activity 继承 SurfaceHolder.Callback 接口, 实现接口的三个方法 SurfaceCreated(), SurfaceChanged(), SurfaceDestroyed() 方法;

-- 示例 : 一般情况下直接设置 Activity 实现该接口, 为 SurfaceHolder 设置接口的时候直接将 Activity 设置进去;

public class HomeActivity extends Activity implements SurfaceHolder.Callback {

	/**
	 * 在 Surface 大小发生改变的时候回调
	 * 实现的 SurfaceHolder.Callback 接口方法
	 */
	@Override
	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
		System.out.println("SurfaceHolder.Callback.surfaceChanged : Surface 大小发生改变");
	}

	/**
	 * 在 Surface 创建的时候回调, 一般在该方法中开始绘图
	 * 实现的 SurfaceHolder.Callback 接口方法
	 */
	@Override
	public void surfaceCreated(SurfaceHolder arg0) {
		System.out.println("SurfaceHolder.Callback.surfaceCreated : Surface 开始创建");
	}

	/**
	 * 在 Surface 销毁之前回调, 在该方法中停止渲染线程, 释放相关资源
	 * 实现的 SurfaceHolder.Callback 接口方法
	 */
	@Override
	public void surfaceDestroyed(SurfaceHolder arg0) {
		System.out.println("SurfaceHolder.Callback.surfaceDestroyed : Surface 销毁");
	}

}

(3) 获取 SurfaceHolder 对象 并添加回调接口子类

获取 SurfaceHolder 流程 :

-- a. 获取 SurfaceView : 使用 findViewById() 方法从布局文件中获取 SurfaceView 组件对象;

surface_view = (SurfaceView) findViewById(R.id.surface_view);

-- b. 从 SurfaceView 组件对象中获取 SurfaceHolder : 调用 SurfaceView 对象的 getHolder() 即可获取 SurfaceHolder 对象;

surface_holder = surface_view.getHolder();						/* 根据 SurfaceView 组件, 获取 SurfaceHolder 对象 */

-- c. 为 SurfaceHolder 设置回调接口 : 调用 Surfaceholder 的 setCallback() 方法即可, 用于监听 Surface 的创建 改变 和 销毁;

surface_holder.addCallback(this); 								/* 为 SurfaceHolder 设置回调函数, 即 SurfaceHolder.Callback 子类对象 */

SurfaceHolder 其它设置 :

-- 设置 SurfaceHolder 画面比例 : 调用 SurfaceHolder 的 setFixedSize() 方法;

surface_holder.setFixedSize(160, 128);							/* 设置视频大小比例 */

-- 设置 SurfaceHolder 数据类型 : 调用 SurfaceHolder 的 setType() 方法;

surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);/* 设置视频类型 */

总体代码示例 :

/**
	 * 初始化相关数据变量
	 */
	private void initData() {

		/* 获取并设置 SurfaceHolder 对象 */
		surface_holder = surface_view.getHolder();						/* 根据 SurfaceView 组件, 获取 SurfaceHolder 对象 */
		surface_holder.addCallback(this); 								/* 为 SurfaceHolder 设置回调函数, 即 SurfaceHolder.Callback 子类对象 */
		surface_holder.setFixedSize(160, 128);							/* 设置视频大小比例 */
		surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);/* 设置视频类型 */

	}

3. AutoCompleteTextView 组件

组件特性 : 可以设置一个适配器, 适配器中维护一组字符串, 当输入一部份字符后, 能 自动将符合条件的提示出来;

-- 示例 :

(1) 在布局文件中定义组件

布局文件中 : 使用 <AutoCompleteTextView /> 标签添加组件;

-- 设置后面省略 : android:ellipsize="end" , 由于链接太长, 不能显示全部, 自动将后面的部分省略;

-- 设置提示 : android:completionHint="选择下载的视频地址", 设置弹出的提示栏最后一栏的提示;

-- 设置单行显示 : android:singleLine="true";

-- 设置输入几个字符之后开始提示 : android:completionThreshold="1", 这里设置的是输入第一个字符之后就开始自动提示;

-- 示例 :

<AutoCompleteTextView
        android:id="@+id/url"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:ellipsize="end"
        android:completionThreshold="1"
        android:text="http://daily3gp.com/vids/747.3gp"
        android:completionHint="选择下载的视频地址"/>

(2) 创建适配器

创建适配器方法 :

android.widget.ArrayAdapter.ArrayAdapter<String>(Context context, int textViewResourceId, String[] objects)

-- Context context 参数 : 上下文对象;

-- int textViewResourceId 参数 : 显示列表的组件的布局;

-- String[] objects 参数 : 字符串数组对象;

代码示例 :

/* 设置一个列表适配器 */
		String[] urls = {
				"http://daily3gp.com/vids/747.3gp",
				"http://daily3gp.com/vids/Funny%20women%20cannot%20understand.3gp",
				"http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/0300020100540024BC9E5C08BD8A98D8200E2B-7950-B9A5-8669-DC283BDCC077?K=3a58dc2cdcc532df261dddec&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JjALshTOi%2FmzqjtJTFS4xCHottelMPGJ%2F5qdDnH0JmYfdKrGgQrUfZPPro%2BPbq5dkgxpgDFG1FAc3Qs1SbRTn3",
				"http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/030002040053FFB59E433100422C39BAFA46CC-4DED-E928-87B8-91706CDB5FF2?K=645d8478a3aa59052411eb8a&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JmALshS57J6zvYspmzTf5CFv0bcFEFGZmA3aHjbDNnYfQ33BwQqkeqMfro%2BYLr5aRSw5AGFW1Ed7uhtlSbRTn3"

		};
		/* 创建数组适配器 */
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_dropdown_item_1line, urls);
		/* 将适配器设置给 AutoCompleteTextView 组件对象 */
		url.setAdapter(adapter);

4. MediaPlayer 播放

(1) 设置音量 和 播放载体

设置音量 :

mediaPlayer.setAudioStreamType(2); 			/* 设置播放音量 */

设置播放载体 : 调用 setDisplay() 方法, 传入 SurfaceHolder 对象;

mediaPlayer.setDisplay(surface_holder); 	/* 设置播放载体 */	

(2) 设置各种监听器

设置错误监听器 : 如果出现错误, 会回调该监听器中的方法, 并提供错误码;

/* 设置 MediaPlayer 错误监听器, 如果出现错误就会回调该方法打印错误代码 */
			mediaPlayer.setOnErrorListener(new OnErrorListener() {
				@Override
				public boolean onError(MediaPlayer arg0, int what, int extra) {
					System.out.println("MediaPlayer 出现错误 what : " + what + " , extra : " + extra);
					return false;
				}
			});

设置缓冲进度监听器 : 缓冲有进展后, 回调该监听器中的方法, 传入缓冲的数据百分比;

/* 设置缓冲进度更新监听器 */
			mediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
				@Override
				public void onBufferingUpdate(MediaPlayer arg0, int percent) {
					/* 打印缓冲的百分比, 如果缓冲 */
					System.out.println("缓冲了的百分比 : " + percent + " %");
				}
			});

设置播放完毕监听器 : 播放完毕后会回调该监听器中的方法;

			/* 设置播放完毕监听器 */
			mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
				@Override
				public void onCompletion(MediaPlayer arg0) {
					System.out.println("播放完毕了");
					status.setText("播放完毕");
				}
			});

设置准备完毕回调监听器 : 准备完毕后会回调该方法;

			/* 设置准备完毕监听器 */
			mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
				@Override
				public void onPrepared(MediaPlayer arg0) {
					System.out.println("准备完毕");
					/* 设置播放状态 */
					status.setText("播放中");
				}
			});

二. 代码示例

1. 布局文件代码

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="20dp"
    tools:context="cn.org.octopus.videodemo.HomeActivity"
    tools:ignore="MergeRootFrame"
    android:orientation="vertical">

    <AutoCompleteTextView
        android:id="@+id/url"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:ellipsize="end"
        android:completionThreshold="1"
        android:text="http://daily3gp.com/vids/747.3gp"
        android:completionHint="选择下载的视频地址"/>

    <SurfaceView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"/>

    <TextView
        android:id="@+id/status"
        android:layout_width="match_parent"
        android:gravity="center"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:textSize="20dp"
        android:text="状态"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <Button
            android:id="@+id/play"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClick"
            android:text="播放"/>

        <Button
            android:id="@+id/pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClick"
            android:text="暂停"/>

        <Button
            android:id="@+id/reset"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClick"
            android:text="重放"/>

        <Button
            android:id="@+id/stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="onClick"
            android:text="停止"/>

    </LinearLayout>

</LinearLayout>

2. AndroidManifest 代码

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.org.octopus.videodemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="17"
        android:targetSdkVersion="19" />
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="cn.org.octopus.videodemo.HomeActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

3. Activity 代码

package cn.org.octopus.videodemo;

import java.io.IOException;

import android.R.anim;
import android.app.Activity;
import android.content.DialogInterface.OnClickListener;
import android.graphics.PixelFormat;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.TextView;
/**
 *
 *
 * SurfaceHolder
 * 		1. 简介 : 是 Surface 的控制器, 用于控制 SurfaceView 绘图, 处理画布上的动画, 渲染效果, 大小等;
 * 		2. 常用方法 :
 * 			-- abstract void addCallback(SurfaceHolder.Callback callback) : 添加一个 SurfaceHolder.Callback 接口对象, 监听 Surface 的开始结束绘制大小改变事件;
 * 			-- abstract Canvas lockCanvas() : 锁定画布, 可以获得 Canvas 对象, 之后就可以在 Canvas 上绘图了;
 *
 * SurfaceHolder.Callback接口 :
 * 		1. Surface 绘图边界 : 所有的绘图工作都在 Surface 创建之后才能进行, 在 Surface 销毁之前结束;
 * 		2. Callback 接口对应的 Surface 边界 : surfaceCreated() 方法在开始绘制时回调, surfaceDestroyed() 在 Surface 销毁前回调;
 * 		3. 该接口中的方法 :
 * 			-- surfaceChanged() : 在 Surface 大小改变时回调;
 * 			-- surfaceCreated() : 在 Surface 创建时回调;
 * 			-- surfaceDestroyed() : 在 Surface 销毁时回调;
 *
 * @author octopus
 *
 */
public class HomeActivity extends Activity implements SurfaceHolder.Callback {

	private AutoCompleteTextView url;							/* 地址输入框, 带自动提示功能 */
	private SurfaceView surface_view;							/* 播放视频载体 */
	private TextView status;									/* 显示播放状态 */
	private Button play;										/* 播放按钮 */
	private Button pause;										/* 咱提供按钮 */
	private Button reset;										/* 重放按钮 */
	private Button stop;										/* 停止按钮 */

	private MediaPlayer mediaPlayer;							/* 播放器 */
	private SurfaceHolder surface_holder; 						/* Surface 控制器 */

	private boolean isStartPlaying;		/* 是否开始了播放 */

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

		initViews();
		initData();
	}

	/**
	 * 初始化成员变量中的组件变量
	 */
	private void initViews() {
		/* 通过 findViewById 获取相关方法 */
		url = (AutoCompleteTextView) findViewById(R.id.url);
		surface_view = (SurfaceView) findViewById(R.id.surface_view);
		status = (TextView) findViewById(R.id.status);
		play = (Button) findViewById(R.id.play);
		pause = (Button) findViewById(R.id.pause);
		reset = (Button) findViewById(R.id.reset);
		stop = (Button) findViewById(R.id.stop);

		/* 设置一个列表适配器 */
		String[] urls = {
				"http://daily3gp.com/vids/747.3gp",
				"http://daily3gp.com/vids/Funny%20women%20cannot%20understand.3gp",
				"http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/0300020100540024BC9E5C08BD8A98D8200E2B-7950-B9A5-8669-DC283BDCC077?K=3a58dc2cdcc532df261dddec&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JjALshTOi%2FmzqjtJTFS4xCHottelMPGJ%2F5qdDnH0JmYfdKrGgQrUfZPPro%2BPbq5dkgxpgDFG1FAc3Qs1SbRTn3",
				"http://k.youku.com/player/getFlvPath/sid/9409280845322127f6c57_00/st/flv/fileid/030002040053FFB59E433100422C39BAFA46CC-4DED-E928-87B8-91706CDB5FF2?K=645d8478a3aa59052411eb8a&ctype=12&ev=1&oip=1931322792&token=5696&ep=eyaUE0uFVsYE4CDdij8bYHrkJ3IIXP4J9h%2BFg9JmALshS57J6zvYspmzTf5CFv0bcFEFGZmA3aHjbDNnYfQ33BwQqkeqMfro%2BYLr5aRSw5AGFW1Ed7uhtlSbRTn3"

		};
		/* 创建数组适配器 */
		ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
				android.R.layout.simple_dropdown_item_1line, urls);
		/* 将适配器设置给 AutoCompleteTextView 组件对象 */
		url.setAdapter(adapter);

		/* 使窗口支持透明度, 把当前 Activity 窗口设置成透明, 设置了该选项就可以使用 setAlpha 等函数设置窗口透明度 */
		getWindow().setFormat(PixelFormat.TRANSPARENT);
	}

	/**
	 * 初始化相关数据变量
	 */
	private void initData() {

		/* 获取并设置 SurfaceHolder 对象 */
		surface_holder = surface_view.getHolder();						/* 根据 SurfaceView 组件, 获取 SurfaceHolder 对象 */
		surface_holder.addCallback(this); 								/* 为 SurfaceHolder 设置回调函数, 即 SurfaceHolder.Callback 子类对象 */
		surface_holder.setFixedSize(160, 128);							/* 设置视频大小比例 */
		surface_holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);/* 设置视频类型 */

	}

	/**
	 * 设置点击事件
	 * @param view
	 */
	public void onClick(View view) {
		int id = view.getId();

		switch (id) {
		case R.id.play:
			/* 播放视频直接从 AutoCompleteTextView 中获取字符串, 播放该 url 代表的网络视频 */
			playVideo(url.getText().toString());
			break;

		case R.id.pause:
			if(mediaPlayer != null){
				mediaPlayer.pause();
				status.setText("暂停");
			}
			break;

		case R.id.reset:
			if(mediaPlayer != null){
				mediaPlayer.seekTo(0);
				mediaPlayer.start();
				status.setText("播放中");
			}
			break;

		case R.id.stop:
			if(mediaPlayer != null){
				mediaPlayer.stop();
				mediaPlayer.release();
				isStartPlaying = false;
				status.setText("停止");
			}
			break;

		default:
			break;
		}
	}

	/**
	 * 播放网络视频
	 * a. 创建并配置 MediaPlayer 对象 (音量, SurfaceHolder)
	 * b. 为 MediaPlayer 设置错误监听器, 缓冲进度监听器, 播放完毕监听器, 准备完毕监听器
	 * c. 未 MediaPlayer 设置数据源
	 * d. 调用 prepare() 进入 Prapared 状态
	 * e. 调用 start() 进入 Started 状态
	 *
	 * @param dataSource 播放视频的网络地址
	 */
	private void playVideo(final String dataSource) {

		/* 点击播放有两种情况
		 * a. 第一次点击 : 需要初始化 MediaPlayer 对象, 设置监听器
		 * b. 第二次点击 : 只需要 调用 mediaPlayer 的 start() 方法
		 * 两种情况通过 isStartPlaying 点击时间判断 */

		if(isStartPlaying){								/* 如果已经开始了播放, 就直接开始播放 */
			mediaPlayer.start();
		}else{											/* 如果是第一次开始播放, 需要初始化 MediaPlayer 设置监听器等操作 */
			mediaPlayer = new MediaPlayer(); 			/* 创建 MediaPlayer 对象 */
			mediaPlayer.setAudioStreamType(2); 			/* 设置播放音量 */
			mediaPlayer.setDisplay(surface_holder); 	/* 设置播放载体 */	

			/* 设置 MediaPlayer 错误监听器, 如果出现错误就会回调该方法打印错误代码 */
			mediaPlayer.setOnErrorListener(new OnErrorListener() {
				@Override
				public boolean onError(MediaPlayer arg0, int what, int extra) {
					System.out.println("MediaPlayer 出现错误 what : " + what + " , extra : " + extra);
					return false;
				}
			});

			/* 设置缓冲进度更新监听器 */
			mediaPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener() {
				@Override
				public void onBufferingUpdate(MediaPlayer arg0, int percent) {
					/* 打印缓冲的百分比, 如果缓冲 */
					System.out.println("缓冲了的百分比 : " + percent + " %");
				}
			});

			/* 设置播放完毕监听器 */
			mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
				@Override
				public void onCompletion(MediaPlayer arg0) {
					System.out.println("播放完毕了");
					status.setText("播放完毕");
				}
			});

			/* 设置准备完毕监听器 */
			mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
				@Override
				public void onPrepared(MediaPlayer arg0) {
					System.out.println("准备完毕");
					/* 设置播放状态 */
					status.setText("播放中");
				}
			});

			new Thread(){
				public void run() {
					try {

						System.out.println("设置数据源");

						mediaPlayer.setDataSource(dataSource);
						mediaPlayer.prepare();

						/* 打印播放视频的时长 */
						System.out.println("视频播放长度 : " + mediaPlayer.getDuration());

						mediaPlayer.start();

					} catch (IllegalStateException e) {
						e.printStackTrace();
					} catch (IOException e) {
						e.printStackTrace();
					}
				};
			}.start();

			/* 设置 MediaPlayer 开始播放标识为 true */
			isStartPlaying = true;
			/* 设置播放状态 */
			status.setText("正在缓冲");
		}

	}

	/**
	 * 在 Surface 大小发生改变的时候回调
	 * 实现的 SurfaceHolder.Callback 接口方法
	 */
	@Override
	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
		System.out.println("SurfaceHolder.Callback.surfaceChanged : Surface 大小发生改变");
	}

	/**
	 * 在 Surface 创建的时候回调, 一般在该方法中开始绘图
	 * 实现的 SurfaceHolder.Callback 接口方法
	 */
	@Override
	public void surfaceCreated(SurfaceHolder arg0) {
		System.out.println("SurfaceHolder.Callback.surfaceCreated : Surface 开始创建");
	}

	/**
	 * 在 Surface 销毁之前回调, 在该方法中停止渲染线程, 释放相关资源
	 * 实现的 SurfaceHolder.Callback 接口方法
	 */
	@Override
	public void surfaceDestroyed(SurfaceHolder arg0) {
		System.out.println("SurfaceHolder.Callback.surfaceDestroyed : Surface 销毁");
	}

	@Override
	protected void onDestroy() {
		if(mediaPlayer != null)
			mediaPlayer.release();
		super.onDestroy();
	}

}

4. 运行示例

日志信息 :

octopus@octopus:~/develop/adt-bundle-linux/sdk/tools$ adb logcat -s System.out
--------- beginning of /dev/log/main
--------- beginning of /dev/log/system
I/System.out(21129): 设置数据源
I/System.out(21129): 视频播放长度 : 31200
I/System.out(21129): 准备完毕
I/System.out(21129): 缓冲了的百分比 : 0 %
I/System.out(21129): 缓冲了的百分比 : 37 %
I/System.out(21129): 缓冲了的百分比 : 37 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 缓冲了的百分比 : 100 %
I/System.out(21129): 播放完毕了
I/System.out(21129): 缓冲了的百分比 : 100 %

作者 : 万境绝尘 (octopus_truth@163.com)

转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/38895143

【Android 多媒体开发】 MediaPlayer 网络视频播放器的更多相关文章

  1. 22_Android中的本地音乐播放器和网络音乐播放器的编写,本地视频播放器和网络视频播放器,照相机案例,偷拍案例实现

    1 编写以下案例: 当点击了"播放"之后,在手机上的/mnt/sdcard2/natural.mp3就会播放. 2 编写布局文件activity_main.xml <Line ...

  2. Android应用开发:网络工具——Volley(二)

    引言 在Android应用开发:网络工具--Volley(一)中结合Cloudant服务介绍了Volley的一般使用方法.当中包括了两种请求类型StringRequest和JsonObjectRequ ...

  3. [Android应用开发] 03.网络编程

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  4. Android应用开发--MP3音乐播放器代码实现(一)

    需求1:将内存卡中的MP3音乐读取出来并显示到列表当中 1.   从数据库中查询所有音乐数据,保存到List集合当中,List当中存放的是Mp3Info对象 2.   迭代List集合,把每一个Mp3 ...

  5. Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2

    1. 使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与Java层,工程量比较大.所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件 ...

  6. 【Android 多媒体开发】 MediaPlayer 状态机 接口 方法 解析

    作者 : 韩曙亮 转载请著名出处 :  http://blog.csdn.net/shulianghan/article/details/38487967 一. MediaPlayer 状态机 介绍 ...

  7. Android开发 MediaPlayer将视频播放时尺寸适配完美

    前言 视频播放有一个较为蛋疼的问题,那就是尺寸适配.如果不做尺寸适配视频将会变形拉伸或者压缩.下面我就介绍个人实现的算法. 满足一边的算法 满足一边?你可能是疑问是什么意思.意思是就是始终将视频的高度 ...

  8. Android 网络视频播放器

    项目概要: 1.登录界面 2.播放列表 3.播放界面

  9. Android多媒体开发-stagefright及AwesomePlayer相关知识梳理

    android的多媒体框架中, stagefright其实是AwesomePlayer的代理,就是个皮包公司. status_t StagefrightPlayer::setDataSource( c ...

随机推荐

  1. Lucene——Field.Store(存储域选项)及Field.Index(索引选项)

    Field.Store.YES或者NO(存储域选项) 设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原 设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,此时内容无法完 ...

  2. .net环境下跨进程、高频率读写数据

    一.需求背景 1.最近项目要求高频次地读写数据,数据量也不是很大,多表总共加起来在百万条上下. 单表最大的也在25万左右,历史数据表因为不涉及所以不用考虑, 难点在于这个规模的热点数据,变化非常频繁. ...

  3. Hibernate异常之cascade

    org.hibernate.MappingException: Unsupported cascade style: delete-option at org.hibernate.engine.spi ...

  4. Node.js JXcore 打包

    Node.js 是一个开放源代码.跨平台的.用于服务器端和网络应用的运行环境. JXcore 是一个支持多线程的 Node.js 发行版本,基本不需要对你现有的代码做任何改动就可以直接线程安全地以多线 ...

  5. Go 语言递归函数

    递归,就是在运行的过程中调用自己. 语法格式如下: func recursion() { recursion() /* 函数调用自身 */ } func main() { recursion() } ...

  6. PHP MySQL Order By 关键词

    ORDER BY 关键词用于对记录集中的数据进行排序. ORDER BY 关键词 ORDER BY 关键词用于对记录集中的数据进行排序. ORDER BY 关键词默认对记录进行升序排序. 如果你想降序 ...

  7. Docker服务端防护

    运行一个容器或应用程序的核心是通过 Docker 服务端.Docker 服务的运行目前需要 root 权限,因此其安全性十分关键. 首先,确保只有可信的用户才可以访问 Docker 服务.Docker ...

  8. 在查询语句中使用 NOLOCK 和 READPAST

    对于非银行等严格要求事务的行业,搜索记录中出现或者不出现某条记录,都是在可容忍范围内,所以碰到死锁,应该首先考虑,我们业务逻辑是否能容忍出现或者不出现某些记录,而不是寻求对双方都加锁条件下如何解锁的问 ...

  9. mysql 数据类型别名参考

    To facilitate the use of code written for SQL implementations from other vendors, MySQL maps data ty ...

  10. ant编译mysql驱动

    修改驱动源码后需要重新编译构建,由于mysql编译需要两个jdk版本且还需要hibernate4和junit,这里记录下. 安装ant. 配置两个jdk,5和8.并修改build.xml配置,如下: ...