仿飞鸽传书的Android版本

2024-03-31 07:58
文章标签 android 版本 飞鸽传书

本文主要是介绍仿飞鸽传书的Android版本,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1. 网络应用中基本上都是TCP(Transmission Control Protocol传输控制协议)和UDP

(User Datagram Protocol用户数据报协议),TCP是面向连接的通信协议,UDP是无连接的通信协议.
Socket连接套接字,Java分别为TCP和UDP提供了相应的类,TCP是java.net.ServerSocket(用于服务器端)
和java.net.Socket(用于客户端);UDP是java.net.DatagramSocket

2. 两台主机之间的点对点通信,称为“单播 Unicast”。当多台主机同时接受一个数据报时,
若采用单播通信,则源主机需要给每个接收主机发送一个相同的数据报;若采用组播通信,
则源主机只需要发送一个数据报即可到达每个接收主机,从而节省了网络带宽,降低了
发送主机的负荷。

3. 组播 Multicast是一种特殊的数据报传输方式,它将具有相同需求的主机加入到某一个组,向

组发送的信息,其所有成员均可接收到。

- 组是用组播地址(D类IP地址:224.0.0.0 - 239.255.255.255)和标准UDP端口号来标识的,

即待发送的数据报的目的地址为一个组播地址。主机可以申请加入某个组播地址所标识

的组,也可以从该组中退出。



程序源代码:

http://download.csdn.net/detail/thinkinwm/6524085


命令定义

1.命令定义     80        用户注册

2.命令定义     81        短消息

3.命令定义     82        文件传输


