Android:手机做服务器控制多个手机客户端同时播放音乐(含源码)

来源:互联网 发布:聪明的程序员用delphi 编辑:程序博客网 时间:2024/05/23 15:38

        朋友要求婚,请我帮忙做个小软件,希望他能控制周边朋友的手机,同时播放求婚用的歌曲。想法挺好的,奈何时间紧急,花了几个小时,帮他实现了一个粗糙的版本,可以控制连接上的手机同时播放、暂停、停止放歌。

        基本想法是使用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() + "");}}};@Overrideprotected 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 blocke.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 blocke.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() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch (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();}}    @Overrideprotected void onDestroy() {// TODO Auto-generated method stubsuper.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();}    }@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// TODO Auto-generated method stubif (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() {@Overridepublic 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 exceptione.printStackTrace();}    }        /**     * 停止播放     */    private void stopPlayMusic(){    try {if (mp3 != null) {mp3.stop();mp3.reset();initMediaPlayer();}} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}    }        /**     * 暂停播放     */    private void pausePlayMusic(){    try {if (isPlaying) {mp3.pause();isPlaying = false;}else {mp3.start();isPlaying = true;}} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}    }        private OnClickListener myOnClickListener = new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubswitch (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服务器IPedtServerIp.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 blocke.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 blocke.printStackTrace();} }}).show();    }        @Overrideprotected 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 blocke.printStackTrace();}         if (mp3 != null) {mp3.stop();mp3.release();mp3 = null;}}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {// TODO Auto-generated method stubif (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() {@Overridepublic 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 blocke.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哈哈~

 源码下载请点击。

0 0
原创粉丝点击