分享

Android通过USB与PC通信

 ada_lib 2019-11-21

        最近项目中有一个功能需要用到Android与PC端同步数据。查阅了相关资料后,采取了一种建立在adb基础之上的Usb通信方式:由于adb可以将Usb模拟为网卡,所以可以利用socket通信的方式实现Android与PC机的通信,以完成同步功能。

一、Android与PC通信的实现

        在《PC客户端与Android服务端的Sockect同步通信》一文详细介绍了建立在adb基础之上的usb(socket)实现的具体方法。大体上的思路如下:

①Android作为server,侦探socket连接请求;添加一个服务类来实现侦听功能。

②PC端作为Client,请求建立socket连接。

③Android端添加一个广播接收类,接受PC端通过Adb发送的广播消息,以启动或者停止①中添加的服务。

④PC端通过Adb发送广播消息通知Android端启动或停止用来侦听socket连接的服务。

        1、PC端通过Adb发送广播,使Android端开启侦听Socket的服务,然后再请求连接。代码如下
  1. //连接
  2. public bool DoConnect()
  3. {
  4. string strCmd = 'adb shell am broadcast -a NotifyServiceStop';
  5. Execute(strCmd, wait_ms);
  6. Thread.Sleep(wait_ms);
  7. strCmd = 'adb forward tcp:12580 tcp:10086';
  8. Execute(strCmd, wait_ms);
  9. Thread.Sleep(wait_ms);
  10. strCmd = 'adb shell am broadcast -a NotifyServiceStart';
  11. Execute(strCmd, wait_ms);
  12. Thread.Sleep(wait_ms);
  13. IPAddress ipaddress = IPAddress.Parse('127.0.0.1');
  14. tcpClient.Connect(ipaddress, 12580);
  15. Thread.Sleep(wait_ms);
  16. if (tcpClient != null)
  17. {
  18. NetworkStream networkkStream = tcpClient.GetStream();
  19. networkkStream.ReadTimeout = timeOut;
  20. networkkStream.WriteTimeout = timeOut;
  21. reader = new BinaryReader(networkkStream);
  22. writer = new BinaryWriter(networkkStream);
  23. return true;
  24. }
  25. else
  26. return false;
  27. }
         其中,Execute()函数用来执行cmd命令,
  1. private string Execute(string command, int seconds)
  2. {
  3. string output = ''; //输出字符串
  4. if (command != null && !command.Equals(''))
  5. {
  6. Process process = new Process();//创建进程对象
  7. ProcessStartInfo startInfo = new ProcessStartInfo();
  8. startInfo.FileName = 'cmd.exe';//设定需要执行的命令
  9. startInfo.Arguments = '/C ' + command;//“/C”表示执行完命令后马上退出
  10. startInfo.UseShellExecute = false;//不使用系统外壳程序启动
  11. startInfo.RedirectStandardInput = false;//不重定向输入
  12. startInfo.RedirectStandardOutput = true; //重定向输出
  13. startInfo.CreateNoWindow = true;//不创建窗口
  14. process.StartInfo = startInfo;
  15. try
  16. {
  17. if (process.Start())//开始进程
  18. {
  19. if (seconds == 0)
  20. {
  21. process.WaitForExit();//这里无限等待进程结束
  22. }
  23. else
  24. {
  25. process.WaitForExit(seconds); //等待进程结束,等待时间为指定的毫秒
  26. }
  27. output = process.StandardOutput.ReadToEnd();//读取进程的输出
  28. }
  29. }
  30. finally
  31. {
  32. if (process != null)
  33. process.Close();
  34. }
  35. }
  36. return output;
  37. }

        2、在Android端,首先需要添加一个广播接收类来处理广播消息,当接受到广播消息是“启动”消息时启动侦听服务,“关闭“消息时停止服务:
  1. public class ServiceBroadcastReceiver extends BroadcastReceiver {
  2. private static String START_ACTION = 'NotifyServiceStart';
  3. private static String STOP_ACTION = 'NotifyServiceStop';
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. Log.d(ConnectService.TAG, Thread.currentThread().getName() + '---->'
  7. + 'ServiceBroadcastReceiver onReceive');
  8. String action = intent.getAction();
  9. if (START_ACTION.equalsIgnoreCase(action)) {
  10. context.startService(new Intent(context, ConnectService.class));
  11. Log.d(ConnectService.TAG, Thread.currentThread().getName() + '---->'
  12. + 'ServiceBroadcastReceiver onReceive start end');
  13. } else if (STOP_ACTION.equalsIgnoreCase(action)) {
  14. context.stopService(new Intent(context, ConnectService.class));
  15. Log.d(ConnectService.TAG, Thread.currentThread().getName() + '---->'
  16. + 'ServiceBroadcastReceiver onReceive stop end');
  17. }
  18. }
  19. }

       然后,添加一个服务类,用来侦听客户端的连接请求:

  1. public class ConnectService extends Service{
  2. public static final String TAG = 'chl';
  3. public static Boolean mainThreadFlag = true;
  4. public static Boolean ioThreadFlag = true;
  5. ServerSocket serverSocket = null;
  6. final int SERVER_PORT = 10086;
  7. @Override
  8. public IBinder onBind(Intent intent)
  9. {
  10. return null;
  11. }
  12. @Override
  13. public void onCreate()
  14. {
  15. super.onCreate();
  16. Log.d(TAG, 'androidService--->onCreate()');
  17. }
  18. @Override
  19. public int onStartCommand(Intent intent, int flags, int startId)
  20. {
  21. mainThreadFlag = true;
  22. new Thread()
  23. {
  24. public void run()
  25. {
  26. doListen();
  27. };
  28. }.start();
  29. return START_NOT_STICKY;
  30. }
  31. private void doListen()
  32. {
  33. serverSocket = null;
  34. try
  35. {
  36. serverSocket = new ServerSocket(SERVER_PORT);
  37. while (mainThreadFlag)
  38. {
  39. Socket socket = serverSocket.accept();
  40. new Thread(new ThreadReadWriterIOSocket(this, socket)).start();
  41. }
  42. } catch (IOException e)
  43. {
  44. e.printStackTrace();
  45. }
  46. }
  47. @Override
  48. public void onDestroy()
  49. {
  50. super.onDestroy();
  51. mainThreadFlag = false;
  52. ioThreadFlag = false;
  53. try
  54. {
  55. if (serverSocket != null)
  56. serverSocket.close();
  57. } catch (IOException e)
  58. {
  59. e.printStackTrace();
  60. }
  61. }
  62. }
         还需要添加一个线程类,来处理客户端的连接请求。建立一个连接后,启动一个此线程来完成与客户端的通信。在这里可以定义与实际需求相应的通信协议。
  1. public class ThreadReadWriterIOSocket implements Runnable{
  2. private Socket client;
  3. private Context context;
  4. private PigProtocol pigProtocol;
  5. public ThreadReadWriterIOSocket(Context context, Socket client)
  6. {
  7. this.client = client;
  8. this.context = context;
  9. pigProtocol = new PigProtocol();
  10. }
  11. @Override
  12. public void run(){
  13. BufferedOutputStream out;
  14. BufferedInputStream in;
  15. try {
  16. Header header = null;
  17. out = new BufferedOutputStream(client.getOutputStream());
  18. in = new BufferedInputStream(client.getInputStream());
  19. ConnectService.ioThreadFlag = true;
  20. while (ConnectService.ioThreadFlag){
  21. try {
  22. if(!client.isConnected()){
  23. break;
  24. }
  25. header = pigProtocol.readHeaderFromSocket(in);
  26. switch (header.CmdId) {
  27. case 0x0001:
  28. //
  29. break;
  30. case 0x0002:
  31. //
  32. break;
  33. default:
  34. break;
  35. }
  36. }
  37. catch (Exception e)
  38. {
  39. // TODO: handle exception
  40. e.printStackTrace();
  41. }
  42. }
  43. out.close();
  44. in.close();
  45. }
  46. catch (Exception e)
  47. {
  48. // TODO: handle exception
  49. e.printStackTrace();
  50. }
  51. finally
  52. {
  53. try
  54. {
  55. if (client != null)
  56. {
  57. client.close();
  58. }
  59. } catch (IOException e)
  60. {
  61. e.printStackTrace();
  62. }
  63. }
  64. }
  65. }
         最后,还需要修改程序清单manifest.xml,加入代码,使程序能够接收到广播消息,并且指定处理消息的类。注意,在指定接收广播消息类、服务类时,一定要指出完整名称,如com.example.connect.ServiceBroadcastReceiver。
  1. <!-- 添加权限 -->
  2. <uses-permission android:name='android.permission.INTERNET'/>
  3. <uses-permission android:name='android.permission.WRITE_EXTERNAL_STORAGE'/>
  4. <application>
  5. <!-- 指定接受广播的类 -->
  6. <receiver android:name='.ServiceBroadcastReceiver'>
  7. <intent-filter >
  8. <action android:name='NotifyServiceStart'/>
  9. <action android:name='NotifyServiceStop' />
  10. </intent-filter>
  11. </receiver>
  12. <!--指定服务类-->
  13. <service
  14. android:name='.ConnectService'>
  15. </service>
  16. </application>

