第一次写博客,挺激动的。由于这段时间在研究android设备之间的通信问题,上网找了很多资料,都没有一个很好的例子,只是泛泛而谈。要不就是只有通过WIFi或者USB连接PC和android设备的通信,这里就给大家讲一讲android设备间的通信。
demo下载连接:http://download.csdn.net/detail/xifei66/9759238
首先是一个简单的布局activity_chat.xml,就是后面进行通信的交互界面
<LinearLayout xmlns:android="
http://schemas.android.com/apk/res/android"
xmlns:tools=" http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:id="@+id/content_text"/>
</ScrollView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/screenshot"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/input_edittext"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send"
android:id="@+id /send_button"
android:onClick="onButtonClick"
/>
</LinearLayout>
</LinearLayout>
xmlns:tools=" http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:orientation="vertical"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:id="@+id/content_text"/>
</ScrollView>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/screenshot"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/input_edittext"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send"
android:id="@+id /send_button"
android:onClick="onButtonClick"
/>
</LinearLayout>
</LinearLayout>
因为android设备之间的连接涉及到主从设备的问题,所以在这边有一个区分,device包下有BaseChatActivity,ChatAtivity,ConnectActivity
public class ConnectActivity extends AppCompatActivity {
public static final String DEVICE_EXTRA_KEY = "device";
private UsbManager mUsbManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
}
@Override
protected void onResume() {
super.onResume();
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
if (deviceList == null || deviceList.size() == 0) {
final Intent intent=new Intent(this, InfoActivity.class);
startActivity(intent);
finish();
return;
}
if (searchForUsbAccessory(deviceList)) {
return;
}
for (UsbDevice device:deviceList.values()) {
initAccessory(device);
}
finish();
}
private boolean searchForUsbAccessory(final HashMap<String, UsbDevice> deviceList) {
for (UsbDevice device:deviceList.values()) {
if (isUsbAccessory(device)) {
final Intent intent=new Intent(this,ChatActivity.class);
intent.putExtra(DEVICE_EXTRA_KEY, device);
startActivity(intent);
finish();
return true;
}
}
return false;
}
private boolean isUsbAccessory(final UsbDevice device) {
return (device.getProductId() == 0x2d00) || (device.getProductId() == 0x2d01);
}
private boolean initAccessory(final UsbDevice device) {
final UsbDeviceConnection connection = mUsbManager.openDevice(device);
if (connection == null) {
return false;
}
initStringControlTransfer(connection, 0, "quandoo"); // MANUFACTURER
initStringControlTransfer(connection, 1, "Android2AndroidAccessory"); // MODEL
initStringControlTransfer(connection, 2, "showcasing android2android USB communication"); // DESCRIPTION
initStringControlTransfer(connection, 3, "0.1"); // VERSION
initStringControlTransfer(connection, 4, " http://quandoo.de"); // URI
initStringControlTransfer(connection, 5, "42"); // SERIAL
connection.controlTransfer(0x40, 53, 0, 0, new byte[]{}, 0, Constants.USB_TIMEOUT_IN_MS);
connection.close();
return true;
}
private void initStringControlTransfer(final UsbDeviceConnection deviceConnection,
final int index,
final String string) {
deviceConnection.controlTransfer(0x40, 52, 0, index, string.getBytes(), string.length(), Constants.USB_TIMEOUT_IN_MS);
}
}
public static final String DEVICE_EXTRA_KEY = "device";
private UsbManager mUsbManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
}
@Override
protected void onResume() {
super.onResume();
final HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
if (deviceList == null || deviceList.size() == 0) {
final Intent intent=new Intent(this, InfoActivity.class);
startActivity(intent);
finish();
return;
}
if (searchForUsbAccessory(deviceList)) {
return;
}
for (UsbDevice device:deviceList.values()) {
initAccessory(device);
}
finish();
}
private boolean searchForUsbAccessory(final HashMap<String, UsbDevice> deviceList) {
for (UsbDevice device:deviceList.values()) {
if (isUsbAccessory(device)) {
final Intent intent=new Intent(this,ChatActivity.class);
intent.putExtra(DEVICE_EXTRA_KEY, device);
startActivity(intent);
finish();
return true;
}
}
return false;
}
private boolean isUsbAccessory(final UsbDevice device) {
return (device.getProductId() == 0x2d00) || (device.getProductId() == 0x2d01);
}
private boolean initAccessory(final UsbDevice device) {
final UsbDeviceConnection connection = mUsbManager.openDevice(device);
if (connection == null) {
return false;
}
initStringControlTransfer(connection, 0, "quandoo"); // MANUFACTURER
initStringControlTransfer(connection, 1, "Android2AndroidAccessory"); // MODEL
initStringControlTransfer(connection, 2, "showcasing android2android USB communication"); // DESCRIPTION
initStringControlTransfer(connection, 3, "0.1"); // VERSION
initStringControlTransfer(connection, 4, " http://quandoo.de"); // URI
initStringControlTransfer(connection, 5, "42"); // SERIAL
connection.controlTransfer(0x40, 53, 0, 0, new byte[]{}, 0, Constants.USB_TIMEOUT_IN_MS);
connection.close();
return true;
}
private void initStringControlTransfer(final UsbDeviceConnection deviceConnection,
final int index,
final String string) {
deviceConnection.controlTransfer(0x40, 52, 0, index, string.getBytes(), string.length(), Constants.USB_TIMEOUT_IN_MS);
}
然后
public class ChatActivity extends BaseChatActivity {
private final AtomicBoolean keepThreadAlive = new AtomicBoolean(true);
private final List<String> sendBuffer = new ArrayList<>();
@Override
public void sendString(final String string) {
sendBuffer.add(string);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new CommunicationRunnable()).start();
}
private class CommunicationRunnable implements Runnable {
@Override
public void run() {
final UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
final UsbDevice device=getIntent().getParcelableExtra(ConnectActivity.DEVICE_EXTRA_KEY);
UsbEndpoint endpointIn = null;
UsbEndpoint endpointOut = null;
final UsbInterface usbInterface = device.getInterface(0);
for (int i = 0; i < device.getInterface(0).getEndpointCount(); i++) {
final UsbEndpoint endpoint = device.getInterface(0).getEndpoint(i);
if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
endpointIn = endpoint;
}
if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
endpointOut = endpoint;
}
}
if (endpointIn == null) {
printLineToUI("Input Endpoint not found");
return;
}
if (endpointOut == null) {
printLineToUI("Output Endpoint not found");
return;
}
final UsbDeviceConnection connection = usbManager.openDevice(device);
if (connection == null) {
printLineToUI("Could not open device");
return;
}
final boolean claimResult = connection.claimInterface(usbInterface, true);
if (!claimResult) {
printLineToUI("Could not claim device");
} else {
final byte buff[] = new byte[Constants.BUFFER_SIZE_IN_BYTES];
printLineToUI("Claimed interface - ready to communicate");
while (keepThreadAlive.get()) {
final int bytesTransferred = connection.bulkTransfer(endpointIn, buff, buff.length, Constants.USB_TIMEOUT_IN_MS);
if (bytesTransferred > 0) {
printLineToUI("host> "+new String(buff, 0, bytesTransferred));
}
synchronized (sendBuffer) {
if (sendBuffer.size()>0) {
final byte[] sendBuff=sendBuffer.get(0).toString().getBytes();
connection.bulkTransfer(endpointOut, sendBuff, sendBuff.length, Constants.USB_TIMEOUT_IN_MS);
sendBuffer.remove(0);
}
}
}
}
connection.releaseInterface(usbInterface);
connection.close();
}
}
@Override
protected void onStop() {
super.onStop();
keepThreadAlive.set(false);
}
}
接着是
public abstract class BaseChatActivity extends AppCompatActivity {
TextView contentTextView;
EditText inputMessage;
Button input;
public abstract void sendString(final String string);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
contentTextView= (TextView) findViewById( R.id.content_text);
inputMessage= (EditText) findViewById( R.id.input_edittext);
input= (Button)findViewById( R.id.send_button);
//监听发送消息事件
input.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final String inputString = inputMessage.getText().toString();
if (inputString.length() == 0) {
return;
}
sendString(inputString);
printLineToUI(getString(R.string.local_prompt2) + inputString);
inputMessage.setText("");
}
});
}
public void printLineToUI(final String line) {
runOnUiThread(new Runnable() {
@Override
public void run() {
contentTextView.setText(contentTextView.getText() + "\n" + line);
}
});
}
}
TextView contentTextView;
EditText inputMessage;
Button input;
public abstract void sendString(final String string);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
contentTextView= (TextView) findViewById( R.id.content_text);
inputMessage= (EditText) findViewById( R.id.input_edittext);
input= (Button)findViewById( R.id.send_button);
//监听发送消息事件
input.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final String inputString = inputMessage.getText().toString();
if (inputString.length() == 0) {
return;
}
sendString(inputString);
printLineToUI(getString(R.string.local_prompt2) + inputString);
inputMessage.setText("");
}
});
}
public void printLineToUI(final String line) {
runOnUiThread(new Runnable() {
@Override
public void run() {
contentTextView.setText(contentTextView.getText() + "\n" + line);
}
});
}
}
在host包下同样有BaseChatActivity,ChatAtivity,ConnectActivity三个类,方法和上面一样,就不重复写了
在main包下有
public abstract class AccessoryCommunicator {
private UsbManager usbManager;
private Context context;
private Handler sendHandler;
private ParcelFileDescriptor fileDescriptor;
private FileInputStream inStream;
private FileOutputStream outStream;
private boolean running;
public AccessoryCommunicator(final Context context) {
this.context = context;
usbManager = (UsbManager) this.context.getSystemService(Context.USB_SERVICE);
final UsbAccessory[] accessoryList = usbManager.getAccessoryList();
if (accessoryList == null || accessoryList.length == 0) {
onError("no accessory found");
} else {
openAccessory(accessoryList[0]);
}
}
public void send(byte[] payload) {
if (sendHandler != null) {
Message msg = sendHandler.obtainMessage();
msg.obj = payload;
sendHandler.sendMessage(msg);
}
}
private void receive(final byte[] payload, final int length) {
onReceive(payload, length);
}
public abstract void onReceive(final byte[] payload, final int length);
public abstract void onError(String msg);
public abstract void onConnected();
public abstract void onDisconnected();
private class CommunicationThread extends Thread {
@Override
public void run() {
running = true;
while (running) {
byte[] msg = new byte[Constants.BUFFER_SIZE_IN_BYTES];
try {
//Handle incoming messages
int len = inStream.read(msg);
while (inStream != null && len > 0 && running) {
receive(msg, len);
Thread.sleep(10);
len = inStream.read(msg);
}
} catch (final Exception e) {
onError("USB Receive Failed " + e.toString() + "\n");
closeAccessory();
}
}
}
}
private void openAccessory(UsbAccessory accessory) {
fileDescriptor = usbManager.openAccessory(accessory);
if (fileDescriptor != null) {
FileDescriptor fd = fileDescriptor.getFileDescriptor();
inStream = new FileInputStream(fd);
outStream = new FileOutputStream(fd);
new CommunicationThread().start();
sendHandler = new Handler() {
public void handleMessage(Message msg) {
try {
outStream.write((byte[]) msg.obj);
} catch (final Exception e) {
onError("USB Send Failed " + e.toString() + "\n");
}
}
};
onConnected();
} else {
onError("could not connect");
}
}
public void closeAccessory() {
running = false;
try {
if (fileDescriptor != null) {
fileDescriptor.close();
}
} catch (IOException e) {
} finally {
fileDescriptor = null;
}
onDisconnected();
}
}
然后是
public class Constants {
public static final int USB_TIMEOUT_IN_MS = 100;
public static final int BUFFER_SIZE_IN_BYTES = 256;
}
public static final int USB_TIMEOUT_IN_MS = 100;
public static final int BUFFER_SIZE_IN_BYTES = 256;
}
接着是InfoActivity ,InfoActivity 是整个整个程序的入口,对应的布局文件就一个textView,用于提示连接两台android设备。
public class InfoActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_info);
}
}
最后就是在里配置一下就好了
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="InfoActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ChatActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter" />
</activity>
<activity android:name=".ConnectActivity"
android:screenOrientation="landscape">
</activity>
<activity android:name=".device.ChatActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
android:resource="@xml/accessory_filter" />
</activity>
<activity android:name=".device.ConnectActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data
android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
</application>
这样就搞定了。由于是第一次写博客,如果有什么写的不对或者不好的地方请各位大神指正!