您的位置:首页 > 移动开发 > Android开发

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

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

基本想法是使用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哈哈~

源码下载请点击。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: