Android通过USB与PC通信

        最近项目中有一个功能需要用到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的服务,然后再请求连接。代码如下

//连接
public bool DoConnect()
{
	string strCmd = "adb shell am broadcast -a NotifyServiceStop";
	Execute(strCmd, wait_ms);
	Thread.Sleep(wait_ms);
	strCmd = "adb forward tcp:12580 tcp:10086";
	Execute(strCmd, wait_ms);
	Thread.Sleep(wait_ms);
	strCmd = "adb shell am broadcast -a NotifyServiceStart";
	Execute(strCmd, wait_ms);
	Thread.Sleep(wait_ms);

	IPAddress ipaddress = IPAddress.Parse("127.0.0.1");

	tcpClient.Connect(ipaddress, 12580);
	Thread.Sleep(wait_ms);
	if (tcpClient != null)
	{
		NetworkStream networkkStream = tcpClient.GetStream();
		networkkStream.ReadTimeout = timeOut;
		networkkStream.WriteTimeout = timeOut;
		reader = new BinaryReader(networkkStream);
		writer = new BinaryWriter(networkkStream);
		return true;
	}
	else
		return false;
}
         其中,Execute()函数用来执行cmd命令,
private string Execute(string command, int seconds)
{
	string output = ""; //输出字符串
	if (command != null && !command.Equals(""))
	{
		Process process = new Process();//创建进程对象
		ProcessStartInfo startInfo = new ProcessStartInfo();
		startInfo.FileName = "cmd.exe";//设定需要执行的命令
		startInfo.Arguments = "/C " + command;//“/C”表示执行完命令后马上退出
		startInfo.UseShellExecute = false;//不使用系统外壳程序启动
		startInfo.RedirectStandardInput = false;//不重定向输入
		startInfo.RedirectStandardOutput = true; //重定向输出
		startInfo.CreateNoWindow = true;//不创建窗口
		process.StartInfo = startInfo;
		try
		{
			if (process.Start())//开始进程
			{
				if (seconds == 0)
				{
					process.WaitForExit();//这里无限等待进程结束
				}
				else
				{
					process.WaitForExit(seconds); //等待进程结束,等待时间为指定的毫秒
				}
				output = process.StandardOutput.ReadToEnd();//读取进程的输出
			}
		}
		finally
		{
			if (process != null)
				process.Close();
		}
	}
	return output;
}

        2、 在Android端,首先需要添加一个广播接收类来处理广播消息,当接受到广播消息是“启动”消息时启动侦听服务,“关闭“消息时停止服务:

public class ServiceBroadcastReceiver extends BroadcastReceiver {

	private static String START_ACTION = "NotifyServiceStart";
	private static String STOP_ACTION = "NotifyServiceStop";

	@Override
	public void onReceive(Context context, Intent intent) {
		Log.d(ConnectService.TAG, Thread.currentThread().getName() + "---->"  
                + "ServiceBroadcastReceiver onReceive");  
  
        String action = intent.getAction();  
        if (START_ACTION.equalsIgnoreCase(action)) {  
            context.startService(new Intent(context, ConnectService.class));  
  
            Log.d(ConnectService.TAG, Thread.currentThread().getName() + "---->"  
                    + "ServiceBroadcastReceiver onReceive start end");  
        } else if (STOP_ACTION.equalsIgnoreCase(action)) {  
            context.stopService(new Intent(context, ConnectService.class));  
            Log.d(ConnectService.TAG, Thread.currentThread().getName() + "---->"  
                    + "ServiceBroadcastReceiver onReceive stop end");  
        }
	}
}

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

public class ConnectService extends Service{
	
	public static final String TAG = "chl";
	public static Boolean mainThreadFlag = true;
	public static Boolean ioThreadFlag = true;
	ServerSocket serverSocket = null;
	final int SERVER_PORT = 10086;
	
	@Override
	public IBinder onBind(Intent intent)
	{
		return null;
	}
		
	@Override
	public void onCreate()
	{
		super.onCreate();
		Log.d(TAG, "androidService--->onCreate()");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId)
	{
		mainThreadFlag = true;
		new Thread()
		{
			public void run()
			{
				doListen();
			};
		}.start();
		return START_NOT_STICKY;
	}
	
