目录

本文导读

需求效果

代码实现


本文导读

  • 本文将介绍基于UDP 协议的 Socket 通信,一些注意事项在这里提前说明。

1)Android 为了确保用户流畅的操作体验,一些耗时的任务不能够在 UI 线程中运行,像网络访问/通信就属于这类任务,因此必须新开线程执行这些操作。

2)Android 规定除了 UI 线程外,其它线程都不可以对 UI 控件访问和操控。

3)当后台线程获取到数据(如 UDP 监听到的消息)之后,需要将这些数据显示到 UI 界面上时,这就又涉及到了 Android 的线程间数据传递问题。

4)Android 的许多操作(如网络访问、手机存储访问等),都需要在 主配置文件 AndroidManifest.xml 中先声明权限。

需求效果

  • 手机上简单的播放效果如上所示,播放的内容通过 UDP 发送过去,即发送什么,它就播放什么。
  • 播放的视频地址是网络上的在线视频地址,直接使用 UDP 工具进行发送消息,当然也可以自己写代码进行 UDP 消息发送,而且写法与 Java 完全一样。

  • 就使用如下简单的两个类达到要求,一个主线程 UI 类,一个后台监听 UDP 消息的 类。

代码实现

  • 布局文件 activity_main.xml 内容如下:
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/GridLayout1"android:layout_width="fill_parent"android:layout_height="wrap_content"android:columnCount="1"android:orientation="horizontal"android:rowCount="4"><VideoViewandroid:id="@+id/videoView"android:layout_width="match_parent"android:layout_height="match_parent" />
</GridLayout>
  • 主活动 MainActivity.java 内容如下:
package com.example.administrator.helloworld;import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.MediaController;
import android.widget.VideoView;import com.example.administrator.helloworld.thread.UdpThread;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 一个应用程序可以有1个或多个活动,而没有任何限制。* 每个为应用程序所定义的活动都需要在 AndroidManifest.xml 中声明,应用的主活动的意图过滤器标签中需要包含 MAIN 动作和 LAUNCHER 类别* 如果 MAIN 动作还是 LAUNCHER 类别没有在活动中声明,那么应用程序的图标将不会出现在主屏幕的应用列表中。*/
public class MainActivity extends AppCompatActivity {/*** android.widget.VideoView:视频播放器控件* myHandler:用于线程间通信的内部类 Handler*/private VideoView videoView;public MyHandler myHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bindViews();/** 创建 handler 并与 looper 绑定* 主线程 MainActivity extends AppCompatActivity,AppCompatActivity 的祖上有 ContextWrapper* android.content.ContextWrapper#getMainLooper() :获取 Looper* */myHandler = new MyHandler(MainActivity.this.getMainLooper());/*** 创建新线程用于循环监听 UDP 消息* 虽然如果将 监听UDP的线程 直接放在本类中,操作会简单一些,但是为了更加清晰,推荐新建线程类,* 所以要将创建好的 Handler 传递到后台的 UDP 线程中去*/UdpThread udpThread = new UdpThread(myHandler);ExecutorService executorService = Executors.newCachedThreadPool();executorService.execute(udpThread);}/*** 绑定视屏控件* 绑定之后,后台 UDP 线程监听发来的在线视频地址,然后传回给主线程进行播放*/private void bindViews() {videoView = findViewById(R.id.videoView);/*** 为 VideoView 视图设置媒体控制器,设置了之后就会自动由进度条、前进、后退等操作*/videoView.setMediaController(new MediaController(this));/**视频准备完成时回调* */videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {@Overridepublic void onPrepared(MediaPlayer mp) {Log.i("Wmx Logs::", "--------------视频准备完毕,可以进行播放.......");}});/*** 视频播放完成时回调*/videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {@Overridepublic void onCompletion(MediaPlayer mp) {/**播放完成时,再次循环播放*/videoView.start();Log.i("Wmx Logs::", "--------------视频播放完成,再次进行播放.......");}});/*** 视频播放发送错误时回调*/videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() {@Overridepublic boolean onError(MediaPlayer mp, int what, int extra) {Log.i("Wmx Logs::", "--------------视频播放发送错误.......");return false;}});}/*** 自定义 android.os.Handler,用于接收后台线程传递过来给主线程的数据* Handler 是 Android 中专门用来处理线程间传递数据的工具*/public class MyHandler extends Handler {/*** android.os.Looper 主要功能是为特定单一线程运行一个消息环,一个线程对应一个 Looper,同样一个 looper 对应一个线程。* 一个线程创建时本身是没有自己的 looper (只有主线程有),因此需要手动创建 Looper ,然后将 Looper 与线程相关联,* 操作方式:在需要关联的 looper 的线程中调用 Looper.prepare(预备),之后再调用 Looper.loop(循环) 启动 looper,如下所示:* <pre>*  class LooperThread extends Thread {*      public Handler mHandler;**      public void run() {*          Looper.prepare();**          mHandler = new Handler() {*              public void handleMessage(Message msg) {*                  // process incoming messages here*              }*          };**          Looper.loop();*      }*  }</pre>* 将 looper 与线程关联的时候,looper 会同时生产一个 messageQueue(消息队列),looper 会不停的从 messageQueue 中取出消息(Message),* 然后线程就可以根据 Message 中的内容进行相应的操作。* 在创建 Handler 的时候,需要与特定的 looper 绑定,这样通过 handler 就可以把 message 传递给特定的 looper,继而传递给特定的线程。* 线程---Looper---Handler:一个 looper 可以对应多个 handler,而一个 handler 只能对应一个 looper** @param L*/public MyHandler(Looper L) {super(L);}/*** 必须重写这个方法,用于处理 android.os.Message,* 当 后台线程调用 android.os.Handler#sendMessage(android.os.Message) 方法发送消息后* 下面的 handleMessage(Message msg) 就会自动触发,然后处理消息*/@Overridepublic void handleMessage(Message message) {/*** android.os.Bundle 就是消息中的数据* android.os.BaseBundle#getString(java.lang.String):取值的 key 不存在时,返回 null*/Bundle bundle = message.getData();String messageText = bundle.getString("messageText");Log.i("Wmx Logs::", "handleMessage 接收到消息>>>" + messageText);/**android.widget.VideoView#stopPlayback():停止视频,释放资源* android.widget.VideoView#setVideoURI(android.net.Uri):重新绑定视频资源* android.widget.VideoView#start():再次开始播放* */if (messageText != null && !"".equals(messageText)) {videoView.stopPlayback();videoView.setVideoURI(Uri.parse(messageText));videoView.start();}}}
}
  • 后台 UDP 监听的子线程 UdpThread.java 内容如下:
