Android從本地伺服器獲取Mp3實現邊下邊播(JavaEE+Tomcat+SQLServer)
實現環境:
1)Lenovo G50-80 Ubuntu16.04筆記本
2)Android Studio
3)Eclipse J2EE
4)Tomcat 8.5
5)sqlServer
6)jdk1.8
概要設計
基於Android平臺的MP3線上播放器的設計與實現的目標為:
(l)採用佈局和元件繪製系統介面。
(2)完成系統中登入介面和播放介面之間的跳轉。
(3)實現介面上端的歌曲資訊展示。
(4)實現線上播放功能。
(4)實現歌曲的上一首和下一首切換。
資料庫表設計
(1) 使用者表Userlogin,管理員資訊表用於存放管理員的相關資訊,結構如表1所示。
(2) 題目表Mp3Info,題目表用於存放使用者答錯的題目,結構如表2所示。
登入模組
執行線上Mp3播放系統,首先進入系統的登入介面。如圖所示:
圖8 登陸介面
登陸時需要判斷使用者名稱和密碼是否正確,首先Android端提出登入請求並將請求帳號和密碼通過Get形式傳送出去,程式碼如下:
public class UserLogin extends AsyncTask<Void,Void,String> { private String account; private String password; UserLogin(String account,String password){ this.account = account; this.password = password; } @Override protected String doInBackground(Void... voids) { String PATH = "http://192.168.43.102:8080/Mp3Manager/login?account="+account+"&password="+password; String result = ""; try { HttpGet get = new HttpGet(new URI(PATH)); HttpClient client = new DefaultHttpClient(); HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { result = EntityUtils.toString(response.getEntity()); Log.v("+++++++++++++++++++", result); }else { Log.v("-------------------", result); } } catch (Exception e) { e.printStackTrace(); } return result; } protected void onPostExecute(String result){ if(result.equals("yes")){ Intent intent = new Intent(mactivity,PlayArea.class); mactivity.startActivity(intent); // Toast.makeText(mactivity, "登入成功", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(mactivity, "登入失敗,請輸入正確的帳號和密碼", Toast.LENGTH_SHORT).show(); } } }
JavaEE後臺收到請求後,通過web.xml篩選後執行UserLogin.java檔案,程式碼如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub String account = request.getParameter("account"); String password = request.getParameter("password"); String sql = "select password from UserCollection where account = '"+account+"'"; DBOper db = new DBOper(); ResultSet rs = db.exeQuery(sql); try { if(rs.next()&&rs.getString(1).equals(password)) { response.getWriter().append("yes"); // System.out.println("yes"); }else { response.getWriter().append("no"); // System.out.println("no"); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
圖9,使用者名稱後臺
5.2 播放音樂模組
登入成功之後進入主介面,如圖所示:
圖10 主介面
此介面分為兩部分,上面的部分是歌曲列表,可以根據歌曲多少自適應調整高度,並且點選之後會顯示出歌曲的詳細資訊;下面部分是播放模組,可進行音樂的播放和暫停,並且還可進行上一曲和下一曲的音樂切換.Android端實現的程式碼如下:
public class myAsyncTask extends AsyncTask<Void, Void, String> {
/**
* 用於非同步下載資料
*/
@Override
protected String doInBackground(Void... arg0) {
// TODO Auto-generated method stub
String result = "";
String PATH = "http://192.168.43.102:8080/Mp3Manager/getMp3Info?";
try {
HttpGet get = new HttpGet(new URI(PATH));
HttpClient client = new DefaultHttpClient();
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
result = EntityUtils.toString(response.getEntity());
// 從後臺獲得的資料去掉空格
result = result.trim();
Log.v("++++++++++", result);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
/**
* 解析result(json格式的字串)並將之存入到lists中
*/
@Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
try {
JSONArray array = new JSONObject(result).getJSONArray("MP3INFO");
for (int i = 0; i < array.length(); i++) {
Mp3Info mp3 = new Mp3Info();
JSONObject object = array.getJSONObject(i);
// Log.v("+++++++++++++++++++++", i+"+++++++++++++++++++");
String id = object.getString("id").toString();
String song = object.getString("song").toString();
String Zsong = object.getString("Zsong").toString();
String singer = object.getString("singer").toString();
String album = object.getString("album").toString();
String notes = object.getString("notes").toString();
mp3.setAlbum(album);
mp3.setId(id);
mp3.setNotes(notes);
mp3.setSong(song);
mp3.setZsong(Zsong);
mp3.setSinger(singer);
lists.add(mp3);
listSong.add(Zsong);
}
lenMp3 = lists.size();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(PlayArea.this,android.R.layout.simple_list_item_1,listSong);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
curMp3 = i;
Mp3Info mp3Info = (Mp3Info)lists.get(i);
String id = mp3Info.getId().toString();
final String song = mp3Info.getSong().toString();
final String Zsong = mp3Info.getZsong().toString();
final String singer = mp3Info.getSinger().toString();
String album = mp3Info.getAlbum().toString();
String notes = mp3Info.getNotes().toString();
new AlertDialog.Builder(PlayArea.this)
.setIcon(R.drawable.title)
.setTitle("歌曲簡介")
.setMessage("歌曲: "+Zsong+"\n\ni d: "+id+"\n\n歌 手: "+singer+"\n\n專 輯: "+album+"\n\n備 注: "+notes)
.setPositiveButton("播放",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
PlayMp3 playMp3 = new PlayMp3();
playMp3.init(song,Zsong,singer);
// Toast.makeText(PlayArea.this, "點選播放 ", Toast.LENGTH_SHORT).show();
}
})
.setNegativeButton("關閉",null)
.show();
}
});
ivMusicPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
PlayMp3 playMp3 = new PlayMp3();
playMp3.init();
}
});
ivMusicPre.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
curMp3--;
if(curMp3<0) curMp3 = lenMp3-1;
Mp3Info mp3Info = (Mp3Info) lists.get(curMp3);
final String song = mp3Info.getSong().toString();
final String Zsong = mp3Info.getZsong().toString();
final String singer = mp3Info.getSinger().toString();
PlayMp3 playMp3 = new PlayMp3();
playMp3.init(song,Zsong,singer);
}
});
ivMusicNext.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
curMp3++;
if(curMp3>=lenMp3) curMp3 = 0;
Mp3Info mp3Info = (Mp3Info) lists.get(curMp3);
final String song = mp3Info.getSong().toString();
final String Zsong = mp3Info.getZsong().toString();
final String singer = mp3Info.getSinger().toString();
PlayMp3 playMp3 = new PlayMp3();
playMp3.init(song,Zsong,singer);
}
});
} catch (Exception e) {
// TODO Auto-generated catch block
Log.v("+++++++++++++++++++++", e.getMessage());
e.printStackTrace();
}
super.onPostExecute(result);
}
}
圖11 點選列表顯示詳情資訊
Android端顯示的資訊是後臺Gson將封裝好的list轉換為String物件傳給Android端解析.程式碼如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
//獲取gson
Gson gson = new Gson();
List<Mp3Info> infos = new ArrayList<>();
//獲取資料庫資訊
DBOper db = new DBOper();
String sql = "select * from Mp3Info";
ResultSet rs = db.exeQuery(sql);
try {
while(rs.next()) {
Mp3Info info = new Mp3Info();
String id = rs.getString(1);
String song = rs.getString(2);
String Zsong = rs.getString(3);
String singer = rs.getString(4);
String album = rs.getString(5);
String notes = rs.getString(6);
info.setId(id);
info.setSong(song);
info.setAlbum(album);
info.setNotes(notes);
info.setZsong(Zsong);
info.setSinger(singer);
infos.add(info);
}
//將infos轉換為json格式的字串
String str = gson.toJson(infos);
request.setAttribute("MP3INFO", "{\"MP3INFO\":"+str+"}");
request.getRequestDispatcher("index.jsp").forward(request, response);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
response.getWriter().append("Served at: ").append(request.getContextPath());
}
圖12 後臺歌曲資訊介面
5.3 播放器資料來源獲取
本系統的最關鍵之處就是從後臺Mp3庫中獲取資料來源交給MediaPlayer.主要想法就是定義兩個Socket:遠端Socket和本地Socket.其中遠端Socket用於請求伺服器資源,本地Socket負責監聽mediaplayer請求,並將遠端Socket獲得資料寫入mediaPlayer中進行播放.
(1)首先初始化本地Socket,程式碼如下:
public MediaPlayerProxy(String writeFileName, boolean writeFile) throws Exception {
currPlayDegree = 0;//當前音樂播放進度
proxyFail = false;//代理播放失敗了
cachedFileLength = 0;//已快取的檔案長度
fileTotalLength = 0;//要快取的檔案總長度
currMusicCachedProgress = 0;//當前的音樂緩衝值(seekbar上的緩衝值)
proxyIdle = false; //代理忙
this.writeFile = writeFile;
this.writeFileName = writeFileName;
try {
if (localServer == null || localServer.isClosed()) {
//建立本地socket伺服器,用來監聽mediaplayer請求和給mediaplayer提供資料
localServer = new ServerSocket();
localServer.setReuseAddress(true);
//建立IP地址為192.168.43.1,埠號為9090的本地埠地址
InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName(LOCAL_IP_ADDRESS), local_ip_port);
//本地socket繫結本地埠地址
localServer.bind(socketAddress);
}
} catch (Exception e) {
Log.e("1111111111111111+++++++",e.getMessage());
try {
local_ip_port--; //埠號非空閒,自減
localServer = new ServerSocket();
localServer.setReuseAddress(true);
//建立IP地址為192.168.43.1,埠號為9090的本地埠地址
InetSocketAddress socketAddress = new InetSocketAddress(InetAddress.getByName(LOCAL_IP_ADDRESS), local_ip_port);
//本地socket繫結本地埠地址
localServer.bind(socketAddress);
} catch (Exception e2) {
Log.e("22222222222222++++++",e2.getMessage());
throw new Exception();
}
}
}
(2)根據真實的請求音原始檔地址,得到本地音原始檔地址將本地音源地址通過setDataSource的方式傳遞給mediaplayer. 前面建立的本地socket物件監聽這個地址,用於獲取mediaplayer的請求資料。程式碼如下:
public String getLocalURLAndSetRemotSocketAddr(String url) {
try {
remotUrl = url;
if (writeFile) { //正在快取的音樂地址
bufferingMusicUrlList.add(remotUrl);
}
String localProxyUrl = "";
final URI originalURI = URI.create(url);
final String remoteHost = originalURI.getHost(); //伺服器主機
if (!TextUtils.isEmpty(remoteHost)) { //存在主機
if (originalURI.getPort() != -1) {//URL帶Port
new Thread(new Runnable() {
@Override
public void run() {
//伺服器的主機號和埠
remoteAddress = new InetSocketAddress(remoteHost, originalURI.getPort());
}
}).start();
//替換
localProxyUrl = url.replace(remoteHost + ":" + originalURI.getPort(), LOCAL_IP_ADDRESS + ":" + local_ip_port);
remoteHostAndPort = remoteHost + ":" + originalURI.getPort();
} else {//URL不帶Port,使用80埠
if (!TextUtils.isEmpty(remoteHost)) {
new Thread(new Runnable() {
@Override
public void run() {
remoteAddress = new InetSocketAddress(remoteHost, HTTP_PORT);//使用80埠
}
}).start();
localProxyUrl = url.replace(remoteHost, LOCAL_IP_ADDRESS + ":" + local_ip_port);
remoteHostAndPort = remoteHost;
}
}
}
return localProxyUrl;
} catch (Exception e) {
Log.e("+333333333333++++++++++",e.getMessage());
return "";
}
}
(3)本地socket監聽mediaplayer,通過getInputStream方法可以獲取到mediaplayer傳遞過來的請求資訊資料,由於我們是通過本地代理地址的方式獲取到的,所以我們需要根據這個本地的請求資訊替換成真實的遠端socket請求資訊,向伺服器獲取真實請求資料。程式碼如下:
public void getTrueSocketRequestInfo(Socket localSocket) throws Exception {
InputStream in_localSocket = localSocket.getInputStream();
String trueSocketRequestInfoStr = "";//儲存MediaPlayer的真實HTTP請求
byte[] local_request = new byte[1024];
while (in_localSocket.read(local_request) != -1) {
String str = new String(local_request);
trueSocketRequestInfoStr = trueSocketRequestInfoStr + str;
if (trueSocketRequestInfoStr.contains("GET") && trueSocketRequestInfoStr.contains("\r\n\r\n")) {
//把request中的本地ip改為遠端ip
trueSocketRequestInfoStr = trueSocketRequestInfoStr.replace(LOCAL_IP_ADDRESS + ":" + local_ip_port, remoteHostAndPort);
this.trueSocketRequestInfoStr = trueSocketRequestInfoStr;
//如果使用者拖動了進度條,因為拖動了滾動條還有Range則表示本地歌曲還未快取完,不再儲存
if (trueSocketRequestInfoStr.contains("Range")) {
Log.e("+44444++++Range++++++","");
writeFile = false;
}
break;
}
}
}
(4)上一步我們獲取到了真實的請求資料資訊,此時通過遠端socket連線遠端請求,程式碼如下:
public Socket sendRemoteRequest() throws Exception {
//建立遠端socket用來請求網路資料
Socket remoteSocket = new Socket();
remoteSocket.connect(remoteAddress, socketTimeoutTime);
remoteSocket.getOutputStream().write(trueSocketRequestInfoStr.getBytes());
remoteSocket.getOutputStream().flush();
return remoteSocket;
}
(5)將遠端socket的資料,通過本地socket寫入mediaplayer進行播放
(6)最後在PlayMp3.java檔案中呼叫,程式碼如下:
private void playMusic() {
if(Zsong!=""&&singer!=""){
tv_Name.setText(Zsong);
tv_author.setText(singer);
}
ivMusicPlay.setImageResource(R.mipmap.music_button_pause);
currentState = STATE_PLAY;
* 第一次執行載入資料,之後執行暫停,開始
* */
if (!firstPlay && mediaPlayer.getCurrentPosition() > 0) {
mediaPlayerHttpProxy = null;
mediaPlayer.start();
} else {
try {
// Toast.makeText(playArea, Zsong+"載入"+mediaPlayer.getCurrentPosition(), Toast.LENGTH_SHORT).show();
mediaPlayerHttpProxy = new MediaPlayerProxy(Zsong, false);
//setProgress是檔案寫入本地進度,setSecondaryProgress就是檔案載入進度
mediaPlayerHttpProxy.setOnCaChedProgressUpdateListener(new MediaPlayerProxy.OnCaChedProgressUpdateListener() {
@Override
public void updateCachedProgress(int progress) {
sb_music.setSecondaryProgress(progress);
}
});
//得到從伺服器得到的資料來源
String localProxyUrl = mediaPlayerHttpProxy.getLocalURLAndSetRemotSocketAddr(TEST_URL);
mediaPlayerHttpProxy.startProxy();
mediaPlayer.setDataSource(localProxyUrl);
mediaPlayer.prepareAsync();
firstPlay = false;
} catch (Exception e) {
Log.e("++++++++++++++++++++",e.getMessage());
}
}
}