├── 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 |
29 |
30 |
35 |
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 |
--------------------------------------------------------------------------------