	private void doListen()
	{
		serverSocket = null;
		try
		{
			serverSocket = new ServerSocket(SERVER_PORT);
			while (mainThreadFlag)
			{
				Socket socket = serverSocket.accept();
				new Thread(new ThreadReadWriterIOSocket(this, socket)).start();
			}
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
		mainThreadFlag = false;
		ioThreadFlag = false;	
		try
		{
			if (serverSocket != null) 
				serverSocket.close();
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}
}
         还需要添加一个线程类,来处理客户端的连接请求。建立一个连接后,启动一个此线程来完成与客户端的通信。在这里可以定义与实际需求相应的通信协议。

public class ThreadReadWriterIOSocket implements Runnable{
	
	private Socket client;
	private Context context;	
	private PigProtocol pigProtocol;
		
	public ThreadReadWriterIOSocket(Context context, Socket client)
	{
		this.client = client;
		this.context = context;
		pigProtocol = new PigProtocol();
	}
	
	@Override
	public void run(){
		
		BufferedOutputStream out;
	 	BufferedInputStream in;
		
		try {
			
			Header header = null;
			out = new BufferedOutputStream(client.getOutputStream());
			in = new BufferedInputStream(client.getInputStream());
			ConnectService.ioThreadFlag = true;
			while (ConnectService.ioThreadFlag){
				try {
					
					if(!client.isConnected()){
						break;
					}
					
					header = pigProtocol.readHeaderFromSocket(in);
					switch (header.CmdId) {
					case 0x0001:
						//
						break;
					case 0x0002:
						//
						break;
					default:
						break;
					}
				} 
				catch (Exception e)
				{
					// TODO: handle exception
					e.printStackTrace();
				}
			}
			out.close();
			in.close();
		} 
		catch (Exception e) 
		{
			// TODO: handle exception
			e.printStackTrace();
		}
		finally
		{
			try
			{
				if (client != null)
				{
					client.close();
				}
			} catch (IOException e)
			{
				e.printStackTrace();
			}
		}
	}
}
         最后,还需要修改程序清单manifest.xml,加入代码,使程序能够接收到广播消息,并且指定处理消息的类。注意,在指定接收广播消息类、服务类时,一定要指出完整名称,如com.example.connect.ServiceBroadcastReceiver。

 <!--    添加权限    -->
 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
 
 <application>
     <!--    指定接受广播的类    -->
     <receiver android:name=".ServiceBroadcastReceiver">
         <intent-filter >
             <action android:name="NotifyServiceStart"/>
             <action android:name="NotifyServiceStop" />
         </intent-filter>
     </receiver>
	 
     <!--指定服务类-->
     <service 
         android:name=".ConnectService">       
     </service>
 </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>类型的对象)的相互转换。

	public static string Obj2Json<T>(T data)
	{
		try
		{
			DataContractJsonSerializer json = new DataContractJsonSerializer(data.GetType());
			using (MemoryStream ms = new MemoryStream())
			{
				json.WriteObject(ms, data);
				return Encoding.UTF8.GetString(ms.ToArray());
			}
		}
		catch (System.Exception ex)
		{
			throw ex;
		}
	}

	public static object Json2Obj(string strJson, Type t)
	{
		try
		{
			DataContractJsonSerializer json = new DataContractJsonSerializer(t);
			using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(strJson)))
			{
				return json.ReadObject(ms);
			}
		}
		catch (System.Exception ex)
		{
			throw ex;
		}
	}

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

        对象转换为Json字符串:

	try {
			
		String strTmp = gson.toJson(content);
		out.write(strTmp.getBytes());
		out.flush();
	} catch (Exception e) {
		// TODO: handle exception
		Log.d("error","writeHeaderFromSocket" + e.getMessage());
	}

        Json字符串转换为对象:

	byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];
	try
	{
		int numReadedBytes = in.read(tempbuffer, 0, MAX_BUFFER_BYTES);
		String strJson = new String(tempbuffer, 0, numReadedBytes, "utf-8");
		MyType myType = gson.fromJson(strJson, MyType.class);

		tempbuffer = null;
	} catch (Exception e)
	{
		e.printStackTrace();
	}

        Json字符串转换为List<T>:

	byte[] tempbuffer = new byte[MAX_BUFFER_BYTES];
	try
	{
		int numReadedBytes = in.read(tempbuffer, 0, MAX_BUFFER_BYTES);
		String strJson = new String(tempbuffer, 0, numReadedBytes, "utf-8");
		List<MyType> listMyType = gson.fromJson(strJson, 
				new TypeToken<List<MyType>>(){}.getType());
		tempbuffer = null;

	} catch (Exception e)
	{
		e.printStackTrace();
	}

 

  • 5
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值