二、Android与PC端传通过socket传递对象

  有了上面的工作,就可以实现Android与PC端通过socket(usb)传递简单的字符串了。但是在实际使用过程中,我们更多的往往是传递对象而不是简单的字符串。最简单的方法是定义相应的协议,在发送端将对象拼接为字符串,然后在接收将接收到的字符串端拆分组合为对象。但是这样实现起来不是很方便,《C#(服务器)与Java(客户端)通过Socket传递对象》一文介绍了利用Json来传递对象的方法。利用文中介绍的方法可以很方便的实现对象传递。《DataContractJsonSerializer类操作json类型数据》一文介绍了C#端json类型数据的具体使用方法。

  我们知道,C#和Java都是完全面向对象的语言,它们都提供了List<T>泛型,所以我们可以利用List<T>泛型实现一次发送一组对象。

  1、在PC端,首先需要完成Json类型与List<T>类型的相互转换。转换得到的Json字符串通过socket发送给Android端,或者读取得到的Json字符串转换为相应的泛型对象,这样就实现了一次发送一组对象的功能。下面是Json字符串与泛型对象相互转换的实现过程,实现了Json类型字符串与C#对象(包括List<T>类型的对象)的相互转换。

  1. public static string Obj2Json<T>(T data)
  2. {
  3. try
  4. {
  5. DataContractJsonSerializer json = new DataContractJsonSerializer(data.GetType());
  6. using (MemoryStream ms = new MemoryStream())
  7. {
  8. json.WriteObject(ms, data);
  9. return Encoding.UTF8.GetString(ms.ToArray());
  10. }
  11. }
  12. catch (System.Exception ex)
  13. {
  14. throw ex;
  15. }
  16. }
  17. public static object Json2Obj(string strJson, Type t)
  18. {
  19. try
  20. {
  21. DataContractJsonSerializer json = new DataContractJsonSerializer(t);
  22. using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(strJson)))
  23. {
  24. return json.ReadObject(ms);
  25. }
  26. }
  27. catch (System.Exception ex)
  28. {
  29. throw ex;
  30. }
  31. }

        2、Android端Json与对象的相互转换通过开源项目google-gson实现。下载包后在项目中导入,并且添加引用import com.google.gson.Gson;就可以使用了。

        对象转换为Json字符串:

  1. try {
  2. String strTmp = gson.toJson(content);
  3. out.write(strTmp.getBytes());
  4. out.flush();
  5. } catch (Exception e) {
  6. // TODO: handle exception
  7. Log.d('error','writeHeaderFromSocket' + e.getMessage());
  8. }

        Json字符串转换为对象:
  1. byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];
  2. try
  3. {
  4. int numReadedBytes = in.read(tempbuffer, 0, MAX_BUFFER_BYTES);
  5. String strJson = new String(tempbuffer, 0, numReadedBytes, 'utf-8');
  6. MyType myType = gson.fromJson(strJson, MyType.class);
  7. tempbuffer = null;
  8. } catch (Exception e)
  9. {
  10. e.printStackTrace();
  11. }

        Json字符串转换为List<T>:
  1. byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];
  2. try
  3. {
  4. int numReadedBytes = in.read(tempbuffer, 0, MAX_BUFFER_BYTES);
  5. String strJson = new String(tempbuffer, 0, numReadedBytes, 'utf-8');
  6. List<MyType> listMyType = gson.fromJson(strJson,
  7. new TypeToken<List<MyType>>(){}.getType());
  8. tempbuffer = null;
  9. } catch (Exception e)
  10. {
  11. e.printStackTrace();
  12. }

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多