package com.example.administrator.helloworld.thread;import android.os.Bundle;
import android.os.Message;
import android.util.Log;import com.example.administrator.helloworld.MainActivity;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.charset.Charset;/*** 后台监听 UDP 消息的子线程*/
public class UdpThread implements Runnable {/*** TAG:日志标签* messageText:UDP 监听到的消息文本,每次不能超过 1024 字节* myHandler:用于给主线程发送消息的 Handler*/private final String TAG = "Wmx Log::";private String messageText;public MainActivity.MyHandler myHandler;public UdpThread(MainActivity.MyHandler myHandler) {this.myHandler = myHandler;}@Overridepublic void run() {Log.i("Wmx Logs:: ", "Udp 新线程开启..........." + Thread.currentThread().getName());DatagramSocket datagramSocket = null;/** 数据接收大小设置为 1024 字节,超出部分是接收不到的*/byte[] buffer = new byte[1024];DatagramPacket datagramPacket;try {/*** InetSocketAddress(String hostname, int port):网络套接字地址,同时指定监听的 ip 与 端口*      A valid port value is between 0 and 65535.* InetSocketAddress(int port):网络套接字地址,指定监听的 端口,ip 默认为移动设备本机 ip* */InetSocketAddress socketAddress = new InetSocketAddress(9090);/**DatagramSocket(SocketAddress bindaddr):根据绑定好的 SocketAddress 创建 UDP 数据包套接字* DatagramSocket(int port):只指定 监听的端口时,IP 默认为移动设备本机 ip*/datagramSocket = new DatagramSocket(socketAddress);datagramPacket = new DatagramPacket(buffer, buffer.length);/**循环监听*/while (true) {datagramSocket.receive(datagramPacket);/**读取数据* 指定使用 UTF-8 编码,对于中文乱码问题,遵循对方发送时使用什么编码,则接收时也使用同样的编码的原则*/messageText = new String(datagramPacket.getData(), 0, datagramPacket.getLength(), Charset.forName("UTF-8"));/*** 可以创建一个新的 Message,但是推荐调用 handler 的 obtainMessage 方法获取 Message,* 这个方法的作用是从系统的消息池中取出一个 Message,这样就可以避免 Message 创建和销毁带来的资源浪费。**/Message obtainMessage = myHandler.obtainMessage();Bundle bundle = new Bundle();bundle.putString("messageText", messageText);/**为发送的消息设置数据*/obtainMessage.setData(bundle);/**发送消息*/myHandler.sendMessage(obtainMessage);Log.i("WMx Logs::", " UDP 监听到消息>>>>>" + messageText + " >>> 线程:" + Thread.currentThread().getName() + ">>为主线程传输完毕...");}} catch (SocketException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {if (datagramSocket != null) {if (!datagramSocket.isConnected()) {datagramSocket.disconnect();}if (!datagramSocket.isClosed()) {datagramSocket.close();}/**即使抛异常了,也要再次监听*/new Thread(new UdpThread(myHandler)).start();}}}
}
  • 主配置文件 AndroidManifest.xml 内容如下,主要就是因为 UDP 结束消息需要访问网络,则必须添加网络访问权限

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.example.administrator.helloworld"><!--添加外部存储的读/写权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!--添加网络访问权限--><uses-permission android:name="android.permission.INTERNET" /><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application>
</manifest>

Android 网络通信 之 UDP相关推荐

