流媒体播放
之所以为理论篇 因为该篇仅实现了播放功能 但还有一些其他待解决/完善功能 所以称之为理论篇
而且该篇以原理居多 故暂不释放源码
[原理]
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
}