├── AndroidManifest.xml ├── LICENSE ├── README.md ├── ic_launcher-web.png ├── proguard-project.txt ├── project.properties ├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xhdpi │ └── ic_launcher.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── layout │ └── activity_player.xml ├── menu │ └── player.xml ├── values-sw600dp │ └── dimens.xml ├── values-sw720dp-land │ └── dimens.xml ├── values-v11 │ └── styles.xml ├── values-v14 │ └── styles.xml └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── src └── com └── bedpotato └── musicplayerproxy ├── App.java ├── Constants.java ├── Player.java ├── PlayerActivity.java ├── PreLoad.java ├── db └── CacheFileInfoDao.java └── utils ├── HttpUtils.java ├── LogcatHelper.java ├── MediaPlayerProxy.java ├── ProxyFileUtils.java ├── ProxyUtils.java └── RequestDealThread.java /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "{}" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright 2013 lialun 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #MusicPlayerProxy 2 | 一个Android音乐播放器代理的实例。 3 | MediaPlayer的缓存大小是无法修改的,缓存文件是无法得到的。 4 | 而在Android4.0之后,系统把缓存调节到了一个较大值,导致在移动网络下onPrepare时间过长。 5 | 同一首音频在重复听或者seek时会多次发请求,不会缓存下来,导致浪费流量。 6 | 7 | 本演示项目实现了一个预缓存、缓存机制。在播放前,可以把音频预缓存, 8 | 音频听过一次后,会缓存下来,重复播放时在本地读取本地文件,不会发送请求。 9 | 10 | 参考: 11 | http://stackoverflow.com/questions/4413300/change-buffer-size-on-mediaplayer 12 | http://stackoverflow.com/questions/10060165/android-mediaplayer-buffer-size-in-ics-4-0 13 | https://code.google.com/p/npr-android-app/source/browse/Npr/src/org/npr/android/news/StreamProxy.java 14 | http://blog.csdn.net/hellogv/article/category/1198699 (推荐大家看下他的博客) 15 | 16 | 欢迎+koukou:(537)(597)(299)讨论交流。(注明MediaPlayerProxy) -------------------------------------------------------------------------------- /ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewhanks/MediaPlayerProxy/baa06d62e6a9ef0efc762787e3642da0635bab61/ic_launcher-web.png -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-19 15 | -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewhanks/MediaPlayerProxy/baa06d62e6a9ef0efc762787e3642da0635bab61/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewhanks/MediaPlayerProxy/baa06d62e6a9ef0efc762787e3642da0635bab61/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewhanks/MediaPlayerProxy/baa06d62e6a9ef0efc762787e3642da0635bab61/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewhanks/MediaPlayerProxy/baa06d62e6a9ef0efc762787e3642da0635bab61/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/layout/activity_player.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 23 | 24 | 30 | 36 | 37 | 38 | 43 | 44 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /res/menu/player.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | MusicPlayerProxy 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/App.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.bedpotato.musicplayerproxy.utils.LogcatHelper; 7 | 8 | /** 9 | * Application 10 | */ 11 | public class App extends Application { 12 | 13 | public static final String TAG = "MusicPlayerTest"; 14 | 15 | public static Context mContext; // 应用全局context 16 | 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | mContext = this.getApplicationContext(); 21 | LogcatHelper.getInstance(this).start(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/Constants.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy; 2 | 3 | import android.os.Environment; 4 | 5 | public class Constants { 6 | /** SD卡路径 */ 7 | public static final String SD_PATH = Environment.getExternalStorageDirectory().getPath() + "/"; 8 | /** 缓存保存路径 */ 9 | public static final String DOWNLOAD_PATH = SD_PATH + "MusicPlayerTest/BufferFiles/"; 10 | /** SD卡预留最小值 */ 11 | public static final int SD_REMAIN_SIZE = 50 * 1024 * 1024; 12 | /** 单次缓存文件最大值 */ 13 | public static final int AUDIO_BUFFER_MAX_LENGTH = 2 * 1024 * 1024; 14 | /** 缓存文件个数最大值 */ 15 | public static final int CACHE_FILE_NUMBER = 3; 16 | /** 预缓存文件大小 */ 17 | public static final int PRECACHE_SIZE = 300*1000; 18 | // Http Header Name 19 | public final static String CONTENT_RANGE = "Content-Range"; 20 | public final static String CONTENT_LENGTH = "Content-Length"; 21 | public final static String RANGE = "Range"; 22 | public final static String HOST = "Host"; 23 | public final static String USER_AGENT = "User-Agent"; 24 | // Http Header Value Parts 25 | public final static String RANGE_PARAMS = "bytes="; 26 | public final static String RANGE_PARAMS_0 = "bytes=0-"; 27 | public final static String CONTENT_RANGE_PARAMS = "bytes "; 28 | 29 | public final static String LINE_BREAK = "\r\n"; 30 | public final static String HTTP_END = LINE_BREAK + LINE_BREAK; 31 | } 32 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/Player.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy; 2 | 3 | import java.io.IOException; 4 | import java.util.Timer; 5 | import java.util.TimerTask; 6 | 7 | import android.annotation.SuppressLint; 8 | import android.media.AudioManager; 9 | import android.media.MediaPlayer; 10 | import android.media.MediaPlayer.OnBufferingUpdateListener; 11 | import android.media.MediaPlayer.OnCompletionListener; 12 | import android.os.Handler; 13 | import android.os.Message; 14 | import android.util.Log; 15 | import android.widget.SeekBar; 16 | 17 | import com.bedpotato.musicplayerproxy.utils.MediaPlayerProxy; 18 | 19 | public class Player implements OnBufferingUpdateListener, OnCompletionListener, MediaPlayer.OnPreparedListener { 20 | public MediaPlayer mediaPlayer; 21 | private SeekBar skbProgress; 22 | 23 | private Timer mTimer = new Timer(); 24 | 25 | MediaPlayerProxy proxy; 26 | 27 | private boolean USE_PROXY = true; 28 | 29 | public Player(SeekBar skbProgress) { 30 | this.skbProgress = skbProgress; 31 | 32 | try { 33 | mediaPlayer = new MediaPlayer(); 34 | mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); 35 | mediaPlayer.setOnBufferingUpdateListener(this); 36 | mediaPlayer.setOnPreparedListener(this); 37 | } catch (Exception e) { 38 | Log.e("mediaPlayer", "error", e); 39 | } 40 | 41 | mTimer.schedule(mTimerTask, 0, 1000); 42 | 43 | proxy = new MediaPlayerProxy(); 44 | proxy.init(); 45 | proxy.start(); 46 | } 47 | 48 | /******************************************************* 49 | * 通过定时器和Handler来更新进度条 50 | ******************************************************/ 51 | TimerTask mTimerTask = new TimerTask() { 52 | @Override 53 | public void run() { 54 | if (mediaPlayer == null) 55 | return; 56 | if (mediaPlayer.isPlaying() && skbProgress.isPressed() == false) { 57 | handleProgress.sendEmptyMessage(0); 58 | } 59 | } 60 | }; 61 | 62 | @SuppressLint("HandlerLeak") 63 | Handler handleProgress = new Handler() { 64 | public void handleMessage(Message msg) { 65 | int position = mediaPlayer.getCurrentPosition(); 66 | int duration = mediaPlayer.getDuration(); 67 | 68 | if (duration > 0) { 69 | long pos = skbProgress.getMax() * position / duration; 70 | skbProgress.setProgress((int) pos); 71 | } 72 | }; 73 | }; 74 | 75 | public void play() { 76 | mediaPlayer.start(); 77 | } 78 | 79 | public void playUrl(String url) { 80 | 81 | if (USE_PROXY) { 82 | startProxy(); 83 | url = proxy.getProxyURL(url); 84 | } 85 | 86 | try { 87 | mediaPlayer.reset(); 88 | mediaPlayer.setDataSource(url); 89 | long p = System.currentTimeMillis(); 90 | Log.e("P", String.valueOf(p)); 91 | mediaPlayer.prepare(); 92 | long s = System.currentTimeMillis(); 93 | Log.e("S", String.valueOf(s) + " " + (p - s)); 94 | mediaPlayer.start(); 95 | long x = System.currentTimeMillis(); 96 | Log.e("X", String.valueOf(x) + " " + (x - s)); 97 | } catch (IllegalArgumentException e) { 98 | e.printStackTrace(); 99 | } catch (IllegalStateException e) { 100 | e.printStackTrace(); 101 | } catch (IOException e) { 102 | e.printStackTrace(); 103 | } 104 | } 105 | 106 | public void pause() { 107 | mediaPlayer.pause(); 108 | } 109 | 110 | public void stop() { 111 | if (mediaPlayer != null) { 112 | mediaPlayer.stop(); 113 | mediaPlayer.release(); 114 | mediaPlayer = null; 115 | } 116 | } 117 | 118 | @Override 119 | /** 120 | * 通过onPrepared播放 121 | */ 122 | public void onPrepared(MediaPlayer arg0) { 123 | Log.e("mediaPlayer", "onPrepared"); 124 | arg0.start(); 125 | 126 | } 127 | 128 | @Override 129 | public void onCompletion(MediaPlayer arg0) { 130 | Log.e("mediaPlayer", "onCompletion"); 131 | } 132 | 133 | @Override 134 | public void onBufferingUpdate(MediaPlayer arg0, int bufferingProgress) { 135 | skbProgress.setSecondaryProgress(bufferingProgress); 136 | int currentProgress = skbProgress.getMax() * mediaPlayer.getCurrentPosition() / mediaPlayer.getDuration(); 137 | // Log.e(currentProgress + "% play", bufferingProgress + "% buffer"); 138 | } 139 | 140 | private void startProxy() { 141 | if (proxy == null) { 142 | proxy = new MediaPlayerProxy(); 143 | proxy.init(); 144 | proxy.start(); 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/PlayerActivity.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | 5 | import android.annotation.SuppressLint; 6 | import android.app.Activity; 7 | import android.os.Bundle; 8 | import android.os.StrictMode; 9 | import android.view.View; 10 | import android.view.View.OnClickListener; 11 | import android.widget.Button; 12 | import android.widget.SeekBar; 13 | 14 | import com.bedp.musicplayerproxy.R; 15 | 16 | 17 | public class PlayerActivity extends Activity { 18 | 19 | private Button btnPlayUrl; 20 | private Button btnCache; 21 | private SeekBar skbProgress; 22 | private Player player; 23 | 24 | @SuppressLint("NewApi") 25 | @Override 26 | public void onCreate(Bundle savedInstanceState) { 27 | 28 | super.onCreate(savedInstanceState); 29 | 30 | if (android.os.Build.VERSION.SDK_INT > 9) { 31 | StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); 32 | StrictMode.setThreadPolicy(policy); 33 | } 34 | 35 | setContentView(R.layout.activity_player); 36 | 37 | this.setTitle("MediaPlayerTest--By BedPotato"); 38 | 39 | btnPlayUrl = (Button) this.findViewById(R.id.btnPlayUrl); 40 | btnPlayUrl.setOnClickListener(new ClickEvent()); 41 | 42 | btnCache = (Button) this.findViewById(R.id.btnCache); 43 | btnCache.setOnClickListener(new ClickEvent()); 44 | 45 | skbProgress = (SeekBar) this.findViewById(R.id.skbProgress); 46 | skbProgress.setOnSeekBarChangeListener(new SeekBarChangeEvent()); 47 | 48 | player = new Player(skbProgress); 49 | } 50 | 51 | class ClickEvent implements OnClickListener { 52 | 53 | @Override 54 | public void onClick(View arg0) { 55 | String url = "http://lkfm-audio.b0.upaiyun.com/audio/综艺娱乐/娱乐节目/娱乐猛回头/神剧<还珠格格>15年捧出3天后.mp3"; 56 | final String urlEn = urlEncode(url); 57 | if (arg0 == btnPlayUrl) { 58 | player.playUrl(url); 59 | }else { 60 | new Thread(new Runnable() { 61 | @Override 62 | public void run() { 63 | PreLoad load = new PreLoad(urlEn); 64 | load.download(300*1000); 65 | } 66 | }).start(); 67 | } 68 | } 69 | } 70 | 71 | class SeekBarChangeEvent implements SeekBar.OnSeekBarChangeListener { 72 | int progress; 73 | 74 | @Override 75 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 76 | this.progress = progress * player.mediaPlayer.getDuration() / seekBar.getMax(); 77 | } 78 | 79 | @Override 80 | public void onStartTrackingTouch(SeekBar seekBar) { 81 | } 82 | 83 | @Override 84 | public void onStopTrackingTouch(SeekBar seekBar) { 85 | // seekTo()的参数是相对与影片时间的数字,而不是与seekBar.getMax()相对的数字 86 | player.mediaPlayer.seekTo(progress); 87 | } 88 | } 89 | 90 | /** 91 | * URL编码 92 | * 93 | * @param url 94 | * @return 95 | */ 96 | public static String urlEncode(String url) { 97 | try { 98 | url = java.net.URLEncoder.encode(url, "UTF-8"); 99 | url = url.replaceAll("%2F", "/"); 100 | url = url.replaceAll("%3A", ":"); 101 | url = url.replaceAll("\\+", "%20"); 102 | } catch (UnsupportedEncodingException e) { 103 | e.printStackTrace(); 104 | } 105 | return url; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/PreLoad.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.URI; 6 | 7 | import org.apache.http.HttpResponse; 8 | import org.apache.http.client.methods.HttpGet; 9 | 10 | import android.util.Log; 11 | 12 | import com.bedpotato.musicplayerproxy.db.CacheFileInfoDao; 13 | import com.bedpotato.musicplayerproxy.utils.HttpUtils; 14 | import com.bedpotato.musicplayerproxy.utils.ProxyFileUtils; 15 | import com.bedpotato.musicplayerproxy.utils.RequestDealThread; 16 | 17 | public class PreLoad extends Thread{ 18 | 19 | private static final String LOG_TAG = RequestDealThread.class.getSimpleName(); 20 | 21 | CacheFileInfoDao cacheDao = CacheFileInfoDao.getInstance(); 22 | ProxyFileUtils fileUtils; 23 | URI uri; 24 | 25 | public PreLoad(String url) { 26 | uri = URI.create(url); 27 | fileUtils = ProxyFileUtils.getInstance(uri, false); 28 | } 29 | 30 | public boolean download(int size) { 31 | try { 32 | Log.i(LOG_TAG, "缓存开始"); 33 | // 34 | if (!fileUtils.isEnable()) { 35 | return false; 36 | } 37 | // 得到文件长度,如果超过缓冲给定长度,则返回 38 | int fileLength = fileUtils.getLength(); 39 | if (fileLength >= size) { 40 | return true; 41 | } 42 | // 如果已经下载完成,返回 43 | System.out.println(fileUtils.getLength() + " " + cacheDao.getFileSize(fileUtils.getFileName())); 44 | if (fileUtils.getLength() == cacheDao.getFileSize(fileUtils.getFileName())) { 45 | return true; 46 | } 47 | // 从之前的位置缓存 48 | HttpResponse response = HttpUtils.send(new HttpGet(uri)); 49 | if (response == null) { 50 | return false; 51 | } 52 | int contentLength = Integer.valueOf(response.getHeaders(Constants.CONTENT_LENGTH)[0].getValue()); 53 | cacheDao.insertOrUpdate(fileUtils.getFileName(), contentLength); 54 | // 55 | InputStream data = response.getEntity().getContent(); 56 | byte[] buff = new byte[1024 * 40]; 57 | int readBytes = 0; 58 | int fileSize = 0; 59 | while (fileUtils.isEnable() && (readBytes = data.read(buff, 0, buff.length)) != -1) { 60 | fileUtils.write(buff, readBytes); 61 | fileSize += readBytes; 62 | if (fileSize >= size) { 63 | break; 64 | } 65 | } 66 | if (fileUtils.isEnable()) { 67 | return true; 68 | } else { 69 | return false; 70 | } 71 | } catch (IOException e) { 72 | Log.e(LOG_TAG, "缓存异常", e); 73 | return false; 74 | } finally { 75 | Log.i(LOG_TAG, "缓存结束"); 76 | ProxyFileUtils.close(fileUtils, false); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/db/CacheFileInfoDao.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy.db; 2 | 3 | import java.util.Collection; 4 | import java.util.Map; 5 | 6 | import android.content.ContentValues; 7 | import android.database.Cursor; 8 | import android.database.sqlite.SQLiteDatabase; 9 | import android.database.sqlite.SQLiteOpenHelper; 10 | import android.util.Log; 11 | 12 | import com.bedpotato.musicplayerproxy.App; 13 | 14 | public class CacheFileInfoDao extends SQLiteOpenHelper { 15 | 16 | static final int DB_VERSION = 1; 17 | static final String DB_NAME = "cachefileinfo.db"; 18 | static final String TABLE_NAME = "CacheFileInfo"; 19 | 20 | private static CacheFileInfoDao audioDao; 21 | 22 | synchronized public static CacheFileInfoDao getInstance() { 23 | if (audioDao == null) { 24 | audioDao = new CacheFileInfoDao(); 25 | } 26 | return audioDao; 27 | } 28 | 29 | private CacheFileInfoDao() { 30 | super(App.mContext, DB_NAME, null, DB_VERSION); 31 | } 32 | 33 | public void insertOrUpdate(String fileName, int fileSize) { 34 | CacheFileInfo cacheFileInfo = new CacheFileInfo(fileName, fileSize); 35 | if (getFileSize(cacheFileInfo.getFileName()) == -1) { 36 | insert(cacheFileInfo); 37 | } else { 38 | update(cacheFileInfo); 39 | } 40 | } 41 | 42 | private void insert(CacheFileInfo cacheFileInfo) { 43 | SQLiteDatabase sqLiteDatabase = getWritableDatabase(); 44 | sqLiteDatabase.beginTransaction(); 45 | try { 46 | ContentValues cv = packData(cacheFileInfo); 47 | sqLiteDatabase.insert(TABLE_NAME, null, cv); 48 | sqLiteDatabase.setTransactionSuccessful(); 49 | } finally { 50 | sqLiteDatabase.endTransaction(); 51 | } 52 | } 53 | 54 | public void delete(String fileName) { 55 | SQLiteDatabase sqLiteDatabase = getWritableDatabase(); 56 | sqLiteDatabase.beginTransaction(); 57 | try { 58 | sqLiteDatabase.delete(TABLE_NAME, "FileName=?", new String[] { fileName }); 59 | sqLiteDatabase.setTransactionSuccessful(); 60 | } finally { 61 | sqLiteDatabase.endTransaction(); 62 | } 63 | } 64 | 65 | private void update(CacheFileInfo cacheFileSize) { 66 | SQLiteDatabase sqLiteDatabase = getWritableDatabase(); 67 | sqLiteDatabase.beginTransaction(); 68 | try { 69 | ContentValues cv = packData(cacheFileSize); 70 | sqLiteDatabase.update(TABLE_NAME, cv, "FileName=?", new String[] { cacheFileSize.getFileName() }); 71 | sqLiteDatabase.setTransactionSuccessful(); 72 | } finally { 73 | sqLiteDatabase.endTransaction(); 74 | } 75 | } 76 | 77 | public int getFileSize(String fileName) { 78 | Cursor cursor = rawQuery("SELECT * FROM " + TABLE_NAME + " WHERE FileName=?", new String[] { fileName }); 79 | CacheFileInfo t = null; 80 | if (cursor != null) { 81 | if (cursor.moveToFirst()) { 82 | t = extractData(cursor); 83 | } 84 | 85 | cursor.close(); 86 | cursor = null; 87 | } 88 | if (t == null) { 89 | return -1; 90 | } else { 91 | return t.getFileSize(); 92 | } 93 | } 94 | 95 | public CacheFileInfo extractData(Cursor cursor) { 96 | if (null == cursor) { 97 | return null; 98 | } 99 | CacheFileInfo album = new CacheFileInfo(); 100 | album.setFileName(cursor.getString(cursor.getColumnIndex("FileName"))); 101 | album.setFileSize(cursor.getInt(cursor.getColumnIndex("FileSize"))); 102 | return album; 103 | } 104 | 105 | public ContentValues packData(CacheFileInfo cacheFileSize) { 106 | ContentValues cv = new ContentValues(); 107 | cv.put("FileName", cacheFileSize.getFileName()); 108 | cv.put("FileSize", cacheFileSize.getFileSize()); 109 | return cv; 110 | } 111 | 112 | @Override 113 | public void onCreate(SQLiteDatabase db) { 114 | Log.i("DB", "CreateTable " + TABLE_NAME); 115 | db.execSQL("create table " + TABLE_NAME + "(FileName STRING PRIMARY KEY,FileSize INTEGER)"); 116 | } 117 | 118 | @Override 119 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 120 | 121 | } 122 | 123 | public Cursor rawQuery(String sql, String[] selectionArgs) { 124 | SQLiteDatabase database = getReadableDatabase(); 125 | return database.rawQuery(sql, selectionArgs); 126 | } 127 | 128 | public static boolean isNull(Object object) { 129 | if (object == null) { 130 | return true; 131 | } 132 | if (object instanceof Collection) { 133 | return ((Collection) object).size() == 0; 134 | } 135 | if (object instanceof Map) { 136 | return ((Map) object).size() == 0; 137 | } 138 | return false; 139 | } 140 | 141 | class CacheFileInfo { 142 | private String fileName; 143 | private int fileSize; 144 | 145 | public CacheFileInfo(String fileName, int fileSize) { 146 | this.fileName = fileName; 147 | this.fileSize = fileSize; 148 | } 149 | 150 | public CacheFileInfo() { 151 | } 152 | 153 | public String getFileName() { 154 | return fileName; 155 | } 156 | 157 | public void setFileName(String fileName) { 158 | this.fileName = fileName; 159 | } 160 | 161 | public int getFileSize() { 162 | return fileSize; 163 | } 164 | 165 | public void setFileSize(int fileSize) { 166 | this.fileSize = fileSize; 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/utils/HttpUtils.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy.utils; 2 | 3 | import java.io.IOException; 4 | 5 | import org.apache.http.HttpResponse; 6 | import org.apache.http.HttpResponseFactory; 7 | import org.apache.http.ParseException; 8 | import org.apache.http.ProtocolVersion; 9 | import org.apache.http.StatusLine; 10 | import org.apache.http.client.ClientProtocolException; 11 | import org.apache.http.client.methods.HttpUriRequest; 12 | import org.apache.http.conn.ClientConnectionOperator; 13 | import org.apache.http.conn.OperatedClientConnection; 14 | import org.apache.http.conn.scheme.PlainSocketFactory; 15 | import org.apache.http.conn.scheme.Scheme; 16 | import org.apache.http.conn.scheme.SchemeRegistry; 17 | import org.apache.http.impl.client.DefaultHttpClient; 18 | import org.apache.http.impl.conn.DefaultClientConnection; 19 | import org.apache.http.impl.conn.DefaultClientConnectionOperator; 20 | import org.apache.http.impl.conn.DefaultResponseParser; 21 | import org.apache.http.impl.conn.SingleClientConnManager; 22 | import org.apache.http.io.HttpMessageParser; 23 | import org.apache.http.io.SessionInputBuffer; 24 | import org.apache.http.message.BasicLineParser; 25 | import org.apache.http.message.ParserCursor; 26 | import org.apache.http.params.CoreConnectionPNames; 27 | import org.apache.http.params.HttpParams; 28 | import org.apache.http.protocol.HTTP; 29 | import org.apache.http.util.CharArrayBuffer; 30 | 31 | import android.util.Log; 32 | 33 | import com.bedpotato.musicplayerproxy.Constants; 34 | 35 | public class HttpUtils { 36 | 37 | private static final String LOG_TAG = HttpUtils.class.getSimpleName(); 38 | 39 | /** 40 | * 生成返回MediaPlayer的Response Header 41 | * 42 | * @param rangeStart 43 | * @param rangeEnd 44 | * @param fileLength 45 | * @return 46 | */ 47 | public static String genResponseHeader(int rangeStart, int rangeEnd, int fileLength) { 48 | StringBuffer sb = new StringBuffer(); 49 | sb.append("HTTP/1.1 206 Partial Content").append("\n"); 50 | sb.append("Content-Type: audio/mpeg").append("\n"); 51 | sb.append("Content-Length: ").append(rangeEnd - rangeStart + 1).append("\n"); 52 | sb.append("Connection: keep-alive").append("\n"); 53 | sb.append("Accept-Ranges: bytes").append("\n"); 54 | String contentRangeValue = String.format(Constants.CONTENT_RANGE_PARAMS + "%d-%d/%d", rangeStart, rangeEnd, 55 | fileLength); 56 | sb.append("Content-Range: ").append(contentRangeValue).append("\n"); 57 | sb.append("\n"); 58 | return sb.toString(); 59 | } 60 | 61 | /** 62 | * 发送请求,得到Response 63 | * 64 | * @param url 65 | * @return 66 | */ 67 | public static HttpResponse send(HttpUriRequest request) { 68 | /* 69 | * 添加需要的Header 70 | */ 71 | request.removeHeaders(Constants.USER_AGENT); 72 | request.addHeader(Constants.USER_AGENT, "TrafficRadio_BedPotato_Exclusive_UA"); 73 | // TODO Others Header 74 | /* 75 | * 发送请求 76 | */ 77 | DefaultHttpClient seed = new DefaultHttpClient(); 78 | SchemeRegistry registry = new SchemeRegistry(); 79 | registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); 80 | SingleClientConnManager mgr = new MyClientConnManager(seed.getParams(), registry); 81 | DefaultHttpClient http = new DefaultHttpClient(mgr, seed.getParams()); 82 | http.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 20000);// 连接时间20s 83 | http.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 60000); 84 | HttpResponse response = null; 85 | try { 86 | Log.d(LOG_TAG, "sending request"); 87 | response = http.execute(request); 88 | Log.d(LOG_TAG, "response receive"); 89 | } catch (ClientProtocolException e) { 90 | Log.e(LOG_TAG, "Error downloading", e); 91 | } catch (IOException e) { 92 | Log.e(LOG_TAG, "Error downloading", e); 93 | } 94 | StatusLine line = response.getStatusLine(); 95 | if (line.getStatusCode() != 200 && line.getStatusCode() != 206) { 96 | Log.i(LOG_TAG, "ERROR Response Status:" + line.toString()); 97 | return null; 98 | }else { 99 | return response; 100 | } 101 | } 102 | 103 | private static class IcyLineParser extends BasicLineParser { 104 | private static final String ICY_PROTOCOL_NAME = "ICY"; 105 | 106 | private IcyLineParser() { 107 | super(); 108 | } 109 | 110 | @Override 111 | public boolean hasProtocolVersion(CharArrayBuffer buffer, ParserCursor cursor) { 112 | boolean superFound = super.hasProtocolVersion(buffer, cursor); 113 | if (superFound) { 114 | return true; 115 | } 116 | int index = cursor.getPos(); 117 | 118 | final int protolength = ICY_PROTOCOL_NAME.length(); 119 | 120 | if (buffer.length() < protolength) 121 | return false; // not long enough for "HTTP/1.1" 122 | 123 | if (index < 0) { 124 | // end of line, no tolerance for trailing whitespace 125 | // this works only for single-digit major and minor version 126 | index = buffer.length() - protolength; 127 | } else if (index == 0) { 128 | // beginning of line, tolerate leading whitespace 129 | while ((index < buffer.length()) && HTTP.isWhitespace(buffer.charAt(index))) { 130 | index++; 131 | } 132 | } // else within line, don't tolerate whitespace 133 | 134 | return index + protolength <= buffer.length() 135 | && buffer.substring(index, index + protolength).equals(ICY_PROTOCOL_NAME); 136 | 137 | } 138 | 139 | @Override 140 | public ProtocolVersion parseProtocolVersion(CharArrayBuffer buffer, ParserCursor cursor) throws ParseException { 141 | 142 | if (buffer == null) { 143 | throw new IllegalArgumentException("Char array buffer may not be null"); 144 | } 145 | if (cursor == null) { 146 | throw new IllegalArgumentException("Parser cursor may not be null"); 147 | } 148 | 149 | final int protolength = ICY_PROTOCOL_NAME.length(); 150 | 151 | int indexFrom = cursor.getPos(); 152 | int indexTo = cursor.getUpperBound(); 153 | 154 | skipWhitespace(buffer, cursor); 155 | 156 | int i = cursor.getPos(); 157 | 158 | // long enough for "HTTP/1.1"? 159 | if (i + protolength + 4 > indexTo) { 160 | throw new ParseException("Not a valid protocol version: " + buffer.substring(indexFrom, indexTo)); 161 | } 162 | 163 | // check the protocol name and slash 164 | if (!buffer.substring(i, i + protolength).equals(ICY_PROTOCOL_NAME)) { 165 | return super.parseProtocolVersion(buffer, cursor); 166 | } 167 | 168 | cursor.updatePos(i + protolength); 169 | 170 | return createProtocolVersion(1, 0); 171 | } 172 | 173 | @Override 174 | public StatusLine parseStatusLine(CharArrayBuffer buffer, ParserCursor cursor) throws ParseException { 175 | return super.parseStatusLine(buffer, cursor); 176 | } 177 | } 178 | 179 | private static class MyClientConnection extends DefaultClientConnection { 180 | @Override 181 | protected HttpMessageParser createResponseParser(final SessionInputBuffer buffer, 182 | final HttpResponseFactory responseFactory, final HttpParams params) { 183 | return new DefaultResponseParser(buffer, new IcyLineParser(), responseFactory, params); 184 | } 185 | } 186 | 187 | private static class MyClientConnectionOperator extends DefaultClientConnectionOperator { 188 | public MyClientConnectionOperator(final SchemeRegistry sr) { 189 | super(sr); 190 | } 191 | 192 | @Override 193 | public OperatedClientConnection createConnection() { 194 | return new MyClientConnection(); 195 | } 196 | } 197 | 198 | private static class MyClientConnManager extends SingleClientConnManager { 199 | private MyClientConnManager(HttpParams params, SchemeRegistry schreg) { 200 | super(params, schreg); 201 | } 202 | 203 | @Override 204 | protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry sr) { 205 | return new MyClientConnectionOperator(sr); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/utils/LogcatHelper.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | import android.content.Context; 13 | import android.os.Environment; 14 | 15 | /** 16 | * log日志统计保存 17 | * 18 | * @author way 19 | * 20 | */ 21 | 22 | public class LogcatHelper { 23 | 24 | private static LogcatHelper INSTANCE = null; 25 | private static String PATH_LOGCAT; 26 | private LogDumper mLogDumper = null; 27 | private int mPId; 28 | 29 | /** 30 | * 31 | * 初始化目录 32 | * 33 | * */ 34 | public void init(Context context) { 35 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// 优先保存到SD卡中 36 | PATH_LOGCAT = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "edoglog"; 37 | } else {// 如果SD卡不存在,就保存到本应用的目录下 38 | PATH_LOGCAT = context.getFilesDir().getAbsolutePath() + File.separator + "edoglog"; 39 | } 40 | File file = new File(PATH_LOGCAT); 41 | if (!file.exists()) { 42 | file.mkdirs(); 43 | } 44 | } 45 | 46 | public static LogcatHelper getInstance(Context context) { 47 | if (INSTANCE == null) { 48 | INSTANCE = new LogcatHelper(context); 49 | } 50 | return INSTANCE; 51 | } 52 | 53 | private LogcatHelper(Context context) { 54 | init(context); 55 | mPId = android.os.Process.myPid(); 56 | } 57 | 58 | public void start() { 59 | if (mLogDumper == null) 60 | mLogDumper = new LogDumper(String.valueOf(mPId), PATH_LOGCAT); 61 | mLogDumper.start(); 62 | } 63 | 64 | public void stop() { 65 | if (mLogDumper != null) { 66 | mLogDumper.stopLogs(); 67 | mLogDumper = null; 68 | } 69 | } 70 | 71 | private class LogDumper extends Thread { 72 | 73 | private Process logcatProc; 74 | private BufferedReader mReader = null; 75 | private boolean mRunning = true; 76 | String cmds = null; 77 | private String mPID; 78 | private FileOutputStream out = null; 79 | 80 | public LogDumper(String pid, String dir) { 81 | mPID = pid; 82 | try { 83 | out = new FileOutputStream(new File(dir, "EDOG-" + MyDate.getFileName() + ".log")); 84 | } catch (FileNotFoundException e) { 85 | e.printStackTrace(); 86 | } 87 | 88 | /** 89 | * 90 | * 日志等级:*:v , *:d , *:w , *:e , *:f , *:s 91 | * 92 | * 显示当前mPID程序的 E和W等级的日志. 93 | * 94 | * */ 95 | 96 | // cmds = "logcat *:e *:w | grep \"(" + mPID + ")\""; 97 | cmds = "logcat | grep \"(" + mPID + ")\"";//打印所有日志信息 98 | // cmds = "logcat -s way";//打印标签过滤信息 99 | // cmds = "logcat *:v *:d *:w *:e *:f *:s *:i | grep \"(" + mPID + ")\""; 100 | 101 | } 102 | 103 | public void stopLogs() { 104 | mRunning = false; 105 | } 106 | 107 | @Override 108 | public void run() { 109 | try { 110 | logcatProc = Runtime.getRuntime().exec(cmds); 111 | mReader = new BufferedReader(new InputStreamReader(logcatProc.getInputStream()), 1024); 112 | String line = null; 113 | while (mRunning && (line = mReader.readLine()) != null) { 114 | if (!mRunning) { 115 | break; 116 | } 117 | if (line.length() == 0) { 118 | continue; 119 | } 120 | if (out != null && line.contains(mPID)) { 121 | out.write((MyDate.getDateEN() + " " + line + "\n").getBytes()); 122 | } 123 | } 124 | 125 | } catch (IOException e) { 126 | e.printStackTrace(); 127 | } finally { 128 | if (logcatProc != null) { 129 | logcatProc.destroy(); 130 | logcatProc = null; 131 | } 132 | if (mReader != null) { 133 | try { 134 | mReader.close(); 135 | mReader = null; 136 | } catch (IOException e) { 137 | e.printStackTrace(); 138 | } 139 | } 140 | if (out != null) { 141 | try { 142 | out.close(); 143 | } catch (IOException e) { 144 | e.printStackTrace(); 145 | } 146 | out = null; 147 | } 148 | 149 | } 150 | 151 | } 152 | 153 | } 154 | 155 | public static class MyDate { 156 | public static String getFileName() { 157 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); 158 | String date = format.format(new Date(System.currentTimeMillis())); 159 | return date;// 2012年10月03日 23:41:31 160 | } 161 | 162 | public static String getDateEN() { 163 | SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 164 | String date1 = format1.format(new Date(System.currentTimeMillis())); 165 | return date1;// 2012-10-03 23:41:31 166 | } 167 | 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/utils/MediaPlayerProxy.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy.utils; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.ServerSocket; 6 | import java.net.Socket; 7 | import java.net.SocketTimeoutException; 8 | import java.net.UnknownHostException; 9 | import java.util.StringTokenizer; 10 | 11 | import org.apache.http.client.methods.HttpGet; 12 | import org.apache.http.client.methods.HttpUriRequest; 13 | 14 | import android.util.Log; 15 | 16 | import com.bedpotato.musicplayerproxy.Constants; 17 | 18 | /** 19 | * 代理 20 | * 21 | * @author 阿伦 22 | * 23 | */ 24 | public class MediaPlayerProxy implements Runnable { 25 | private static final String LOG_TAG = MediaPlayerProxy.class.getSimpleName(); 26 | 27 | private int port; 28 | 29 | private ServerSocket socket; 30 | 31 | private Thread thread; 32 | 33 | private boolean isRunning = true; 34 | 35 | protected static RequestDealThread downloadThread; 36 | 37 | /** 38 | * 创建ServerSocket,使用自动分配端口 39 | */ 40 | public void init() { 41 | try { 42 | socket = new ServerSocket(port, 0, InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 })); 43 | socket.setSoTimeout(5000); 44 | port = socket.getLocalPort(); 45 | Log.d(LOG_TAG, "port " + port + " obtained"); 46 | } catch (UnknownHostException e) { 47 | Log.e(LOG_TAG, "Error initializing server", e); 48 | } catch (IOException e) { 49 | Log.e(LOG_TAG, "Error initializing server", e); 50 | } 51 | } 52 | 53 | public void start() { 54 | if (socket == null) { 55 | throw new IllegalStateException("Cannot start proxy; it has not been initialized."); 56 | } 57 | thread = new Thread(this); 58 | thread.start(); 59 | } 60 | 61 | public void stop() { 62 | isRunning = false; 63 | if (thread == null) { 64 | throw new IllegalStateException("Cannot stop proxy; it has not been started."); 65 | } 66 | thread.interrupt(); 67 | try { 68 | thread.join(5000); 69 | } catch (InterruptedException e) { 70 | e.printStackTrace(); 71 | } 72 | Log.d(LOG_TAG, "stop"); 73 | } 74 | 75 | public String getProxyURL(String url) { 76 | return String.format("http://127.0.0.1:%d/%s", port, url); 77 | } 78 | 79 | @Override 80 | public void run() { 81 | Log.d(LOG_TAG, "running"); 82 | while (isRunning) { 83 | try { 84 | final Socket client = socket.accept(); 85 | if (client == null) { 86 | continue; 87 | } 88 | Log.d(LOG_TAG, "client connected"); 89 | HttpUriRequest request = readRequest(client); 90 | if (request != null) { 91 | downloadThread = new RequestDealThread(request, client); 92 | downloadThread.start(); 93 | } 94 | } catch (SocketTimeoutException e) { 95 | // Do nothing 96 | } catch (IOException e) { 97 | Log.e(LOG_TAG, "Error connecting to client", e); 98 | } 99 | } 100 | Log.d(LOG_TAG, "Proxy interrupted. Shutting down."); 101 | } 102 | 103 | private HttpUriRequest readRequest(Socket client) { 104 | // 得到Request String 105 | HttpUriRequest request = null; 106 | int bytes_read; 107 | byte[] local_request = new byte[1024]; 108 | String requestStr = ""; 109 | try { 110 | while ((bytes_read = client.getInputStream().read(local_request)) != -1) { 111 | byte[] tmpBuffer = new byte[bytes_read]; 112 | System.arraycopy(local_request, 0, tmpBuffer, 0, bytes_read); 113 | String str = new String(tmpBuffer); 114 | Log.i(LOG_TAG + " Header-> ", str); 115 | requestStr = requestStr + str; 116 | if (requestStr.contains("GET") && requestStr.contains(Constants.HTTP_END)) { 117 | break; 118 | } 119 | } 120 | } catch (IOException e) { 121 | Log.e(LOG_TAG, "获取Request Header异常", e); 122 | return request; 123 | } 124 | 125 | if (requestStr == "") { 126 | Log.i(LOG_TAG, "请求头为空,获取异常"); 127 | return request; 128 | } 129 | 130 | // 将Request String组合为HttpUriRequest 131 | String[] requestParts = requestStr.split(Constants.LINE_BREAK); 132 | StringTokenizer st = new StringTokenizer(requestParts[0]); 133 | String method = st.nextToken(); 134 | String uri = st.nextToken(); 135 | 136 | Log.d(LOG_TAG + " URL-> ", uri); 137 | request = new HttpGet(uri.substring(1)); 138 | for (int i = 1; i < requestParts.length; i++) { 139 | int separatorLocation = requestParts[i].indexOf(":"); 140 | String name = requestParts[i].substring(0, separatorLocation).trim(); 141 | String value = requestParts[i].substring(separatorLocation + 1).trim(); 142 | // 不添加Host Header,因为URL的Host为127.0.0.1 143 | if (!name.equals(Constants.HOST)) { 144 | request.addHeader(name, value); 145 | } 146 | } 147 | // 如果没有Range,统一添加默认Range,方便后续处理 148 | if (request.getFirstHeader(Constants.RANGE) == null) { 149 | request.addHeader(Constants.RANGE, Constants.RANGE_PARAMS_0); 150 | } 151 | return request; 152 | } 153 | } -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/utils/ProxyFileUtils.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.IOException; 6 | import java.io.RandomAccessFile; 7 | import java.net.URI; 8 | 9 | import android.util.Log; 10 | 11 | import com.bedpotato.musicplayerproxy.Constants; 12 | import com.bedpotato.musicplayerproxy.db.CacheFileInfoDao; 13 | 14 | public class ProxyFileUtils { 15 | private static final String LOG_TAG = ProxyFileUtils.class.getSimpleName(); 16 | 17 | private static ProxyFileUtils fileUtilsByMediaPlayer; 18 | private static ProxyFileUtils fileUtilsByPreLoader; 19 | 20 | public static ProxyFileUtils getInstance(URI uri, boolean isUseByMediaPlayer) { 21 | // 如果文件名有错误,返回空Utils 22 | String name = getValidFileName(uri); 23 | if (name == null || name.length() <= 0) { 24 | ProxyFileUtils utils = new ProxyFileUtils(null); 25 | utils.setEnableFalse(); 26 | return utils; 27 | } 28 | // 如果是预加载,如果MediaPlayer正在使用文件,则不预加载,返回空Utils 29 | if (!isUseByMediaPlayer) { 30 | if (fileUtilsByMediaPlayer != null && fileUtilsByMediaPlayer.getFileName().equals(name)) { 31 | ProxyFileUtils utils = new ProxyFileUtils(null); 32 | utils.setEnableFalse(); 33 | return utils; 34 | } 35 | } 36 | // 创建Utils,关闭之前Util。如果是MediaPlayer使用文件Preloader也在使用,则关闭Preloader的Utils 37 | if (isUseByMediaPlayer) { 38 | if (fileUtilsByPreLoader != null && fileUtilsByPreLoader.getFileName().equals(name)) { 39 | close(fileUtilsByPreLoader, false); 40 | } 41 | close(fileUtilsByMediaPlayer, true); 42 | fileUtilsByMediaPlayer = new ProxyFileUtils(name); 43 | return fileUtilsByMediaPlayer; 44 | } else { 45 | close(fileUtilsByPreLoader, false); 46 | fileUtilsByPreLoader = new ProxyFileUtils(name); 47 | return fileUtilsByPreLoader; 48 | } 49 | } 50 | 51 | private boolean isEnable; 52 | 53 | private FileOutputStream outputStream; 54 | private RandomAccessFile randomAccessFile; 55 | private File file; 56 | 57 | /** 58 | * 判断存储是否可用 59 | * 60 | * @return 61 | */ 62 | private boolean isSdAvaliable() { 63 | // 判断外部存储器是否可用 64 | File dir = new File(Constants.DOWNLOAD_PATH); 65 | if (!dir.exists()) { 66 | dir.mkdirs(); 67 | if(!dir.exists()){ 68 | return false; 69 | } 70 | } 71 | // 删除部分缓存文件 72 | ProxyUtils.asynRemoveBufferFile(Constants.CACHE_FILE_NUMBER); 73 | // 可用空间大小是否大于SD卡预留最小值 74 | long freeSize = ProxyUtils.getAvailaleSize(Constants.DOWNLOAD_PATH); 75 | if (freeSize > Constants.SD_REMAIN_SIZE) { 76 | return true; 77 | } else { 78 | 79 | return false; 80 | } 81 | } 82 | 83 | private ProxyFileUtils(String name) { 84 | if (name == null || name.length() <= 0) { 85 | isEnable = false; 86 | return; 87 | } 88 | 89 | if (!isSdAvaliable()) { 90 | isEnable = false; 91 | return; 92 | } 93 | 94 | try { 95 | file = new File(Constants.DOWNLOAD_PATH + name); 96 | if (!file.exists()) { 97 | File dir = new File(Constants.DOWNLOAD_PATH); 98 | dir.mkdirs(); 99 | file.createNewFile(); 100 | } 101 | randomAccessFile = new RandomAccessFile(file, "r"); 102 | outputStream = new FileOutputStream(file, true); 103 | isEnable = true; 104 | } catch (IOException e) { 105 | isEnable = false; 106 | Log.e(LOG_TAG, "文件操作失败,无SD?", e); 107 | } 108 | } 109 | 110 | public String getFileName() { 111 | return file.getName(); 112 | } 113 | 114 | public boolean isEnable() { 115 | return isEnable; 116 | } 117 | 118 | public void setEnableFalse() { 119 | isEnable = false; 120 | } 121 | 122 | public int getLength() { 123 | if (isEnable) { 124 | return (int) file.length(); 125 | } else { 126 | return -1; 127 | } 128 | } 129 | 130 | public boolean delete() { 131 | return file.delete(); 132 | } 133 | 134 | public byte[] read(int startPos) { 135 | if (isEnable) { 136 | int byteCount = (int) (getLength() - startPos); 137 | byte[] tmp = new byte[byteCount]; 138 | try { 139 | randomAccessFile.seek(startPos); 140 | randomAccessFile.read(tmp); 141 | return tmp; 142 | } catch (IOException e) { 143 | Log.e(LOG_TAG, "缓存读取失败", e); 144 | return null; 145 | } 146 | } else { 147 | return null; 148 | } 149 | } 150 | 151 | public byte[] read(int startPos, int length) { 152 | if (isEnable) { 153 | int byteCount = (int) (getLength() - startPos); 154 | if (byteCount > length) { 155 | byteCount = length; 156 | } 157 | byte[] tmp = new byte[byteCount]; 158 | try { 159 | randomAccessFile.seek(startPos); 160 | randomAccessFile.read(tmp); 161 | return tmp; 162 | } catch (IOException e) { 163 | Log.e(LOG_TAG, "缓存读取失败", e); 164 | return null; 165 | } 166 | } else { 167 | return null; 168 | } 169 | } 170 | 171 | public void write(byte[] buffer, int byteCount) { 172 | if (isEnable) { 173 | try { 174 | outputStream.write(buffer, 0, byteCount); 175 | outputStream.flush(); 176 | } catch (IOException e) { 177 | Log.e(LOG_TAG, "缓存写入失败", e); 178 | } 179 | } 180 | } 181 | 182 | public static void close(ProxyFileUtils fileUtils, boolean isUseByMediaPlayer) { 183 | if (isUseByMediaPlayer) { 184 | if (fileUtilsByMediaPlayer != null && fileUtilsByMediaPlayer == fileUtils) { 185 | fileUtilsByMediaPlayer.setEnableFalse(); 186 | fileUtilsByMediaPlayer = null; 187 | } 188 | } else { 189 | if (fileUtilsByPreLoader != null && fileUtilsByPreLoader == fileUtils) { 190 | fileUtilsByPreLoader.setEnableFalse(); 191 | fileUtilsByPreLoader = null; 192 | } 193 | } 194 | } 195 | 196 | public static void deleteFile(String url) { 197 | URI uri = URI.create(url); 198 | String name = getValidFileName(uri); 199 | boolean isSuccess = new File(name).delete(); 200 | if (isSuccess) { 201 | CacheFileInfoDao.getInstance().delete(name); 202 | } 203 | } 204 | 205 | public static boolean isFinishCache(String url) { 206 | URI uri = URI.create(url); 207 | String name = getValidFileName(uri); 208 | File f = new File(name); 209 | 210 | if (!f.exists()) { 211 | return false; 212 | } 213 | if (f.length() != CacheFileInfoDao.getInstance().getFileSize(name)) { 214 | return false; 215 | } 216 | return true; 217 | } 218 | 219 | /** 220 | * 获取有效的文件名 221 | * 222 | * @param url 223 | * @return 224 | */ 225 | static protected String getValidFileName(URI uri) { 226 | String path = uri.getRawPath(); 227 | String name = path.substring(path.lastIndexOf("/")); 228 | name = name.replace("\\", ""); 229 | name = name.replace("/", ""); 230 | name = name.replace(":", ""); 231 | name = name.replace("*", ""); 232 | name = name.replace("?", ""); 233 | name = name.replace("\"", ""); 234 | name = name.replace("<", ""); 235 | name = name.replace(">", ""); 236 | name = name.replace("|", ""); 237 | name = name.replace(" ", "_"); // 前面的替换会产生空格,最后将其一并替换掉 238 | return name; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/utils/ProxyUtils.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy.utils; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.Comparator; 7 | import java.util.List; 8 | 9 | import android.os.StatFs; 10 | 11 | import com.bedpotato.musicplayerproxy.Constants; 12 | 13 | public class ProxyUtils { 14 | private static final String LOG_TAG = ProxyUtils.class.getSimpleName(); 15 | 16 | /** 17 | * 删除多余的缓存文件 18 | * 19 | * @param dirPath 20 | * 缓存文件的文件夹路径 21 | * @param maximun 22 | * 缓存文件的最大数量 23 | */ 24 | static protected void asynRemoveBufferFile(final int maximun) { 25 | new Thread() { 26 | public void run() { 27 | List lstBufferFile = getFilesSortByDate(Constants.DOWNLOAD_PATH); 28 | while (lstBufferFile.size() > maximun) { 29 | lstBufferFile.get(0).delete(); 30 | lstBufferFile.remove(0); 31 | } 32 | } 33 | }.start(); 34 | } 35 | 36 | /** 37 | * 获取外部存储器可用的空间 38 | * 39 | * @return 40 | */ 41 | static protected long getAvailaleSize(String dir) { 42 | StatFs stat = new StatFs(dir);// path.getPath()); 43 | long totalBlocks = stat.getBlockCount();// 获取block数量 44 | long blockSize = stat.getBlockSize(); 45 | long availableBlocks = stat.getAvailableBlocks(); 46 | return availableBlocks * blockSize; // 获取可用大小 47 | } 48 | 49 | /** 50 | * 获取文件夹内的文件,按日期排序,从旧到新 51 | * 52 | * @param dirPath 53 | * @return 54 | */ 55 | static private List getFilesSortByDate(String dirPath) { 56 | List result = new ArrayList(); 57 | File dir = new File(dirPath); 58 | File[] files = dir.listFiles(); 59 | if (files == null || files.length == 0) 60 | return result; 61 | 62 | Arrays.sort(files, new Comparator() { 63 | public int compare(File f1, File f2) { 64 | return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified()); 65 | } 66 | }); 67 | 68 | for (int i = 0; i < files.length; i++) { 69 | result.add(files[i]); 70 | } 71 | return result; 72 | } 73 | 74 | public static String getExceptionMessage(Exception ex) { 75 | String result = ""; 76 | StackTraceElement[] stes = ex.getStackTrace(); 77 | for (int i = 0; i < stes.length; i++) { 78 | result = result + stes[i].getClassName() + "." + stes[i].getMethodName() + " " + stes[i].getLineNumber() 79 | + "line" + "\r\n"; 80 | } 81 | return result; 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/com/bedpotato/musicplayerproxy/utils/RequestDealThread.java: -------------------------------------------------------------------------------- 1 | package com.bedpotato.musicplayerproxy.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.net.Socket; 6 | import java.net.SocketException; 7 | 8 | import org.apache.http.Header; 9 | import org.apache.http.HttpResponse; 10 | import org.apache.http.client.methods.HttpUriRequest; 11 | 12 | import android.util.Log; 13 | 14 | import com.bedpotato.musicplayerproxy.Constants; 15 | import com.bedpotato.musicplayerproxy.db.CacheFileInfoDao; 16 | 17 | public class RequestDealThread extends Thread { 18 | private static final String LOG_TAG = RequestDealThread.class.getSimpleName(); 19 | 20 | Socket client; 21 | HttpUriRequest request; 22 | 23 | ProxyFileUtils fileUtils; 24 | /** MediaPlayer发出的原始请求Range Start */ 25 | private int originRangeStart; 26 | /** 和本地缓存判断后,需要发出的请求Range Start */ 27 | private long realRangeStart; 28 | 29 | CacheFileInfoDao cacheDao; 30 | 31 | public RequestDealThread(HttpUriRequest request, Socket client) { 32 | this.request = request; 33 | this.client = client; 34 | cacheDao = CacheFileInfoDao.getInstance(); 35 | } 36 | 37 | @Override 38 | public void run() { 39 | try { 40 | fileUtils = ProxyFileUtils.getInstance(request.getURI(), true); 41 | processRequest(request, client); 42 | } catch (IllegalStateException e) { 43 | e.printStackTrace(); 44 | } catch (IOException e) { 45 | e.printStackTrace(); 46 | } 47 | } 48 | 49 | private int getRangeStart(HttpUriRequest request) { 50 | Header rangeHeader = request.getFirstHeader(Constants.RANGE); 51 | if (rangeHeader != null) { 52 | String value = rangeHeader.getValue(); 53 | return Integer.valueOf(value.substring(value.indexOf("bytes=") + 6, value.indexOf("-"))); 54 | } 55 | return 0; 56 | } 57 | 58 | /** 59 | * 伪造Response Header,发送缓存内容 60 | * 61 | * @param rangeStart 62 | * 数据起始位置(如果从头开始则为0) 63 | * @param rangeEnd 64 | * 数据截止位置(一般为缓存长度-1) 65 | * @param fileLength 66 | * 缓存文件长度 67 | * @param audioCache 68 | * 缓存内容 69 | * @throws IOException 70 | */ 71 | private void sendLocalHeaderAndCache(int rangeStart, int rangeEnd, int fileLength, byte[] audioCache) 72 | throws IOException { 73 | // 返回MediaPlayer Header信息 74 | String httpString = HttpUtils.genResponseHeader(rangeStart, rangeEnd, fileLength); 75 | byte[] httpHeader = httpString.toString().getBytes(); 76 | client.getOutputStream().write(httpHeader, 0, httpHeader.length); 77 | // 返回Content 78 | if (audioCache != null && audioCache.length > 0) { 79 | client.getOutputStream().write(audioCache, 0, audioCache.length); 80 | } 81 | } 82 | 83 | private void processRequest(HttpUriRequest request, Socket client) throws IllegalStateException, IOException { 84 | if (request == null) { 85 | return; 86 | } 87 | 88 | try { 89 | byte[] audioCache = null; 90 | // 得到MediaPlayer原始请求Range起始 91 | originRangeStart = getRangeStart(request); 92 | Log.i(LOG_TAG, "原始请求Range起始值:" + originRangeStart + " 本地缓存长度:" + fileUtils.getLength()); 93 | // 缓存的文件大小 94 | int cacheFileSize = cacheDao.getFileSize(fileUtils.getFileName()); 95 | /* 96 | * 如果缓存完成,无需发送请求,本地缓存返回MediaPlayer。 97 | */ 98 | if (fileUtils.isEnable() && fileUtils.getLength() == cacheFileSize) { 99 | audioCache = fileUtils.read(originRangeStart, Constants.AUDIO_BUFFER_MAX_LENGTH); 100 | sendLocalHeaderAndCache(originRangeStart, cacheFileSize - 1, cacheFileSize, audioCache); 101 | return; 102 | } 103 | // TODO 这里可能需要网络判断 104 | /* 105 | * 请求Range起始值和本地缓存比对。如果有缓存,得到缓存内容,修改Range。 如果没有缓存,则Range不变。 106 | */ 107 | if (fileUtils.isEnable() && originRangeStart < fileUtils.getLength()) { 108 | audioCache = fileUtils.read(originRangeStart, Constants.AUDIO_BUFFER_MAX_LENGTH); 109 | Log.i(LOG_TAG, "本地已缓存长度(跳过):" + audioCache.length); 110 | // 得到需要发送请求Range Start(本地缓存结尾位置+1=缓存长度) 111 | realRangeStart = fileUtils.getLength(); 112 | // 替换请求Header 113 | request.removeHeaders(Constants.RANGE); 114 | request.addHeader(Constants.RANGE, Constants.RANGE_PARAMS + realRangeStart + "-"); 115 | } else { 116 | realRangeStart = originRangeStart; 117 | } 118 | // 缓存是否已经到最大值(如果缓存已经到最大值,则只需要返回缓存) 119 | boolean isCacheEnough = (audioCache != null && audioCache.length == Constants.AUDIO_BUFFER_MAX_LENGTH) ? true 120 | : false; 121 | 122 | /* 123 | * 如果缓存足够,且本地有文件长度,则直接发送缓存,不发送请求。。。。。。。。。。。。。。。。。。。 124 | * 如果缓存足够,本地没有文件长度,则发送请求,使用ResponseHeader,返回缓存,!不接收ResponseContent 125 | * 如果缓存不足,则发送请求,使用ResponseHeader,返回缓存,!返回Response Content 126 | */ 127 | // 缓存足够&&有文件大小 128 | if (isCacheEnough && cacheFileSize > 0) { 129 | sendLocalHeaderAndCache(originRangeStart, cacheFileSize - 1, cacheFileSize, audioCache); 130 | } 131 | // 缓存不够。或者数据库没有文件大小 132 | else { 133 | HttpResponse realResponse = null; 134 | /* 135 | * 返回Header和Cache 136 | */ 137 | // 如果数据库没有存文件大小,则获取(处理数据库没有文件大小的情况) 138 | if (cacheFileSize <= 0) { 139 | Log.d(LOG_TAG, "数据库未包含文件大小,发送请求"); 140 | realResponse = HttpUtils.send(request); 141 | if (realResponse == null) { 142 | return; 143 | } 144 | cacheFileSize = getContentLength(realResponse); 145 | } 146 | sendLocalHeaderAndCache(originRangeStart, cacheFileSize - 1, cacheFileSize, audioCache); 147 | // 如果缓存不足,返回Response Content(处理缓存不足的情况) 148 | if (realResponse == null) { 149 | Log.d(LOG_TAG, "缓存不足,发送请求"); 150 | realResponse = HttpUtils.send(request); 151 | if (realResponse == null) { 152 | return; 153 | } 154 | } 155 | Log.d(LOG_TAG, "接收ResponseContent"); 156 | InputStream data = realResponse.getEntity().getContent(); 157 | if (!isCacheEnough) { 158 | byte[] buff = new byte[1024 * 40]; 159 | boolean isPrint = true; 160 | int fileLength = 0; 161 | int readBytes; 162 | while (Thread.currentThread() == MediaPlayerProxy.downloadThread 163 | && (readBytes = data.read(buff, 0, buff.length)) != -1) { 164 | long fileBufferLocation = fileLength + realRangeStart; 165 | fileLength += readBytes; 166 | long fileBufferEndLocation = fileLength + realRangeStart; 167 | // 保存文件 168 | if (fileUtils.getLength() == fileBufferLocation) { 169 | fileUtils.write(buff, readBytes); 170 | } 171 | // 打印缓存大小 172 | if (System.currentTimeMillis() / 1000 % 2 == 0) { 173 | if (isPrint) { 174 | Log.d(LOG_TAG, "Cache Size:" + readBytes + " File Start:" + fileBufferLocation 175 | + "File End:" + fileBufferEndLocation); 176 | isPrint = false; 177 | } 178 | } else { 179 | isPrint = true; 180 | } 181 | client.getOutputStream().write(buff, 0, readBytes); 182 | } 183 | } 184 | } 185 | } catch (SocketException e) { 186 | Log.i(LOG_TAG, "连接被终止", e); 187 | } catch (Exception e) { 188 | Log.e(LOG_TAG, e.getMessage(), e); 189 | } finally { 190 | client.close(); 191 | Log.i(LOG_TAG, "代理关闭"); 192 | } 193 | } 194 | 195 | /** 196 | * 得到Content大小 197 | * 198 | * @param response 199 | * @return 200 | */ 201 | private int getContentLength(HttpResponse response) { 202 | int contentLength = 0; 203 | Header header = response.getFirstHeader(Constants.CONTENT_RANGE); 204 | if (header != null) { 205 | String range = header.getValue(); 206 | contentLength = Integer.valueOf(range.substring(range.indexOf("-") + 1, range.indexOf("/"))) + 1; 207 | } else { 208 | header = response.getFirstHeader(Constants.CONTENT_LENGTH); 209 | if (header != null) { 210 | contentLength = Integer.valueOf(header.getValue()); 211 | } 212 | } 213 | if (contentLength != 0) { 214 | cacheDao.insertOrUpdate(fileUtils.getFileName(), contentLength); 215 | } 216 | return contentLength; 217 | } 218 | } 219 | --------------------------------------------------------------------------------