1. 程式人生 > >Android UDP - 簡單訊息推送功能

Android UDP - 簡單訊息推送功能

近期接觸了幾個小的Android開發專案,根據需求要利用到網路傳輸中的UDP協議方式傳輸,遇到不少坑,在此分享一下在Android應用中UDP的一些簡單技術功能實現,希望能幫到用得到的同僚。

需(wo)求(yao)分(gan)析(ma):

從PC上輸入一串亂七八糟,然後能在我手機(某為powered by android)上顯示出來。

當然這個需求有N種方法去實現,這裡和大家分享一下我是如何用UDP/IP方式在Android上實現的。

1、寫伺服器:口號霸氣點,但只實現能發包就行。java或C#或其他語言都沒問題學過計算機網路的應該清楚網路分層細腰結構什麼的,我用java花5分鐘寫了個小窗體。

2、寫移動端:收到資料然後顯示出來,這裡可是有好幾個坑待會兒跟大家敲小黑板。

接下來就是正文了:

伺服器部分:

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;

/********************************************************
 * File name : UdpServer.java
 * Author : Johan	Version : 1.0	Date : 2018/4/6 
 * Description : // 輸入資訊通過UDP協議向移動端傳送
 * Fuction list : // 1.UdpServer
 * 		     2.sendMessage
 * 		     3.main
 * Others : // 
 ********************************************************/

public class UdpServer extends JFrame{

	//// TODO 控制元件及支援項
	private static final long serialVersionUID = 1L;
	private Container con;     // 容器
	private JPanel pan;        // 面板
	private JTextField text;   // 輸入框
	private JButton button;    // 提交按鈕

	/***************************************
	 * Function name : UdpServer
	 * Description : 建構函式
	 * Variables : 
	 ***************************************/
	UdpServer(){
		super();
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setBounds(200,200,200,150);
		con = this.getContentPane();
		pan = new JPanel();
		text = new JTextField(15);
		button = new JButton("傳送");
		
		// 點選按鈕後向固定IP地址裝置某埠傳送資料包
		button.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				String str = text.getText();
				sendMessage(str, "192.168.43.113", 30000);
			}
		});
		
		pan.add(text);
		pan.add(button);
		con.add(pan);
		this.setVisible(true);
	}
	
	/***************************************
	 * Function name : sendMessage
	 * Description : 傳送資訊
	 * Variables : String msg, String IP, int PORT
	 ***************************************/
	public void sendMessage(String msg, String IP, int PORT){
		try {
            DatagramSocket sendSocket = new DatagramSocket();
            InetAddress serverAddr = InetAddress.getByName(IP);
            DatagramPacket outPacket = new DatagramPacket(msg.getBytes(), msg.getBytes().length,serverAddr, PORT);
            sendSocket.send(outPacket);
            sendSocket.close();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
	}
	
	/***************************************
	 * Function name : main
	 * Description : 主函式
	 * Variables : String[] args
	 ***************************************/
	public static void main(String[] args) {
		new UdpServer();
	}

}

Android應用java原始碼:

/******************************************************
 - Project name : UDPLinkTest
 - File name : MainActivity.java
 - Author : Johan    Version : 1.0   Date : 2018-4-6
 - Description : // 接收來自UdpServer程式的資料
 - Function list :
 1.onCreate
 - History : //
 *****************************************************/

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class MainActivity extends AppCompatActivity {


    //// TODO GUI控制元件
    private TextView text = null;   // 顯示接收到的資料

    //// TODO 後臺服務支援項
    private myThread MT;            // 接收執行緒
    private Handler handler;        // 用於修改主介面UI

    /*******************************
     - Function name : onCreate
     - Description : 建構函式
     - Variables : Bundle savedInstanceState
     *******************************/
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView) findViewById(R.id.textView2);

        // 繫結執行緒更改UI
        handler = new Handler(){
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 0:
                        //在這裡得到資料,並且可以直接更新UI
                        String data = (String)msg.obj;
                        text.setText(data);
                        break;
                    default:
                        break;
                }
            }
        };

        // 開啟執行緒
        MT = new myThread();
        MT.start();
    }


    //// TODO 網路響應執行緒
    public class myThread extends Thread{
        //// 網路連線屬性
        final int PORT = 30000;  // 執行緒埠
        private String data;     // 接收到的資料

        /*******************************
         - Function name : run
         - Description : 執行緒執行函式
         - Variables :
         *******************************/
        public void run(){
            super.run();
            // 該函式主要用於接收伺服器返回的資料資訊
            try {
                // 開啟一個socket
                DatagramSocket socket = new DatagramSocket(PORT);

                // 迴圈監聽
                while (true) {
                    // 解析包文
                    byte[] buf = new byte[1024];
                    DatagramPacket packet = new DatagramPacket(buf, buf.length);
                    socket.receive(packet);
                    buf = packet.getData();
                    data = new String(buf, 0, packet.getLength());

                    // 修改主介面UI,即顯示出來
                    new Thread(new Runnable(){
                        @Override
                        public void run() {
                            Message msg = new Message();
                            msg.what = 0;
                            msg.obj = data;
                            handler.sendMessage(msg);
                        }

                    }).start();
                }
            } catch (SocketException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

別急著ctrl+c!下面這行程式碼決定你能否成功:

<uses-permission android:name="android.permission.INTERNET"/>

沒錯!就是要記得在ActivityMainfests.xml裡面加這條許可權宣告,不太清楚如何加許可權的小夥伴可以問問度娘。

那麼要注意的坑在哪兒呢?

注意:

1、記得給APP加網路許可權。

2、APP接收到包以後是不能直接以你以為的很簡單的方式直接修改繫結的textView控制元件,會閃退,要用到源程式裡面onCreate方法裡面的handler部分和myThread執行函式中new Thread部分,這只是一種方法,具體可搜尋“android子執行緒修改UI的方法”,一般有4種,網上的大神們總結的也很好不過我覺得這個挺好用而且在我的專案需求中很方便。

3、你實驗用的PC和你的手機要連到同一個WLAN裡面,同時只是為了驗證使用要注意修改PC端java程式傳送包目的IP。

4、訊息中帶中文會亂碼哦,如果只是進行一些程式級響應的通訊可以不必考慮這點了,解決方法是在DatagramPacket處修改一下傳輸資料的方式。

附上效果圖:



<開始扯感受了,急著走的客官可以go for encoding了>

這裡我只是實現和驗證了一個很小的功能,就是實現程式間的UDP通訊

那這東西有啥用呢?

雖然實際生產中應該會有更先進和安全的技術,但這不妨礙我們在技術上的舉一反三:

1、都是java程式,PC能發到APP,那APP也能發到PC,這就是簡單的訊息互動。

2、既然PC和APP能互動,那一個WLAN下的APP與APP也能互動(其實用起來很麻煩,但可以實現)。

3、既然APP和APP互動很麻煩,那優化一下可以改為加一個代理,即通過伺服器轉發訊息。

4、既然有伺服器了,那隻要登入到伺服器上的APP就能兩兩之間通訊,聽上去是不是很熟悉。

5、這感覺就有點像QQ的原型了,據我道聽途說,QQ的資料傳送正是使用的UDP協議,當然我是聽說的......


我在專案中實際用到這個小技術是因為我要接受伺服器發給每個APP的預警資訊,達到釋出預警的目的

當然對於TCP協議和UDP協議,二者本身存在各自的優劣,大家可以根據實際需要選擇。


俗話說“給我一個技術,我能創新出一個地球”。(其實是本人說的)