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

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

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

 源码下载请点击。

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值