  1. android不能使用udp获取数据解决

    android不能使用udp获取数据 如果你要通过wifi在内网里,用android手机通过udp发送数据到pc上,并在pc上通过java程序将数据返回到手机上,但是确无法获取数据,今天我也遇到了这个 ...

  2. android网络通信之SOAP教程实例汇总

    一.实例教程:Android网络通信之 SOAP教程篇: 1.android webservice通信之ksoap http://www.eoeandroid.com/thread-162563-1- ...

  3. sunny底层android,Android网络通信概述

    网络通信在App的使用中占据重要地位,要实现网络通信,从宏观上分为两种方式,即:调用原生类和使用第三方框架. 调用原生类 Android中通过原生类进行网络通信时,根据业务场景的不同,主要分为两种方式 ...

  4. Android 网络通信框架Volley简介(Google IO 2013)

    Volley主页 https://android.googlesource.com/platform/frameworks/volley http://www.youtube.com/watch?v= ...

  5. Android 通过局域网udp广播自动建立socket连接

    Android开发中经常会用到socket通讯.由于项目需要,最近研究了一下这方面的知识. 需求是想通过wifi实现android移动设备和android平台的电视之间的文件传输与控制. 毫无疑问这中 ...

  6. android 网络通信框架volly

    1. 什么是Volley 在这之前,我们在程序中需要和网络通信的时候,大体使用的东西莫过于AsyncTaskLoader,HttpURLConnection,AsyncTask,HTTPClient( ...

  7. android网络通信之HTTP协议教程实例汇总

    在现在的开发和应用中,网络通讯是必不可少的.虽然还是比较怀念小时候,抱着一台95在那里玩单机游戏玩的天昏地暗的时光,但是,现在,就算一个幼儿园的小盆友如果问你要手机玩游戏,突然发现居然买不了冰激凌草莓 ...

  8. Android网络通信的六种方式示例代码

    表1展示了Android SDK中的一些与网络有关的API包名 表1. Android SDK 网络包 包                      描述                        ...

  9. Android 网络通信架构学习

    最近跟着云课堂上的极客学院做安卓APP,学习了课程里面介绍的一种网络通信架构.清晰明了,比我自己东一块西一块拼凑出来的要好很多.在这里记录一下. 云课堂的连接:http://study.163.com ...

  10. Android网络通信的六种方式

    在Android中几种网络编程的方式: (1)针对TCP/IP的Socket.ServerSocket (2)针对UDP的DatagramSocket.DatagramPackage.这里需要注意的是 ...

最新文章

  1. android webview详情,Android中的WebView详细介绍
  2. Android用户界面开发(11):Menu
  3. 命令行编译 WRK ,windbg 调试
  4. 分布式入门:常用的分布式基础算法
  5. [hihoCoder 1384]Genius ACM
  6. js简单判断身份证合法性以及身份证生日合法性
  7. java 数字转大写 100行内搞定
  8. Python 修改python插件包的默认安装路径
  9. R语言读取tsv文件
  10. JS小练习:使用JavaScript实现点击‘上一张’,‘下一张’循环播放图片
  11. 电脑桌面宠物-开机自启
  12. 计算机专业本科上线分数,计算机专业本科分数线
  13. 【笔记本显卡改BIOS硬超频方法初探及简明步骤】
  14. 电气工程与计算机最好的大学,加州大学伯克利分校电气工程与计算机科学研究生怎么样?好不好...
  15. 深入AMCL(四):相机如何辅助AMCL自动全局定位
  16. WPF源码控件库《Newbeecoder.UI》轮播
  17. Mete8 手机夏天充电慢
  18. thinkpad卡在logo界面_联想笔记本开不了机卡住logo界面如何解决
  19. 深度学习-卷积神经网络(CNN)
  20. Tiva C(TM4C)的bootloader和启动过程与stm32对比

热门文章

  1. Android 设计模式:(一)策略模式 —— 封装行为的大局观
  2. 郁闷的.net程序员与坑爹的.net 4 client profile
  3. DropDownList操作;ListBox操作;动态创建控件;Response.Write(欢迎学习ASP.NET''!);
  4. 【数据分享】糖尿病患者研究数据集
  5. 拓端tecdat|R语言指数平滑法holt-winters分析谷歌Google Analytics博客用户访问时间序列数据
  6. 拓端tecdat|R语言做复杂金融产品的几何布朗运动的模拟
  7. 清华大学操作系统OS学习(四)——物理内存管理:连续内存分配
  8. cuda对应pytorh安装
  9. python实现验证码图像数据去噪处理的心路历程
  10. 2021-06-27函数定义与参数