项目实例:https://download.csdn.net/download/qq_37437983/10484636
实现环境:
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()); } } }