Android:手机做服务器控制多个手机客户端同时播放音乐(含源码)
2014-11-06 15:23
591 查看
朋友要求婚,请我帮忙做个小软件,希望他能控制周边朋友的手机,同时播放求婚用的歌曲。想法挺好的,奈何时间紧急,花了几个小时,帮他实现了一个粗糙的版本,可以控制连接上的手机同时播放、暂停、停止放歌。
基本想法是使用C/S架构,一台手机做为服务器,其他手机做为客户端,所有客户端用socket连接上服务器,客户端不断读取socket中的数据,解析出命令后控制歌曲的播放。
实现效果:
废话不多说,直接贴代码:
服务器:
布局文件:activity_main.xml
客户端:
布局文件activity_main.xml
大家求婚如有需要,可以尽情使用,O(∩_∩)O哈哈~
源码下载请点击。
基本想法是使用C/S架构,一台手机做为服务器,其他手机做为客户端,所有客户端用socket连接上服务器,客户端不断读取socket中的数据,解析出命令后控制歌曲的播放。
实现效果:
废话不多说,直接贴代码:
服务器:
布局文件:activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:background="#FFFFFF"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_marginTop="20dp" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15dp" android:textColor="#000000" android:text="服务器IP地址:" /> <TextView android:id="@+id/tvIP" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="192.168.0.1" android:textSize="20dp" android:textColor="#0000FF"/> </LinearLayout> <LinearLayout android:layout_marginTop="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15dp" android:textColor="#000000" android:text="连接的客户端数目:" /> <TextView android:id="@+id/tvClientNum" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:textSize="20dp" android:textColor="#FF0000"/> </LinearLayout> <Button android:id="@+id/btn_start_play" android:layout_gravity="center" android:layout_width="250dp" android:layout_height="80dp" android:textSize="22dp" android:text="开始播放音乐" android:layout_marginTop="50dp" android:layout_marginBottom="10dp" /> <Button android:id="@+id/btn_pause_play" android:layout_gravity="center" android:layout_width="250dp" android:layout_height="50dp" android:textSize="18dp" android:text="暂停播放" android:layout_marginTop="20dp" android:layout_marginBottom="10dp" /> <Button android:id="@+id/btn_stop_play" android:layout_gravity="center" android:layout_width="250dp" android:layout_height="50dp" android:textSize="18dp" android:text="停止播放" android:layout_marginTop="20dp" android:layout_marginBottom="10dp" /> </LinearLayout>ServerThread.java
package ict.ldj.server; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import android.util.Log; public class ServerThread implements Runnable { // 定义当前线程所处理的Socket private Socket socket = null; // 该线程所处理的Socket所对应的输入流 BufferedReader br = null; public ServerThread(Socket socket) throws IOException { this.socket = socket; // 初始化该Socket对应的输入流 br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8")); } @Override public void run() { try { String content = null; // 采用循环不断从Socket中读取客户端发送过来的数据 while ((content = readFromClient()) != null) { // 遍历socketList中的每个Socket,将读到的内容向每个Socket发送一次 Log.w("client msg",content + "\t"); if ("QUIT".equals(content)) { socket.close(); ServerMainActivity.socketList.remove(socket); } // for (Socket s : ServerMainActivity.socketList) { // OutputStream os = s.getOutputStream(); // os.write((content + "\n").getBytes("utf-8")); // } } } catch (Exception e) { e.printStackTrace(); } } /** * 定义读取客户端数据的方法 * * @return */ private String readFromClient() { try { return br.readLine(); } // 如果捕捉到异常,表明该Socket对应的客户端已经关闭 catch (Exception e) { // 删除该Socket ServerMainActivity.socketList.remove(socket); e.printStackTrace(); } return null; } }ServerMainActivity.java
package ict.ldj.server; import java.io.IOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Process; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class ServerMainActivity extends Activity { // 定义保存所有Socket的集合 public static ArrayList<Socket> socketList = new ArrayList<Socket>(); private static final int refresh_num_msd_code = 0x101; public Button btnStartPlay = null; public Button btnPausePlay = null; public Button btnStopPlay = null; public TextView tvIP = null; public TextView tvClientNum = null; //用来更新与服务器连接的客户端个数 public Handler mHandler = new Handler(){ public void handleMessage(Message msg) { if (msg.what == refresh_num_msd_code) { tvClientNum.setText(socketList.size() + ""); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWidget(); new Thread() { public void run() { try { StartListenerSocket(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }.start(); } private void initWidget() { btnStartPlay = (Button) findViewById(R.id.btn_start_play); btnPausePlay = (Button) findViewById(R.id.btn_pause_play); btnStopPlay = (Button) findViewById(R.id.btn_stop_play); tvIP = (TextView) findViewById(R.id.tvIP); tvClientNum = (TextView) findViewById(R.id.tvClientNum); btnStartPlay.setOnClickListener(myOnClickListener); btnPausePlay.setOnClickListener(myOnClickListener); btnStopPlay.setOnClickListener(myOnClickListener); tvIP.setText(getLocalIp()); new Thread(){ public void run(){ while(true){ try { mHandler.sendEmptyMessage(refresh_num_msd_code); this.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }.start(); } /** * 获取本机的IP地址 * @return 本机的IP地址,Sring */ private String getLocalIp(){ // 获取wifi服务 WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); //判断wifi是否开启 if (!wifiManager.isWifiEnabled()) { wifiManager.setWifiEnabled(true); } WifiInfo wifiInfo = wifiManager.getConnectionInfo(); int ipAddress = wifiInfo.getIpAddress(); return intToIp(ipAddress); } private String intToIp(int i) { return (i & 0xFF ) + "." + ((i >> 8 ) & 0xFF) + "." + ((i >> 16 ) & 0xFF) + "." + ( i >> 24 & 0xFF) ; } private OnClickListener myOnClickListener = new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.btn_start_play: try { for (Socket s : socketList) { OutputStream os = s.getOutputStream(); if (os != null) os.write(("START PLAY MUSIC\n").getBytes("utf-8")); } } catch (Exception e) { e.printStackTrace(); } break; case R.id.btn_pause_play: try { for (Socket s : socketList) { OutputStream os = s.getOutputStream(); if (os != null) os.write(("PAUSE PLAY MUSIC\n").getBytes("utf-8")); } } catch (Exception e) { e.printStackTrace(); } break; case R.id.btn_stop_play: try { for (Socket s : socketList) { OutputStream os = s.getOutputStream(); if (os != null) os.write(("STOP PLAY MUSIC\n").getBytes("utf-8")); } } catch (Exception e) { e.printStackTrace(); } break; default: break; } } }; public void StartListenerSocket() throws IOException { ServerSocket ss = new ServerSocket(20000); System.out.println("服务器创建成功!"); System.out.println("等待客戶端的连接。。。"); while (true) { // 此行代码会阻塞,等待用户的连接 Socket socket = ss.accept(); System.out.println("有客户端连接进来!"); Log.w("server log", "有客户端连接进来!"); socketList.add(socket); // 每当客户端连接后启动一条ServerThread线程为该客户端服务 new Thread(new ServerThread(socket)).start(); } } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); closeAllSocket(); } /** * 关闭服务器连接的所有Socket */ private void closeAllSocket(){ try { for (Socket s : socketList) { OutputStream os = s.getOutputStream(); if(os != null) os.write(("QUIT\n").getBytes("utf-8")); s.close(); ServerMainActivity.socketList.remove(s); } } catch (Exception e) { e.printStackTrace(); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // TODO Auto-generated method stub if (keyCode == KeyEvent.KEYCODE_BACK) { new AlertDialog.Builder(this) .setIcon(R.drawable.menu_exit) .setTitle("Notice") .setMessage("Do you want to quit?") .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { closeAllSocket(); ServerMainActivity.this.finish(); Process.killProcess(Process.myPid()); } }).show(); return true; } else { return super.onKeyDown(keyCode, event); } } }
客户端:
布局文件activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FFFFFF" android:orientation="vertical" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:orientation="horizontal" > <EditText android:id="@+id/edtServerIp" android:layout_width="240dp" android:layout_height="wrap_content" android:layout_gravity="center" android:hint="如:192.168.0.101" android:paddingLeft="10dp" /> <Button android:id="@+id/btnSetAndEditIP" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="10dp" android:paddingRight="10dp" android:text="设置\n服务器IP" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:orientation="horizontal" > <Button android:id="@+id/btnConnectServer" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="3" android:enabled="false" android:paddingLeft="10dp" android:paddingRight="10dp" android:text="连接服务器" /> <Button android:id="@+id/btnDisConnectServer" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="2" android:enabled="false" android:paddingLeft="10dp" android:paddingRight="10dp" android:text="断开服务器连接" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:visibility="gone" android:orientation="horizontal" > <Button android:id="@+id/start" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:text="start" /> <Button android:id="@+id/pause" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:text="pause" /> <Button android:id="@+id/stop" android:layout_width="0dp" android:layout_weight="2" android:layout_height="wrap_content" android:text="stop" /> </LinearLayout> </LinearLayout>ClientThread.java
package ict.ldj.client; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import android.os.Handler; import android.os.Message; import android.util.Log; public class ClientThread implements Runnable { private Handler handler; // 该线程所处理的Socket所对应的输入流 private BufferedReader br = null; public ClientThread(Socket socket, Handler handler) throws IOException { this.handler = handler; br = new BufferedReader(new InputStreamReader(socket.getInputStream())); } @Override public void run() { try { String content = null; // 不断读取Socket输入流的内容 while ((content = br.readLine()) != null) { // 每当读到来自服务器的数据之后,发送消息通知程序界面显示该数据 if ("START PLAY MUSIC".equals(content)) { handler.sendEmptyMessage(ClientMainActivity.socket_start_play); }else if ("PAUSE PLAY MUSIC".equals(content)) { handler.sendEmptyMessage(ClientMainActivity.socket_pause_play); }else if ("STOP PLAY MUSIC".equals(content)) { handler.sendEmptyMessage(ClientMainActivity.socket_stop_play); }else if ("QUIT".equals(content)) { handler.sendEmptyMessage(ClientMainActivity.socket_quit); }else{ Message msg = new Message(); msg.what = ClientMainActivity.socket_msg; msg.obj = content; handler.sendMessage(msg); } Log.w("msg", content); } } catch (Exception e) { e.printStackTrace(); } } }ClientMainActivity.java
package ict.ldj.client; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.res.AssetFileDescriptor; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Process; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class ClientMainActivity extends Activity { public static final int socket_quit = 0; // socket断开 public static final int socket_start_play = 1; // 播放音乐 public static final int socket_pause_play = 2; // 暂停播放 public static final int socket_stop_play = 3; // 停止播放 public static final int socket_msg = 0x234; // 用来聊天用的信息,该程序中未使用 private EditText edtServerIp = null; private Button btnSetAndEditIP = null; private Button btnConnectServer = null; private Button btnDisConnectServer = null; private String serverIp = ""; private OutputStream os = null; private Handler handler = null; private Socket socket = null; private MediaPlayer mp3 = null; private Boolean isPlaying = false; //设置标记,false表示正在播放 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initWidget(); } private void initWidget(){ edtServerIp = (EditText) findViewById(R.id.edtServerIp); btnSetAndEditIP = (Button) findViewById(R.id.btnSetAndEditIP); btnConnectServer = (Button) findViewById(R.id.btnConnectServer); btnDisConnectServer = (Button) findViewById(R.id.btnDisConnectServer); btnSetAndEditIP.setOnClickListener(myOnClickListener); btnConnectServer.setOnClickListener(myOnClickListener); btnDisConnectServer.setOnClickListener(myOnClickListener); findViewById(R.id.start).setOnClickListener(myOnClickListener); findViewById(R.id.stop).setOnClickListener(myOnClickListener); findViewById(R.id.pause).setOnClickListener(myOnClickListener); initMediaPlayer(); handler = new Handler() { public void handleMessage(Message msg) { // 如果消息来自子线程 if (msg.what == socket_msg) { // 将读取的内容追加显示在文本框中 String mString = msg.obj.toString(); }else if (msg.what == socket_start_play) { showMessage("开始播放音乐"); // 开始播放音乐 startPlayMusic(); }else if (msg.what == socket_pause_play) { showMessage("暂停播放"); pausePlayMusic(); }else if (msg.what == socket_stop_play) { showMessage("停止播放"); stopPlayMusic(); }else if(msg.what == socket_quit){ showAlertDialog("与服务器的连接已断开!!"); } } }; } /** * 初始化播放器句柄 */ private void initMediaPlayer(){ mp3 = new MediaPlayer(); // mp3 = MediaPlayer.create(ClientMainActivity.this,R.raw.music); try { AssetFileDescriptor afd = this.getAssets().openFd("music.mp3"); mp3.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(),afd.getLength()); if (mp3 != null) { mp3.prepare(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 开始播放 */ private void startPlayMusic(){ try { if (mp3 != null) { mp3.stop(); } mp3.prepare(); mp3.start(); isPlaying = true; } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } /** * 停止播放 */ private void stopPlayMusic(){ try { if (mp3 != null) { mp3.stop(); mp3.reset(); initMediaPlayer(); } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } /** * 暂停播放 */ private void pausePlayMusic(){ try { if (isPlaying) { mp3.pause(); isPlaying = false; }else { mp3.start(); isPlaying = true; } } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } private OnClickListener myOnClickListener = new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.btnSetAndEditIP: if (btnSetAndEditIP.getText().toString().startsWith("设置")) { serverIp = edtServerIp.getText().toString(); edtServerIp.setEnabled(false); btnConnectServer.setEnabled(true); btnSetAndEditIP.setText("修改\n服务器IP"); }else { // 修改\n服务器IP edtServerIp.setEnabled(true); btnConnectServer.setEnabled(false); btnSetAndEditIP.setText("设置\n服务器IP"); } break; case R.id.btnConnectServer: // 连接服务器 if (serverIp.length() <= 7) { showMessage("服务器 IP 地址输入不合法"); }else { if (socket != null) { showMessage("服务器已连接!!"); }else{ try { socket = new Socket(serverIp, 20000); // 客户端启动ClientThread线程不断读取来自服务器的数据 new Thread(new ClientThread(socket, handler)).start(); os = socket.getOutputStream(); showMessage("服务器连接成功!!"); btnDisConnectServer.setEnabled(true); } catch (Exception e) { showAlertDialog("服务器连接失败!!"); e.printStackTrace(); } } } break; case R.id.btnDisConnectServer: if (socket != null) { if(os != null){ try { os.write(("QUIT\n").getBytes()); socket.close(); btnDisConnectServer.setEnabled(false); socket = null; } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } break; case R.id.start: startPlayMusic(); break; case R.id.stop: stopPlayMusic(); break; case R.id.pause: pausePlayMusic(); break; default: break; } } }; /** * 弹出信息显示框,如果点击Yes按钮,则向服务器端发送一个QUIT标志,向服务器提示Socket要释放 * @param str 弹出框中输出的信息 */ public void showAlertDialog(String str){ new AlertDialog.Builder(this) .setIcon(R.drawable.menu_exit) .setTitle("Notice") .setMessage(str) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { try { if(os != null) os.write(("QUIT\n").getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }) .show(); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); try { if(os != null) os.write(("QUIT\n").getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (mp3 != null) { mp3.stop(); mp3.release(); mp3 = null; } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // TODO Auto-generated method stub if (keyCode == KeyEvent.KEYCODE_BACK) { new AlertDialog.Builder(this) .setIcon(R.drawable.menu_exit) .setTitle("Notice") .setMessage("Do you want to quit?") .setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }) .setPositiveButton("Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { try { if(os != null) os.write(("QUIT\n").getBytes()); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //关闭程序 ClientMainActivity.this.finish(); Process.killProcess(Process.myPid()); } }) .show(); return true; } else { return super.onKeyDown(keyCode, event); } } private void showMessage(String str){ Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show(); } }
大家求婚如有需要,可以尽情使用,O(∩_∩)O哈哈~
源码下载请点击。
相关文章推荐
- OpenWrt - MPDroid实现Android手机控制OpenWRT播放音乐
- [JavaME]手机同时播放两个音乐 探讨一
- Socket编程总结—Android手机服务器与多个Android手机客户端之间的通信(非阻塞)
- 手机同时播放两个音乐 探讨二
- 【手机变身无线音箱-WIFI流媒体传输】--音乐在PC和手机间双向播放-详解Android DLNA使用-使用DLNA实现不同设备间媒体共享
- 淘宝网手机客户端应用开发见解带android版源码下载
- 用Android搭建客户端 手机和服务器交互开发实例
- [Android实例] 手机点菜系统【附服务器源码,附日历,天气预报】
- 手机同时播放两个音乐 探讨二
- 手机同时播放两个音乐之探讨一[JavaME]
- android手机控制电脑源码
- Android音乐编程:控制应用程序的音量和播放
- 一个嵌入式web服务器项目,实现通过手机Android App实现对嵌入式设备的控制
- 手机同时播放两个音乐之探讨一[JavaME]
- android手机客户端上传文件,java servlet服务器端接收并保存到服务器
- 手机同时播放两个音乐 探讨二[JavaME]
- android 音乐、铃声播放控制相关问题
- 手机同时播放两个音乐之探讨一[JavaME]
- android手机短信监听器,监听到的短信发送到web服务器,完整源码分享
- 客户端与服务器SSL双向认证(客户端:Android-服务端:vc)-含源码