編輯:Android開發實例
本文是在 Android實現MediaPlayer與Http Proxy結合使用示例(二) 基礎上,進一步優化代理服務器,支持了Http的302、301重定向,獲取Http Request和Http Response的文本內容。本文以視頻播放結合Http Proxy,講述MediaPlayer播放過程中的握手過程。
吐槽一下:用google搜索“代理服務器”無效,所以本文用Media Proxy,大家懂的......
先來看看本文程序運行的截屏動畫:
再來看看程序運行時輸出的Log信息.....這裡是關鍵:
07-29 15:51:30.692: E/HttpGetProxy(449): ..........sckPlayer connected.......... MediaPlayer發出請求
07-29 15:51:30.692: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1
07-29 15:51:30.692: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)
07-29 15:51:30.692: E/to Media Server---->(449): Host: daily3gp.com
07-29 15:51:30.692: E/to Media Server---->(449):
07-29 15:51:31.119: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:31.122: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:31.775: E/from Media Server---->(449): HTTP/1.1 200 OK
07-29 15:51:31.775: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:33 GMT
07-29 15:51:31.775: E/from Media Server---->(449): Server: Apache
07-29 15:51:31.775: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT
07-29 15:51:31.775: E/from Media Server---->(449): Accept-Ranges: bytes
07-29 15:51:31.775: E/from Media Server---->(449): Content-Length: 754777
07-29 15:51:31.775: E/from Media Server---->(449): Content-Type: video/3gpp
07-29 15:51:34.512: E/HttpGetProxy(449): ..........sckPlayer connected..........MediaPlayer發出請求
07-29 15:51:34.532: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1
07-29 15:51:34.532: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)
07-29 15:51:34.532: E/to Media Server---->(449): Host: daily3gp.com
07-29 15:51:34.532: E/to Media Server---->(449): Range: bytes=720896-
07-29 15:51:34.532: E/to Media Server---->(449):
07-29 15:51:34.873: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:34.873: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:35.505: E/from Media Server---->(449): HTTP/1.1 206 Partial Content
07-29 15:51:35.505: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:36 GMT
07-29 15:51:35.505: E/from Media Server---->(449): Server: Apache
07-29 15:51:35.505: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT
07-29 15:51:35.505: E/from Media Server---->(449): Accept-Ranges: bytes
07-29 15:51:35.505: E/from Media Server---->(449): Content-Length: 33881
07-29 15:51:35.505: E/from Media Server---->(449): Content-Range: bytes 720896-754776/754777
07-29 15:51:35.505: E/from Media Server---->(449): Content-Type: video/3gpp
07-29 15:51:38.754: E/HttpGetProxy(449): ..........over..........
07-29 15:51:51.461: E/HttpGetProxy(449): ..........sckPlayer connected..........MediaPlayer發出請求
07-29 15:51:51.471: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1
07-29 15:51:51.471: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)
07-29 15:51:51.471: E/to Media Server---->(449): Host: daily3gp.com
07-29 15:51:51.471: E/to Media Server---->(449): Range: bytes=196608-
07-29 15:51:51.471: E/to Media Server---->(449):
07-29 15:51:51.722: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:51.722: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:52.285: E/from Media Server---->(449): HTTP/1.1 206 Partial Content
07-29 15:51:52.285: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:53 GMT
07-29 15:51:52.285: E/from Media Server---->(449): Server: Apache
07-29 15:51:52.285: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT
07-29 15:51:52.285: E/from Media Server---->(449): Accept-Ranges: bytes
07-29 15:51:52.285: E/from Media Server---->(449): Content-Length: 558169
07-29 15:51:52.285: E/from Media Server---->(449): Content-Range: bytes 196608-754776/754777
07-29 15:51:52.285: E/from Media Server---->(449): Content-Type: video/3gpp
07-29 15:51:54.812: E/HttpGetProxy(449): ..........sckPlayer connected..........MediaPlayer發出請求
07-29 15:51:54.822: E/to Media Server---->(449): GET /vids/family_guy_penis_car.3gp HTTP/1.1
07-29 15:51:54.822: E/to Media Server---->(449): User-Agent: stagefright/1.0 (Linux;Android 2.2)
07-29 15:51:54.822: E/to Media Server---->(449): Host: daily3gp.com
07-29 15:51:54.822: E/to Media Server---->(449): Range: bytes=589824-
07-29 15:51:54.822: E/to Media Server---->(449):
07-29 15:51:55.117: E/HttpGetProxy(449): ..........remote Server connected..........
07-29 15:51:55.117: E/HttpGetProxy(449): ..........remote start to receive..........
07-29 15:51:55.693: E/from Media Server---->(449): HTTP/1.1 206 Partial Content
07-29 15:51:55.693: E/from Media Server---->(449): Date: Sun, 29 Jul 2012 15:51:57 GMT
07-29 15:51:55.693: E/from Media Server---->(449): Server: Apache
07-29 15:51:55.693: E/from Media Server---->(449): Last-Modified: Thu, 14 Jan 2010 23:29:02 GMT
07-29 15:51:55.693: E/from Media Server---->(449): Accept-Ranges: bytes
07-29 15:51:55.693: E/from Media Server---->(449): Content-Length: 164953
07-29 15:51:55.693: E/from Media Server---->(449): Content-Range: bytes 589824-754776/754777
07-29 15:51:55.693: E/from Media Server---->(449): Content-Type: video/3gpp
07-29 15:51:59.620: E/HttpGetProxy(449): ..........over..........
從截屏動畫和Log信息看出,手動seek一次,但MediaPlayer進行了多次Range請求,這說明了“MediaPlayer會自動seek”,或許與MediaPlayer本地緩存有關。另外,不同硬解廠家實現的MediaPlayer估計會有不同的操作。
本文的代碼可以到這裡下載:
http://download.csdn.net/detail/hellogv/4463651
HttpGetProxy.JAVA還是本文的關鍵部分:
- public class HttpGetProxy {
- final static private String TAG = "HttpGetProxy";
- final static private String LOCAL_IP_ADDRESS_1 = "127.0.0.1";
- final static private String LOCAL_IP_ADDRESS_2 = "10.0.2.2";
- final static private int HTTP_PORT = 80;
- final static private String HTTP_END="\r\n\r\n";
- /**代理服務器使用的端口*/
- private int proxy_ip_port;
- /**鏈接帶的端口*/
- private String original_ip_port;
- /**遠程服務器地址*/
- private String remoteHost;
- /**本地服務器地址*/
- private String localHost;
- private ServerSocket localServer = null;
- /**收發Media Player請求的Socket*/
- private Socket sckPlayer = null;
- /**收發Media Server請求的Socket*/
- private Socket sckServer = null;
- private SocketAddress address;
- /**
- * 初始化代理服務器
- * @param localport 代理服務器監聽的端口
- */
- public HttpGetProxy(int localport) {
- try {
- _HttpGetProxy(LOCAL_IP_ADDRESS_1,localport);
- } catch (Exception e) {
- Log.e(TAG,LOCAL_IP_ADDRESS_1+"???"+e.toString());
- try {
- _HttpGetProxy(LOCAL_IP_ADDRESS_2,localport);
- }catch (Exception e1) {
- Log.e(TAG,LOCAL_IP_ADDRESS_2+"???"+e.toString());
- System.exit(0);
- }
- }
- }
- private void _HttpGetProxy(String ipAddress,int localport) throws UnknownHostException, IOException {
- proxy_ip_port=localport;
- localServer = new ServerSocket(localport,1,InetAddress.getByName(ipAddress));
- localHost=ipAddress;
- }
- /**
- * 把網絡URL轉為本地URL,127.0.0.1替換網絡域名
- * @param url 網絡URL
- * @return 本地URL
- */
- public String getLocalURL(String urlString){
- //----排除HTTP特殊----//
- String targetUrl=ProxyUtils.getRedirectUrl(urlString);
- //----獲取對應本地代理服務器的鏈接----//
- String result = null;
- URI originalURI=URI.create(targetUrl);
- remoteHost=originalURI.getHost();
- if(originalURI.getPort()!=-1){//URL帶Port
- address = new InetSocketAddress(remoteHost,originalURI.getPort());//使用默認端口
- original_ip_port = originalURI.getPort()+"";//保存端口,中轉時替換
- result=targetUrl.replace(remoteHost+":"+originalURI.getPort(),
- localHost+":"+proxy_ip_port);
- }
- else{//URL不帶Port
- address = new InetSocketAddress(remoteHost,HTTP_PORT);//使用80端口
- original_ip_port = "";
- result=targetUrl.replace(remoteHost,localHost+":"+proxy_ip_port);
- }
- return result;
- }
- /**
- * 啟動代理服務器
- * @throws IOException
- */
- public void asynStartProxy(){
- new Thread() {
- public void run() {
- int bytes_read;
- byte[] local_request = new byte[1024];
- byte[] remote_reply = new byte[1024];
- while (true) {
- try {
- //--------------------------------------
- //監聽MediaPlayer的請求,MediaPlayer->代理服務器
- //--------------------------------------
- sckPlayer = localServer.accept();
- Log.e(TAG, "..........sckPlayer connected..........");
- String requestStr = "";
- while ((bytes_read = sckPlayer.getInputStream().read(local_request)) != -1) {
- byte[] tmpBuffer=new byte[bytes_read];
- System.arraycopy(local_request,0,tmpBuffer,0,bytes_read);
- String str = new String(tmpBuffer);
- //Log.e("from MediaPlayer---->", str);
- requestStr = requestStr + str;
- if (requestStr.contains("GET")
- && requestStr.contains(HTTP_END)) {
- break;
- }
- }
- //把request中的本地ip改為遠程ip
- requestStr = requestStr.replace(localHost,remoteHost);
- //把代理服務器端口改為原URL端口
- if(TextUtils.isEmpty(original_ip_port))
- requestStr = requestStr.replace(":"+proxy_ip_port, "");
- else
- requestStr = requestStr.replace(":"+proxy_ip_port, ":"+original_ip_port);
- Log.e("to Media Server---->", requestStr);
- //--------------------------------------
- //把MediaPlayer的請求發到網絡服務器,代理服務器->網絡服務器
- //--------------------------------------
- sckServer = new Socket();
- sckServer.connect(address);
- Log.e(TAG,"..........remote Server connected..........");
- sckServer.getOutputStream().write(requestStr.getBytes());//發送MediaPlayer的請求
- //------------------------------------------------------
- //把網絡服務器的反饋發到MediaPlayer,網絡服務器->代理服務器->MediaPlayer
- //------------------------------------------------------
- Log.e(TAG,"..........remote start to receive..........");
- String responseStr = "";
- boolean isCaptured=false;
- while ((bytes_read = sckServer.getInputStream().read(remote_reply)) != -1) {
- byte[] tmpBuffer=new byte[bytes_read];
- System.arraycopy(remote_reply,0,tmpBuffer,0,bytes_read);
- //----捕獲收到的Response文本內容----//
- if (!isCaptured) {
- String str = new String(tmpBuffer);
- responseStr += str;
- if (responseStr.contains("HTTP/")
- && responseStr.contains(HTTP_END)) {
- int endIndex=responseStr.indexOf(HTTP_END, 0);
- responseStr=responseStr.substring(0, endIndex);
- Log.e("from Media Server---->", responseStr);
- isCaptured=true;
- }
- }
- sckPlayer.getOutputStream().write(tmpBuffer);
- sckPlayer.getOutputStream().flush();
- }
- Log.e(TAG, "..........over..........");
- //關閉對內,對內 2個SOCKET
- sckPlayer.close();
- sckServer.close();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }.start();
- }
- }
當前比較成熟一點的應用基本上都會在進入應用之顯示一個啟動界面.這個啟動界面或簡單,或復雜,或簡陋,或華麗,用意不同,風格也不同.下面來觀摩幾個流行的應用的啟動界面
ListView與GridView是Android開發中的常用控件,
系統啟動過程圖: Framework層所有的Service都是運行在SystemServer進程中;SystemServer進程是由Zygote進程創
從事Android開發,免不了會在應用裡嵌入一些廣告SDK,在嵌入了眾多SDK後,發現幾乎每個要求在AndroidManifest.xml申明Activity