流媒体播放
之所以为理论篇 因为该篇仅实现了播放功能 但还有一些其他待解决/完善功能 所以称之为理论篇
而且该篇以原理居多 故暂不释放源码
[原理]
1. 下载目标URI
2. 当下载了 96*10/8 Byte 开始播放之
3. 一边播放 一边下载
4. 当下载了 100 byte 暂停播放 重置播放目标 并继续播放
5. 下载完成后 重置播放目标 并继续播放
[代码 步骤]
1. 开辟Thread
public void startStreaming(final String mediaUri){
Runnable r = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
try {
readStream(mediaUri);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
new Thread(r).start();
}
2. 创建目标URI 并下载之
public void readStream(String mediaUri) throws MalformedURLException, IOException{
URLConnection uc = new URL(/blog_article/mediaUri/index.html).openConnection();
uc.connect();
InputStream is = uc.getInputStream();
if(is == null){
//error, InputStream is null
}
dlMedia = new File(context.getCacheDir(),"downloadingMedia.dat");
if(dlMedia.exists()){
dlMedia.delete();
}
FileOutputStream fo = new FileOutputStream(dlMedia);
byte buf[] = new byte[16384];
loadByte = 0;
do {
int numread = is.read(buf);
loadByte += numread;
if(numread <= 0){//end of stream, so exist
break;
}
fo.write(buf, 0, numread);
testMediaBuffer();
}
while(true);
is.close();
//buffer all stream to MediaPlayer if end of stream
transferBufferToMediaPlayer();
3. 根据此刻下载的byte 判断是:开始播放 还是 缓冲下载数据
public void testMediaBuffer(){
Runnable update = new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
//initial MediaPlayer if null
if(mp == null){
if(loadByte >= INTIAL_KB_BUFFER){
loadByte = 0;
startMediaPlayer();
}
}//load buffer while 1000
else if(loadByte > BUFFER_KEY_BYTE) {
loadByte = 0;
transferBufferToMediaPlayer();
}
}
};
handler.post(update);
}
4. 开始播放 并跟踪播放进度
public void startPlayProgressUpdater() {
//float progress = mp.getCurrentPosition();
if (mp.isPlaying()) {
Runnable notification = new Runnable() {
public void run() {
startPlayProgressUpdater();
}
};
handler.postDelayed(notification,100);
}
}
public void startMediaPlayer() {
try {
File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
// We double buffer the data to avoid potential read/write errors that could happen if the
// download thread attempted to write at the same time the MediaPlayer was trying to read.
// For example, we can't guarantee that the MediaPlayer won't open a file for playing and leave it locked while
// the media is playing. This would permanently deadlock the file download. To avoid such a deadloack,
// we move the currently loaded data to a temporary buffer file that we start playing while the remaining
// data downloads.
moveFile(dlMedia,bufferedFile);
mp = createMediaPlayer(bufferedFile);
// We have pre-loaded enough content and started the MediaPlayer so update the buttons & progress meters.
mp.start();
startPlayProgressUpdater();
} catch (IOException e) {
//Log.e(getClass().getName(), "Error initializing the MediaPlayer.", e);
return;
}
}
5. 缓冲下载数据
private void transferBufferToMediaPlayer() {
try {
//remember current position
int curPosition = mp.getCurrentPosition();
// Copy the currently downloaded content to a new buffered File. Store the old File for deleting later.
File bufferedFile = new File(context.getCacheDir(),"playingMedia" + (counter++) + ".dat");
moveFile(dlMedia,bufferedFile);
// Pause the current player now as we are about to create and start a new one. So far (Android v1.5),
// this always happens so quickly that the user never realized we've stopped the player and started a new one
mp.pause();
// Create a new MediaPlayer rather than try to re-prepare the prior one.
mp = createMediaPlayer(bufferedFile);
mp.seekTo(curPosition);
mp.start();
// Lastly delete the previously playing buffered File as it's no longer needed.
bufferedFile.delete();
}catch (Exception e) {
//error, to print
}
}
6. 文件移动
private void moveFile(File oldLocation, File newLocation)
throws IOException {
if ( oldLocation.exists( )) {
BufferedInputStream reader = new BufferedInputStream( new FileInputStream(oldLocation) );
BufferedOutputStream writer = new BufferedOutputStream( new FileOutputStream(newLocation, false));
try {
byte[] buff = new byte[8192];
int numChars;
while ( (numChars = reader.read( buff, 0, buff.length ) ) != -1) {
writer.write( buff, 0, numChars );
}
} catch( IOException ex ) {
throw new IOException("IOException when transferring " + oldLocation.getPath() + " to " + newLocation.getPath());
} finally {
try {
if ( reader != null ){
writer.close();
reader.close();
}
} catch( IOException ex ){
//Log.e(getClass().getName(),"Error closing files when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() );
}
}
} else {
throw new IOException("Old location does not exist when transferring " + oldLocation.getPath() + " to " + newLocation.getPath() );
}
}
7. 播放指定目标
private MediaPlayer createMediaPlayer(File mediaFile)
throws IOException {
MediaPlayer mPlayer = new MediaPlayer();
// It appears that for security/permission reasons, it is better to pass a FileDescriptor rather than a direct path to the File.
// Also I have seen errors such as "PVMFErrNotSupported" and "Prepare failed.: status=0x1" if a file path String is passed to
// setDataSource(). So unless otherwise noted, we use a FileDescriptor here.
FileInputStream fis = new FileInputStream(mediaFile);
mPlayer.setDataSource(fis.getFD());
mPlayer.prepare();
return mPlayer;
}
8. Toast 信息提示:
public void popMsg(String msg){
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
}
9. done, plx test it, post feedback, thanks
是的啊 但是从效果看不是太好 我现在也在寻找更smooth的方法
在Android中所有的视图基本是与View有关的.一个重点的组件就是常用的ListView。这个组件的用法很多在在Android的SDK组件有讲解,我这里只是简单的使用使用常用一种方式。
列表的显示需要三个元素:
1.ListVeiw 用来展示列表的View。
2.适配器 用来把数据映射到ListView上的中介。
3.数据 具体的将被映射的字符串,图片,或者基本组件。
根据列表的适配器类型,列表分为三种,ArrayAdapter,SimpleAdapter和SimpleCursorAdapter其中以ArrayAdapter最为简单,只能展示一行字。SimpleAdapter有最好的扩充性,可以自定义出各种效果。SimpleCursorAdapter可以认为是SimpleAdapter对数据库的简单结合,可以方面的把数据库的内容以列表的形式展示出来。
我的采用ArrayAdapter的使用如下:
源代码DictActivity使用如下:
/**
* 创建组件
*/
public void createComponent(){
List<SearchItem> items = new ArrayList<SearchItem>();
//创建自定义的ArrayAapter的适配器对象
searchItemAdapter = new SearchItemAdapter(this, R.layout.row, items);
//获取ListView对象
searchListView = (ListView) findViewById(R.id.search_listview);
//设置适配器对象
searchListView.setAdapter(searchItemAdapter);
queryButton = (Button) findViewById(R.id.query_button);
wordEdit = (EditText) findViewById(R.id.word_edittext);
}
好了,下一篇我们将讲解关于中英文字段翻译的布局文件,主要为layout下面的文件,稍后再学习呗!
ConnectivityManager conMan = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
//mobile
State mobile = conMan.getNetworkInfo(0).getState();
//wifi
State wifi = conMan.getNetworkInfo(1).getState();
if (mobile == NetworkInfo.State.CONNECTED || mobile == NetworkInfo.State.CONNECTING) {
//mobile
} else if (wifi == NetworkInfo.State.CONNECTED || wifi == NetworkInfo.State.CONNECTING) {
//wifi
}