package com.android.flypigeon.util;import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;import com.android.flypigeon.R;public class Constant {public static Map<String, Integer> exts = new HashMap<String, Integer>();static {exts.put("doc", R.drawable.doc);exts.put("docx", R.drawable.doc);exts.put("xls", R.drawable.xls);exts.put("xlsx", R.drawable.xls);exts.put("ppt", R.drawable.ppt);exts.put("pptx", R.drawable.ppt);exts.put("jpg", R.drawable.image);exts.put("jpeg", R.drawable.image);exts.put("gif", R.drawable.image);exts.put("png", R.drawable.image);exts.put("ico", R.drawable.image);exts.put("apk", R.drawable.apk);exts.put("jar", R.drawable.jar);exts.put("rar", R.drawable.rar);exts.put("zip", R.drawable.rar);exts.put("mp3", R.drawable.music);exts.put("wma", R.drawable.music);exts.put("aac", R.drawable.music);exts.put("ac3", R.drawable.music);exts.put("ogg", R.drawable.music);exts.put("flac", R.drawable.music);exts.put("midi", R.drawable.music);exts.put("pcm", R.drawable.music);exts.put("wav", R.drawable.music);exts.put("amr", R.drawable.music);exts.put("m4a", R.drawable.music);exts.put("ape", R.drawable.music);exts.put("mid", R.drawable.music);exts.put("mka", R.drawable.music);exts.put("svx", R.drawable.music);exts.put("snd", R.drawable.music);exts.put("vqf", R.drawable.music);exts.put("aif", R.drawable.music);exts.put("voc", R.drawable.music);exts.put("cda", R.drawable.music);exts.put("mpc", R.drawable.music);exts.put("mpeg", R.drawable.video);exts.put("mpg", R.drawable.video);exts.put("dat", R.drawable.video);exts.put("ra", R.drawable.video);exts.put("rm", R.drawable.video);exts.put("rmvb", R.drawable.video);exts.put("mp4", R.drawable.video);exts.put("flv", R.drawable.video);exts.put("mov", R.drawable.video);exts.put("qt", R.drawable.video);exts.put("asf", R.drawable.video);exts.put("wmv", R.drawable.video);exts.put("avi", R.drawable.video);exts.put("3gp", R.drawable.video);exts.put("mkv", R.drawable.video);exts.put("f4v", R.drawable.video);exts.put("m4v", R.drawable.video);exts.put("m4p", R.drawable.video);exts.put("m2v", R.drawable.video);exts.put("dat", R.drawable.video);exts.put("xvid", R.drawable.video);exts.put("divx", R.drawable.video);exts.put("vob", R.drawable.video);exts.put("mpv", R.drawable.video);exts.put("mpeg4", R.drawable.video);exts.put("mpe", R.drawable.video);exts.put("mlv", R.drawable.video);exts.put("ogm", R.drawable.video);exts.put("m2ts", R.drawable.video);exts.put("mts", R.drawable.video);exts.put("ask", R.drawable.video);exts.put("trp", R.drawable.video);exts.put("tp", R.drawable.video);exts.put("ts", R.drawable.video);}// 自定义Actionpublic static final String updateMyInformationAction = "com.android.flypigeon.updateMyInformation";public static final String personHasChangedAction = "com.android.flypigeon.personHasChanged";public static final String hasMsgUpdatedAction = "com.android.flypigeon.hasMsgUpdated";public static final String receivedSendFileRequestAction = "com.android.flypigeon.receivedSendFileRequest";public static final String refuseReceiveFileAction = "com.android.flypigeon.refuseReceiveFile";public static final String remoteUserRefuseReceiveFileAction = "com.android.flypigeon.remoteUserRefuseReceiveFile";public static final String dataReceiveErrorAction = "com.android.flypigeon.dataReceiveError";public static final String dataSendErrorAction = "com.android.flypigeon.dataSendError";public static final String whoIsAliveAction = "com.android.flypigeon.whoIsAlive";// 询问当前那个Activity是激活状态public static final String imAliveNow = "com.android.flypigeon.imAliveNow";public static final String remoteUserUnAliveAction = "com.android.flypigeon.remoteUserUnAlive";public static final String fileSendStateUpdateAction = "com.android.flypigeon.fileSendStateUpdate";public static final String fileReceiveStateUpdateAction = "com.android.flypigeon.fileReceiveStateUpdate";public static final String receivedTalkRequestAction = "com.android.flypigeon.receivedTalkRequest";public static final String acceptTalkRequestAction = "com.android.flypigeon.acceptTalkRequest";public static final String remoteUserClosedTalkAction = "com.android.flypigeon.remoteUserClosedTalk";// 系统Action// System Action declarepublic static final String bootCompleted = "android.intent.action.BOOT_COMPLETED";public static final String WIFIACTION = "android.net.conn.CONNECTIVITY_CHANGE";public static final String ETHACTION = "android.intent.action.ETH_STATE";// 生成唯一ID码public static int getMyId() {int id = (int) (Math.random() * 1000000);return id;}// other 其它定义,另外消息长度为60个汉字,utf-8中定义一个汉字占3个字节,所以消息长度为180bytes// 文件长度为30个汉字,所以总长度为90个字节public static final int bufferSize = 256;public static final int msgLength = 180;public static final int fileNameLength = 90;public static final int readBufferSize = 4096;// 文件读写缓存public static final byte[] pkgHead = "AND".getBytes();public static final int CMD80 = 80;public static final int CMD81 = 81;public static final int CMD82 = 82;public static final int CMD83 = 83;public static final int CMD_TYPE1 = 1;public static final int CMD_TYPE2 = 2;public static final int CMD_TYPE3 = 3;public static final int OPR_CMD1 = 1;public static final int OPR_CMD2 = 2;public static final int OPR_CMD3 = 3;public static final int OPR_CMD4 = 4;public static final int OPR_CMD5 = 5;public static final int OPR_CMD6 = 6;public static final int OPR_CMD10 = 10;public static final String MULTICAST_IP = "239.9.9.1";public static final int PORT = 5760;public static final int AUDIO_PORT = 5761;// int to ip转换public static String intToIp(int i) {String ip = ((i >> 24) & 0xFF) + "." + ((i >> 16) & 0xFF) + "."+ ((i >> 8) & 0xFF) + "." + (i & 0xFF);return ip;}// 其它定义public static final int FILE_RESULT_CODE = 1;public static final int SELECT_FILES = 1;// 是否要在文件选择器中显示文件public static final int SELECT_FILE_PATH = 2;// 文件选择器只显示文件夹// 文件选择状态保存public static TreeMap<Integer, Boolean> fileSelectedState = new TreeMap<Integer, Boolean>();// 转换文件大小public static String formatFileSize(long fileS) {DecimalFormat df = new DecimalFormat("#.00");String fileSizeString = "";if (fileS < 1024) {fileSizeString = fileS + "B";// fileSizeString = df.format((double) fileS) + "B";} else if (fileS < 1048576) {fileSizeString = df.format((double) fileS / 1024) + "K";} else if (fileS < 1073741824) {fileSizeString = df.format((double) fileS / 1048576) + "M";} else {fileSizeString = df.format((double) fileS / 1073741824) + "G";}return fileSizeString;}}


package com.android.flypigeon.service;import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;import com.android.flypigeon.R;
import com.android.flypigeon.util.ByteAndInt;
import com.android.flypigeon.util.Constant;
import com.android.flypigeon.util.FileName;
import com.android.flypigeon.util.FileState;
import com.android.flypigeon.util.Message;
import com.android.flypigeon.util.Person;import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;public class MainService extends Service {private ServiceBinder sBinder = new ServiceBinder();// 服务绑定器private static ArrayList<Map<Integer, Person>> children = new ArrayList<Map<Integer, Person>>();// 保存所有组中的用户,每个map对象保存一个组的全部用户private static Map<Integer, Person> childrenMap = new HashMap<Integer, Person>();// 当前在线用户private static ArrayList<Integer> personKeys = new ArrayList<Integer>();// 当前在线用户idprivate static Map<Integer, List<Message>> msgContainer = new HashMap<Integer, List<Message>>();// 所有用户信息容器private SharedPreferences pre = null;private SharedPreferences.Editor editor = null;private WifiManager wifiManager = null;private ServiceBroadcastReceiver receiver = null;private InetAddress localInetAddress = null;private String localIp = null;private byte[] localIpBytes = null;private byte[] regBuffer = new byte[Constant.bufferSize];// 本机网络注册交互指令private byte[] msgSendBuffer = new byte[Constant.bufferSize];// 信息发送交互private byte[] fileSendBuffer = new byte[Constant.bufferSize];// 文件发送交互指令private byte[] talkCmdBuffer = new byte[Constant.bufferSize];// 通话指令private static Person me = null;// 用来保存自身的相关信息private CommunicationBridge comBridge = null;// 通讯与协议解析模块@Overridepublic IBinder onBind(Intent arg0) {return sBinder;}@Overridepublic boolean onUnbind(Intent intent) {return false;}@Overridepublic void onRebind(Intent intent) {}@Overridepublic void onCreate() {}@Overridepublic void onStart(Intent intent, int startId) {initCmdBuffer();// 初始化指令缓存wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);new CheckNetConnectivity().start();// 侦测网络状态,获取IP地址comBridge = new CommunicationBridge();// 启动socket连接comBridge.start();pre = PreferenceManager.getDefaultSharedPreferences(this);editor = pre.edit();regBroadcastReceiver();// 注册广播接收器getMyInfomation();// 获得自身信息new UpdateMe().start();// 向网络发送心跳包,并注册new CheckUserOnline().start();// 检查用户列表是否有超时用户sendPersonHasChangedBroadcast();// 通知有新用户加入或退出System.out.println("Service started...");}// 服务绑定public class ServiceBinder extends Binder {public MainService getService() {return MainService.this;}}// 获得自已的相关信息private void getMyInfomation() {SharedPreferences pre = PreferenceManager.getDefaultSharedPreferences(this);int iconId = pre.getInt("headIconId", R.drawable.black_bird);String nickeName = pre.getString("nickeName", "Zhang San");int myId = pre.getInt("myId", Constant.getMyId());editor.putInt("myId", myId);editor.commit();if (null == me)me = new Person();me.personHeadIconId = iconId;me.personNickeName = nickeName;me.personId = myId;me.ipAddress = localIp;// 更新注册命令用户数据System.arraycopy(ByteAndInt.int2ByteArray(myId), 0, regBuffer, 6, 4);System.arraycopy(ByteAndInt.int2ByteArray(iconId), 0, regBuffer, 10, 4);for (int i = 14; i < 44; i++)regBuffer[i] = 0;// 把原来的昵称内容清空byte[] nickeNameBytes = nickeName.getBytes();System.arraycopy(nickeNameBytes, 0, regBuffer, 14,nickeNameBytes.length);// 更新通话命令用户数据System.arraycopy(ByteAndInt.int2ByteArray(myId), 0, talkCmdBuffer, 6, 4);System.arraycopy(ByteAndInt.int2ByteArray(iconId), 0, talkCmdBuffer,10, 4);for (int i = 14; i < 44; i++)talkCmdBuffer[i] = 0;// 把原来的昵称内容清空System.arraycopy(nickeNameBytes, 0, talkCmdBuffer, 14,nickeNameBytes.length);}private String getCurrentTime() {Date date = new Date();return date.toLocaleString();}// 检测网络连接状态,获得本机IP地址private class CheckNetConnectivity extends Thread {public void run() {try {if (!wifiManager.isWifiEnabled()) {wifiManager.setWifiEnabled(true);}for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {NetworkInterface intf = en.nextElement();for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {InetAddress inetAddress = enumIpAddr.nextElement();if (!inetAddress.isLoopbackAddress()) {if (inetAddress.isReachable(1000)) {localInetAddress = inetAddress;localIp = inetAddress.getHostAddress().toString();localIpBytes = inetAddress.getAddress();System.arraycopy(localIpBytes, 0, regBuffer,44, 4);}}}}} catch (Exception ex) {ex.printStackTrace();}};};// 初始化指令缓存private void initCmdBuffer() {// 初始化用户注册指令缓存for (int i = 0; i < Constant.bufferSize; i++)regBuffer[i] = 0;System.arraycopy(Constant.pkgHead, 0, regBuffer, 0, 3);regBuffer[3] = Constant.CMD80;regBuffer[4] = Constant.CMD_TYPE1;regBuffer[5] = Constant.OPR_CMD1;// 初始化信息发送指令缓存for (int i = 0; i < Constant.bufferSize; i++)msgSendBuffer[i] = 0;System.arraycopy(Constant.pkgHead, 0, msgSendBuffer, 0, 3);msgSendBuffer[3] = Constant.CMD81;msgSendBuffer[4] = Constant.CMD_TYPE1;msgSendBuffer[5] = Constant.OPR_CMD1;// 初始化发送文件指令缓存for (int i = 0; i < Constant.bufferSize; i++)fileSendBuffer[i] = 0;System.arraycopy(Constant.pkgHead, 0, fileSendBuffer, 0, 3);fileSendBuffer[3] = Constant.CMD82;fileSendBuffer[4] = Constant.CMD_TYPE1;fileSendBuffer[5] = Constant.OPR_CMD1;// 初始化通话指令// 初始化发送文件指令缓存for (int i = 0; i < Constant.bufferSize; i++)talkCmdBuffer[i] = 0;System.arraycopy(Constant.pkgHead, 0, talkCmdBuffer, 0, 3);talkCmdBuffer[3] = Constant.CMD83;talkCmdBuffer[4] = Constant.CMD_TYPE1;talkCmdBuffer[5] = Constant.OPR_CMD1;}// 获得所有用户对象public ArrayList<Map<Integer, Person>> getChildren() {return children;}// 获得所有用户idpublic ArrayList<Integer> getPersonKeys() {return personKeys;}// 根据用户id获得该用户的消息public List<Message> getMessagesById(int personId) {return msgContainer.get(personId);}// 根据用户id获得该用户的消息数量public int getMessagesCountById(int personId) {List<Message> msgs = msgContainer.get(personId);if (null != msgs) {return msgs.size();} else {return 0;}}// 每隔10秒发送一个心跳包boolean isStopUpdateMe = false;private class UpdateMe extends Thread {@Overridepublic void run() {while (!isStopUpdateMe) {try {comBridge.joinOrganization();sleep(10000);} catch (Exception e) {e.printStackTrace();}}}}// 检测用户是否在线,如果超过15说明用户已离线,秒则从列表中清除该用户private class CheckUserOnline extends Thread {@Overridepublic void run() {super.run();boolean hasChanged = false;while (!isStopUpdateMe) {if (childrenMap.size() > 0) {Set<Integer> keys = childrenMap.keySet();for (Integer key : keys) {if (System.currentTimeMillis()- childrenMap.get(key).timeStamp > 15000) {childrenMap.remove(key);personKeys.remove(Integer.valueOf(key));hasChanged = true;}}}if (hasChanged)sendPersonHasChangedBroadcast();try {sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}}}// 发送用户更新广播private void sendPersonHasChangedBroadcast() {Intent intent = new Intent();intent.setAction(Constant.personHasChangedAction);sendBroadcast(intent);}// 注册广播接收器private void regBroadcastReceiver() {receiver = new ServiceBroadcastReceiver();IntentFilter filter = new IntentFilter();filter.addAction(Constant.WIFIACTION);filter.addAction(Constant.ETHACTION);filter.addAction(Constant.updateMyInformationAction);filter.addAction(Constant.refuseReceiveFileAction);filter.addAction(Constant.imAliveNow);registerReceiver(receiver, filter);}// 广播接收器处理类private class ServiceBroadcastReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if (intent.getAction().equals(Constant.WIFIACTION)|| intent.getAction().equals(Constant.ETHACTION)) {new CheckNetConnectivity().start();} else if (intent.getAction().equals(Constant.updateMyInformationAction)) {getMyInfomation();comBridge.joinOrganization();} else if (intent.getAction().equals(Constant.refuseReceiveFileAction)) {comBridge.refuseReceiveFile();} else if (intent.getAction().equals(Constant.imAliveNow)) {}}}// 发送信息public void sendMsg(int personId, String msg) {comBridge.sendMsg(personId, msg);}// 发送文件public void sendFiles(int personId, ArrayList<FileName> files) {comBridge.sendFiles(personId, files);}// 接收文件public void receiveFiles(String fileSavePath) {comBridge.receiveFiles(fileSavePath);}// 获得欲接收的文件名public ArrayList<FileState> getReceivedFileNames() {return comBridge.getReceivedFileNames();}// 获得欲发送的文件名public ArrayList<FileState> getBeSendFileNames() {return comBridge.getBeSendFileNames();}// 开始语音呼叫public void startTalk(int personId) {comBridge.startTalk(personId);}// 结束语音呼叫public void stopTalk(int personId) {comBridge.stopTalk(personId);}// 接受远程语音呼叫public void acceptTalk(int personId) {comBridge.acceptTalk(personId);}@Overridepublic void onDestroy() {comBridge.release();unregisterReceiver(receiver);isStopUpdateMe = true;System.out.println("Service on destory...");}// ========================协议分析与通讯模块=======================================================private class CommunicationBridge extends Thread {private MulticastSocket multicastSocket = null;private byte[] recvBuffer = new byte[Constant.bufferSize];private int fileSenderUid = 0;// 用来保存文件发送者的id号private boolean isBusyNow = false;// 现在是否正在收发文件,如果该状态为true则表示现在正在进行收发文件操作,这时需要向其它发送文件的用户发送忙指令private String fileSavePath = null;// 用来保存接收到的文件private boolean isStopTalk = false;// 通话结束标志private ArrayList<FileName> tempFiles = null;// 用来临时保存需要发送的文件名private int tempUid = 0;// 用来临时保存需要发送文件的用户id(接受文件方的用户id)private ArrayList<FileState> receivedFileNames = new ArrayList<FileState>();private ArrayList<FileState> beSendFileNames = new ArrayList<FileState>();private FileHandler fileHandler = null;// 文件处理线程,用来收发文件private AudioHandler audioHandler = null;// 音频处理模块,用来收发音频数据public CommunicationBridge() {fileHandler = new FileHandler();fileHandler.start();audioHandler = new AudioHandler();audioHandler.start();}// 打开组播端口,准备组播通讯@Overridepublic void run() {super.run();try {multicastSocket = new MulticastSocket(Constant.PORT);multicastSocket.joinGroup(InetAddress.getByName(Constant.MULTICAST_IP));System.out.println("Socket started...");while (!multicastSocket.isClosed() && null != multicastSocket) {for (int i = 0; i < Constant.bufferSize; i++) {recvBuffer[i] = 0;}DatagramPacket rdp = new DatagramPacket(recvBuffer,recvBuffer.length);multicastSocket.receive(rdp);parsePackage(recvBuffer);}} catch (Exception e) {try {if (null != multicastSocket && !multicastSocket.isClosed()) {multicastSocket.leaveGroup(InetAddress.getByName(Constant.MULTICAST_IP));multicastSocket.close();}} catch (Exception e1) {e1.printStackTrace();}e.printStackTrace();}}// 解析接收到的数据包private void parsePackage(byte[] pkg) {int CMD = pkg[3];// 命令字int cmdType = pkg[4];// 命令类型int oprCmd = pkg[5];// 操作命令// 获得用户ID号byte[] uId = new byte[4];System.arraycopy(pkg, 6, uId, 0, 4);int userId = ByteAndInt.byteArray2Int(uId);switch (CMD) {case Constant.CMD80:switch (cmdType) {case Constant.CMD_TYPE1:// 如果该信息不是自己发出则给对方发送回应包,并把对方加入用户列表if (userId != me.personId) {updatePerson(userId, pkg);// 发送应答包byte[] ipBytes = new byte[4];// 获得请求方的ip地址System.arraycopy(pkg, 44, ipBytes, 0, 4);try {InetAddress targetIp = InetAddress.getByAddress(ipBytes);regBuffer[4] = Constant.CMD_TYPE2;// 把自己的注册信息修改成应答信息标志,把自己的信息发送给请求方DatagramPacket dp = new DatagramPacket(regBuffer,Constant.bufferSize, targetIp,Constant.PORT);multicastSocket.send(dp);} catch (Exception e) {e.printStackTrace();}}break;case Constant.CMD_TYPE2:updatePerson(userId, pkg);break;case Constant.CMD_TYPE3:childrenMap.remove(userId);personKeys.remove(Integer.valueOf(userId));sendPersonHasChangedBroadcast();break;}break;case Constant.CMD81:// 收到信息switch (cmdType) {case Constant.CMD_TYPE1:List<Message> messages = null;if (msgContainer.containsKey(userId)) {messages = msgContainer.get(userId);} else {messages = new ArrayList<Message>();}byte[] msgBytes = new byte[Constant.msgLength];System.arraycopy(pkg, 10, msgBytes, 0, Constant.msgLength);String msgStr = new String(msgBytes).trim();Message msg = new Message();msg.msg = msgStr;msg.receivedTime = getCurrentTime();messages.add(msg);msgContainer.put(userId, messages);Intent intent = new Intent();intent.setAction(Constant.hasMsgUpdatedAction);intent.putExtra("userId", userId);intent.putExtra("msgCount", messages.size());sendBroadcast(intent);break;case Constant.CMD_TYPE2:break;}break;case Constant.CMD82:switch (cmdType) {case Constant.CMD_TYPE1:// 收到文件传输请求switch (oprCmd) {case Constant.OPR_CMD1:// 发送广播,通知界面有文件需要传输if (!isBusyNow) {// isBusyNow = true;fileSenderUid = userId;// 保存文件发送者的id号,以便后面若接收者拒绝接收文件时可以通过该id找到发送者,并给发送者发送拒绝接收指令Person person = childrenMap.get(Integer.valueOf(userId));Intent intent = new Intent();intent.putExtra("person", person);intent.setAction(Constant.receivedSendFileRequestAction);sendBroadcast(intent);} else {// 如果当前正在收发文件则向对方发送忙指令Person person = childrenMap.get(Integer.valueOf(userId));fileSendBuffer[4] = Constant.CMD_TYPE2;fileSendBuffer[5] = Constant.OPR_CMD4;byte[] meIdBytes = ByteAndInt.int2ByteArray(me.personId);System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4);try {DatagramPacket dp = new DatagramPacket(fileSendBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress),Constant.PORT);multicastSocket.send(dp);} catch (Exception e) {e.printStackTrace();}}break;case Constant.OPR_CMD5:// 接收对方传过来的文件名信息byte[] fileNameBytes = new byte[Constant.fileNameLength];byte[] fileSizeByte = new byte[8];System.arraycopy(pkg, 10, fileNameBytes, 0,Constant.fileNameLength);System.arraycopy(pkg, 100, fileSizeByte, 0, 8);FileState fs = new FileState();fs.fileName = new String(fileNameBytes).trim();fs.fileSize = Long.valueOf(ByteAndInt.byteArrayToLong(fileSizeByte));receivedFileNames.add(fs);break;}break;case Constant.CMD_TYPE2:switch (oprCmd) {case Constant.OPR_CMD2:// 对方同意接收文件fileHandler.startSendFile();System.out.println("Start send file to remote user ...");break;case Constant.OPR_CMD3:// 对方拒绝接收文件Intent intent = new Intent();intent.setAction(Constant.remoteUserRefuseReceiveFileAction);sendBroadcast(intent);System.out.println("Remote user refuse to receive file ...");break;case Constant.OPR_CMD4:// 对方现在忙System.out.println("Remote user is busy now ...");break;}break;}break;case Constant.CMD83:// 83命令,语音通讯相关switch (cmdType) {case Constant.CMD_TYPE1:switch (oprCmd) {case Constant.OPR_CMD1:// 接收到远程语音通话请求System.out.println("Received a talk request ... ");isStopTalk = false;Person person = childrenMap.get(Integer.valueOf(userId));Intent intent = new Intent();intent.putExtra("person", person);intent.setAction(Constant.receivedTalkRequestAction);sendBroadcast(intent);break;case Constant.OPR_CMD2:// 收到关闭指令,关闭语音通话System.out.println("Received remote user stop talk cmd ... ");isStopTalk = true;Intent i = new Intent();i.setAction(Constant.remoteUserClosedTalkAction);sendBroadcast(i);break;}break;case Constant.CMD_TYPE2:switch (oprCmd) {case Constant.OPR_CMD1:// 被叫应答,开始语音通话if (!isStopTalk) {System.out.println("Begin to talk with remote user ... ");Person person = childrenMap.get(Integer.valueOf(userId));audioHandler.audioSend(person);}break;}break;}break;}}// 更新或加用户信息到用户列表中private void updatePerson(int userId, byte[] pkg) {Person person = new Person();getPerson(pkg, person);childrenMap.put(userId, person);if (!personKeys.contains(Integer.valueOf(userId)))personKeys.add(Integer.valueOf(userId));if (!children.contains(childrenMap))children.add(childrenMap);sendPersonHasChangedBroadcast();}// 关闭Socket连接private void release() {try {regBuffer[4] = Constant.CMD_TYPE3;// 把命令类型修改成注消标志,并广播发送,从所有用户中退出DatagramPacket dp = new DatagramPacket(regBuffer,Constant.bufferSize,InetAddress.getByName(Constant.MULTICAST_IP),Constant.PORT);multicastSocket.send(dp);System.out.println("Send logout cmd ...");multicastSocket.leaveGroup(InetAddress.getByName(Constant.MULTICAST_IP));multicastSocket.close();System.out.println("Socket has closed ...");} catch (Exception e) {e.printStackTrace();} finally {fileHandler.release();audioHandler.release();}}// 分析数据包并获取一个用户信息private void getPerson(byte[] pkg, Person person) {byte[] personIdBytes = new byte[4];byte[] iconIdBytes = new byte[4];byte[] nickeNameBytes = new byte[30];byte[] personIpBytes = new byte[4];System.arraycopy(pkg, 6, personIdBytes, 0, 4);System.arraycopy(pkg, 10, iconIdBytes, 0, 4);System.arraycopy(pkg, 14, nickeNameBytes, 0, 30);System.arraycopy(pkg, 44, personIpBytes, 0, 4);person.personId = ByteAndInt.byteArray2Int(personIdBytes);person.personHeadIconId = ByteAndInt.byteArray2Int(iconIdBytes);person.personNickeName = (new String(nickeNameBytes)).trim();person.ipAddress = Constant.intToIp(ByteAndInt.byteArray2Int(personIpBytes));person.timeStamp = System.currentTimeMillis();}// 注册自己到网络中public void joinOrganization() {try {if (null != multicastSocket && !multicastSocket.isClosed()) {regBuffer[4] = Constant.CMD_TYPE1;// 恢复成注册请求标志,向网络中注册自己DatagramPacket dp = new DatagramPacket(regBuffer,Constant.bufferSize,InetAddress.getByName(Constant.MULTICAST_IP),Constant.PORT);multicastSocket.send(dp);}} catch (IOException e) {e.printStackTrace();}}// 发送信息public void sendMsg(int personId, String msg) {try {Person psn = childrenMap.get(personId);if (null != psn) {System.arraycopy(ByteAndInt.int2ByteArray(me.personId), 0,msgSendBuffer, 6, 4);int msgLength = Constant.msgLength + 10;for (int i = 10; i < msgLength; i++) {msgSendBuffer[i] = 0;}byte[] msgBytes = msg.getBytes();System.arraycopy(msgBytes, 0, msgSendBuffer, 10,msgBytes.length);DatagramPacket dp = new DatagramPacket(msgSendBuffer,Constant.bufferSize,InetAddress.getByName(psn.ipAddress), Constant.PORT);multicastSocket.send(dp);}} catch (Exception e) {e.printStackTrace();}}// 向对方发送请求接收文件指令public void sendFiles(int personId, ArrayList<FileName> files) {if (personId > 0 && null != files && files.size() > 0) {try {tempUid = personId;tempFiles = files;Person person = childrenMap.get(Integer.valueOf(tempUid));fileSendBuffer[4] = Constant.CMD_TYPE1;fileSendBuffer[5] = Constant.OPR_CMD5;byte[] meIdBytes = ByteAndInt.int2ByteArray(me.personId);System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4);int fileNameLength = Constant.fileNameLength + 10;// 清除头文件包的文件名存储区域,以便写新的文件名// 把要传送的所有文件名传送给对方for (final FileName file : tempFiles) {// 收集生成要发送文件的文相关资料FileState fs = new FileState(file.fileSize, 0,file.getFileName());beSendFileNames.add(fs);byte[] fileNameBytes = file.getFileName().getBytes();for (int i = 10; i < fileNameLength; i++)fileSendBuffer[i] = 0;System.arraycopy(fileNameBytes, 0, fileSendBuffer, 10,fileNameBytes.length);// 把文件名放入头数据包System.arraycopy(ByteAndInt.longToByteArray(file.fileSize), 0,fileSendBuffer, 100, 8);DatagramPacket dp = new DatagramPacket(fileSendBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress),Constant.PORT);multicastSocket.send(dp);}// 对方发送请求接收文件指令fileSendBuffer[5] = Constant.OPR_CMD1;DatagramPacket dp = new DatagramPacket(fileSendBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress),Constant.PORT);multicastSocket.send(dp);} catch (Exception e) {e.printStackTrace();}}}// 向对方响应同意接收文件指令public void receiveFiles(String fileSavePath) {this.fileSavePath = fileSavePath;Person person = childrenMap.get(Integer.valueOf(fileSenderUid));fileSendBuffer[4] = Constant.CMD_TYPE2;fileSendBuffer[5] = Constant.OPR_CMD2;byte[] meIdBytes = ByteAndInt.int2ByteArray(me.personId);System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4);try {DatagramPacket dp = new DatagramPacket(fileSendBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress), Constant.PORT);multicastSocket.send(dp);} catch (Exception e) {e.printStackTrace();}}// 向文件发送者发送拒绝接收文件指令public void refuseReceiveFile() {// isBusyNow = false;Person person = childrenMap.get(Integer.valueOf(fileSenderUid));fileSendBuffer[4] = Constant.CMD_TYPE2;fileSendBuffer[5] = Constant.OPR_CMD3;byte[] meIdBytes = ByteAndInt.int2ByteArray(me.personId);System.arraycopy(meIdBytes, 0, fileSendBuffer, 6, 4);try {DatagramPacket dp = new DatagramPacket(fileSendBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress), Constant.PORT);multicastSocket.send(dp);} catch (Exception e) {e.printStackTrace();}}// 获得欲接收文件的文件名public ArrayList<FileState> getReceivedFileNames() {return receivedFileNames;}// 获得欲发送文件的文件名public ArrayList<FileState> getBeSendFileNames() {return beSendFileNames;}// 开始语音呼叫,向远方发送语音呼叫请求public void startTalk(int personId) {try {isStopTalk = false;talkCmdBuffer[4] = Constant.CMD_TYPE1;talkCmdBuffer[5] = Constant.OPR_CMD1;System.arraycopy(InetAddress.getByName(me.ipAddress).getAddress(), 0, talkCmdBuffer, 44, 4);Person person = childrenMap.get(Integer.valueOf(personId));DatagramPacket dp = new DatagramPacket(talkCmdBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress), Constant.PORT);multicastSocket.send(dp);} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}// 结束语音呼叫public void stopTalk(int personId) {isStopTalk = true;talkCmdBuffer[4] = Constant.CMD_TYPE1;talkCmdBuffer[5] = Constant.OPR_CMD2;Person person = childrenMap.get(Integer.valueOf(personId));try {System.arraycopy(InetAddress.getByName(me.ipAddress).getAddress(), 0, talkCmdBuffer, 44, 4);DatagramPacket dp = new DatagramPacket(talkCmdBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress), Constant.PORT);multicastSocket.send(dp);} catch (Exception e) {e.printStackTrace();}}// 接受远程语音呼叫请求,并向远程发送语音数据public void acceptTalk(int personId) {talkCmdBuffer[3] = Constant.CMD83;talkCmdBuffer[4] = Constant.CMD_TYPE2;talkCmdBuffer[5] = Constant.OPR_CMD1;try {// 发送接受语音指令System.arraycopy(InetAddress.getByName(me.ipAddress).getAddress(), 0, talkCmdBuffer, 44, 4);Person person = childrenMap.get(Integer.valueOf(personId));DatagramPacket dp = new DatagramPacket(talkCmdBuffer,Constant.bufferSize,InetAddress.getByName(person.ipAddress), Constant.PORT);multicastSocket.send(dp);audioHandler.audioSend(person);// 同时向对方发送音频数据} catch (UnknownHostException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}// =========================TCP文件传输模块==================================================================// 基于Tcp传输的文件收发模块private class FileHandler extends Thread {private ServerSocket sSocket = null;public FileHandler() {}@Overridepublic void run() {super.run();try {sSocket = new ServerSocket(Constant.PORT);System.out.println("File Handler socket started ...");while (!sSocket.isClosed() && null != sSocket) {Socket socket = sSocket.accept();socket.setSoTimeout(5000);new SaveFileToDisk(socket).start();}} catch (IOException e) {e.printStackTrace();}}// 保存接收到的数据private class SaveFileToDisk extends Thread {private Socket socket = null;public SaveFileToDisk(Socket socket) {this.socket = socket;}@Overridepublic void run() {super.run();OutputStream output = null;InputStream input = null;try {byte[] recvFileCmd = new byte[Constant.bufferSize];// 接收对方第一次发过来的数据,该数据包中包含了要发送的文件名input = socket.getInputStream();input.read(recvFileCmd);// 读取对方发过来的数据int cmdType = recvFileCmd[4];// 按协议这位为命令类型int oprCmd = recvFileCmd[5];// 操作命令if (cmdType == Constant.CMD_TYPE1&& oprCmd == Constant.OPR_CMD6) {byte[] fileNameBytes = new byte[Constant.fileNameLength];// 从收到的数据包中提取文件名System.arraycopy(recvFileCmd, 10, fileNameBytes, 0,Constant.fileNameLength);StringBuffer sb = new StringBuffer();String fName = new String(fileNameBytes).trim();sb.append(fileSavePath).append(File.separator).append(fName);// 组合成完整的文件名String fileName = sb.toString();File file = new File(fileName);// 根据获得的文件名创建文件// 定义数据接收缓冲区,准备接收对方传过来的文件内容byte[] readBuffer = new byte[Constant.readBufferSize];output = new FileOutputStream(file);// 打开文件输出流准备把接收到的内容写到文件中int readSize = 0;int length = 0;long count = 0;FileState fs = getFileStateByName(fName,receivedFileNames);while (-1 != (readSize = input.read(readBuffer))) {// 循环读取内容output.write(readBuffer, 0, readSize);// 把接收到的内容写到文件中output.flush();length += readSize;count++;if (count % 10 == 0) {fs.currentSize = length;fs.percent = ((int) ((Float.valueOf(length) / Float.valueOf(fs.fileSize)) * 100));Intent intent = new Intent();intent.setAction(Constant.fileReceiveStateUpdateAction);sendBroadcast(intent);}}fs.currentSize = length;fs.percent = ((int) ((Float.valueOf(length) / Float.valueOf(fs.fileSize)) * 100));Intent intent = new Intent();intent.setAction(Constant.fileReceiveStateUpdateAction);sendBroadcast(intent);} else {Intent intent = new Intent();intent.putExtra("msg",getString(R.string.data_receive_error));intent.setAction(Constant.dataReceiveErrorAction);sendBroadcast(intent);}} catch (Exception e) {Intent intent = new Intent();intent.putExtra("msg", e.getMessage());intent.setAction(Constant.dataReceiveErrorAction);sendBroadcast(intent);e.printStackTrace();} finally {try {if (null != input)input.close();if (null != output)output.close();if (!socket.isClosed())socket.close();} catch (Exception e1) {e1.printStackTrace();}}}}// 开始给对方发送文件public void startSendFile() {// 获得接收方信息Person person = childrenMap.get(Integer.valueOf(tempUid));final String userIp = person.ipAddress;// 组合头数据包,该数据包中包括要发送的文件名final byte[] sendFileCmd = new byte[Constant.bufferSize];for (int i = 0; i < Constant.bufferSize; i++)sendFileCmd[i] = 0;System.arraycopy(Constant.pkgHead, 0, sendFileCmd, 0, 3);sendFileCmd[3] = Constant.CMD82;sendFileCmd[4] = Constant.CMD_TYPE1;sendFileCmd[5] = Constant.OPR_CMD6;System.arraycopy(ByteAndInt.int2ByteArray(me.personId), 0,sendFileCmd, 6, 4);for (final FileName file : tempFiles) {// 采用多线程发送文件new Thread() {@Overridepublic void run() {Socket socket = null;OutputStream output = null;InputStream input = null;try {socket = new Socket(userIp, Constant.PORT);byte[] fileNameBytes = file.getFileName().getBytes();int fileNameLength = Constant.fileNameLength + 10;// 清除头文件包的文件名存储区域,以便写新的文件名for (int i = 10; i < fileNameLength; i++)sendFileCmd[i] = 0;System.arraycopy(fileNameBytes, 0, sendFileCmd,10, fileNameBytes.length);// 把文件名放入头数据包System.arraycopy(ByteAndInt.longToByteArray(file.fileSize), 0,sendFileCmd, 100, 8);output = socket.getOutputStream();// 构造一个输出流output.write(sendFileCmd);// 把头数据包发给对方output.flush();sleep(1000);// sleep 1秒钟,等待对方处理完// 定义数据发送缓冲区byte[] readBuffer = new byte[Constant.readBufferSize];// 文件读写缓存input = new FileInputStream(new File(file.fileName));// 打开一个文件输入流int readSize = 0;int length = 0;long count = 0;FileState fs = getFileStateByName(file.getFileName(), beSendFileNames);while (-1 != (readSize = input.read(readBuffer))) {// 循环把文件内容发送给对方output.write(readBuffer, 0, readSize);// 把内容写到输出流中发送给对方output.flush();length += readSize;count++;if (count % 10 == 0) {fs.currentSize = length;fs.percent = ((int) ((Float.valueOf(length) / Float.valueOf(fs.fileSize)) * 100));Intent intent = new Intent();intent.setAction(Constant.fileSendStateUpdateAction);sendBroadcast(intent);}}fs.currentSize = length;fs.percent = ((int) ((Float.valueOf(length) / Float.valueOf(fs.fileSize)) * 100));Intent intent = new Intent();intent.setAction(Constant.fileSendStateUpdateAction);sendBroadcast(intent);} catch (Exception e) {// 往界面层发送文件传输出错信息Intent intent = new Intent();intent.putExtra("msg", e.getMessage());intent.setAction(Constant.dataSendErrorAction);sendBroadcast(intent);e.printStackTrace();} finally {try {if (null != output)output.close();if (null != input)input.close();if (!socket.isClosed())socket.close();} catch (Exception e1) {e1.printStackTrace();}}}}.start();}}// 根据文件名从文件状态列表中获得该文件状态private FileState getFileStateByName(String fileName,ArrayList<FileState> fileStates) {for (FileState fileState : fileStates) {if (fileState.fileName.equals(fileName)) {return fileState;}}return null;}public void release() {try {System.out.println("File handler socket closed ...");if (null != sSocket)sSocket.close();} catch (IOException e) {e.printStackTrace();}}}// =========================TCP文件传输模块结束==============================================================// =========================TCP语音传输模块==================================================================// 基于Tcp语音传输模块private class AudioHandler extends Thread {private ServerSocket sSocket = null;// private G711Codec codec;public AudioHandler() {}@Overridepublic void run() {super.run();try {sSocket = new ServerSocket(Constant.AUDIO_PORT);// 监听音频端口System.out.println("Audio Handler socket started ...");while (!sSocket.isClosed() && null != sSocket) {Socket socket = sSocket.accept();socket.setSoTimeout(5000);audioPlay(socket);}} catch (IOException e) {e.printStackTrace();}}// 用来启动音频播放子线程public void audioPlay(Socket socket) {new AudioPlay(socket).start();}// 用来启动音频发送子线程public void audioSend(Person person) {new AudioSend(person).start();}// 音频播线程public class AudioPlay extends Thread {Socket socket = null;public AudioPlay(Socket socket) {this.socket = socket;// android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);}@Overridepublic void run() {super.run();try {InputStream is = socket.getInputStream();// 获得音频缓冲区大小int bufferSize = android.media.AudioTrack.getMinBufferSize(8000,AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT);// 获得音轨对象AudioTrack player = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT, bufferSize,AudioTrack.MODE_STREAM);// 设置喇叭音量player.setStereoVolume(1.0f, 1.0f);// 开始播放声音player.play();byte[] audio = new byte[160];// 音频读取缓存int length = 0;while (!isStopTalk) {length = is.read(audio);// 从网络读取音频数据if (length > 0 && length % 2 == 0) {// for(int// i=0;i<length;i++)audio[i]=(byte)(audio[i]*2);//音频放大1倍player.write(audio, 0, length);// 播放音频数据}}player.stop();is.close();socket.close();} catch (IOException e) {e.printStackTrace();}}}// 音频发送线程public class AudioSend extends Thread {Person person = null;public AudioSend(Person person) {this.person = person;// android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);}@Overridepublic void run() {super.run();Socket socket = null;OutputStream os = null;AudioRecord recorder = null;try {socket = new Socket(person.ipAddress,Constant.AUDIO_PORT);socket.setSoTimeout(5000);os = socket.getOutputStream();// 获得录音缓冲区大小int bufferSize = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT);// 获得录音机对象recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000,AudioFormat.CHANNEL_CONFIGURATION_MONO,AudioFormat.ENCODING_PCM_16BIT, bufferSize * 10);recorder.startRecording();// 开始录音byte[] readBuffer = new byte[640];// 录音缓冲区int length = 0;while (!isStopTalk) {length = recorder.read(readBuffer, 0, 640);// 从mic读取音频数据if (length > 0 && length % 2 == 0) {os.write(readBuffer, 0, length);// 写入到输出流,把音频数据通过网络发送给对方}}recorder.stop();os.close();socket.close();} catch (IOException e) {e.printStackTrace();}}}public void release() {try {System.out.println("Audio handler socket closed ...");if (null != sSocket)sSocket.close();} catch (IOException e) {e.printStackTrace();}}}// =========================TCP语音传输模块结束==================================================================}// ========================协议分析与通讯模块结束=======================================================
}


这篇关于仿飞鸽传书的Android版本的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863822

相关文章

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

你的华为手机升级了吗? 鸿蒙NEXT多连推5.0.123版本变化颇多

《你的华为手机升级了吗?鸿蒙NEXT多连推5.0.123版本变化颇多》现在的手机系统更新可不仅仅是修修补补那么简单了,华为手机的鸿蒙系统最近可是动作频频,给用户们带来了不少惊喜... 为了让用户的使用体验变得很好,华为手机不仅发布了一系列给力的新机,还在操作系统方面进行了疯狂的发力。尤其是近期,不仅鸿蒙O

什么是 Ubuntu LTS?Ubuntu LTS和普通版本区别对比

《什么是UbuntuLTS?UbuntuLTS和普通版本区别对比》UbuntuLTS是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性,与常规的Ubuntu版本相比,LTS版... 如果你正打算安装 Ubuntu 系统,可能会被「LTS 版本」和「普通版本」给搞得一头雾水吧?尤其是对于刚入

windows端python版本管理工具pyenv-win安装使用

《windows端python版本管理工具pyenv-win安装使用》:本文主要介绍如何通过git方式下载和配置pyenv-win,包括下载、克隆仓库、配置环境变量等步骤,同时还详细介绍了如何使用... 目录pyenv-win 下载配置环境变量使用 pyenv-win 管理 python 版本一、安装 和

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk