1、 提到串口编程,就不得不提到JNI,不得不提到JavaAPI中的文件描述符类:FileDescriptor。下面我分别对JNI、FileDescriptor以及串口的一些知识点和实现的源码进行分析说明。这里主要是参考了开源项目android-serialport-api。 串口编程需要了解的基本知识点:对于串口编程,我们只需对串口进行一系列的设置,然后打开串口,这些操作我们可以参考串口调试助手的源码进行学习。在Java中如果要实现串口的读写功能只需操作文件设备类:FileDescriptor即可,其他的事都由驱动来完成不用多管!当然,你想了解,那就得看驱动代码了。这里并不打算
2、对驱动进行说明,只初略阐述应用层的实现方式。 (一)JNI: 关于JNI的文章网上有很多,不再多做解释,想详细了解的朋友可以查看云中漫步的技术文章,写得很好,分析也很全面,那么在这篇拙文中我强调3点: 1、如何将编译好的SO文件打包到APK中?(方法很简单,直接在工程目录下新建文件夹 libs/armeabi,将SO文件Copy到此目录即可) 2、命名要注意的地方?(在编译好的SO文件中,将文件重命名为:libfilename.so即可。其中filename.so是编译好后生成的文件) 3、MakeFile文件的编写(不用多说,可以直接参考pac
3、kage/apps目录下用到JNI的相关项目写法) 这是关键的代码: [cpp] view plaincopy 1. int fd; 2. speed_t speed; 3. jobject mFileDescriptor; 4. 5. /* Check arguments */ 6. { 7. speed = getBaudrate(baudrate); 8.
4、 if (speed == -1) { 9. /* TODO: throw an exception */ 10. LOGE("Invalid baudrate"); 11. return NULL; 12. } 13. } 14. 15. /* Opening device */ 16. {
5、 17. jboolean iscopy; 18. const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); 19. LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags); 20. fd = open(path_utf, O_RDWR | flags); 21.
6、 LOGD("open() fd = %d", fd); 22. (*env)->ReleaseStringUTFChars(env, path, path_utf); 23. if (fd == -1) 24. { 25. /* Throw an exception */ 26. LOGE("Cannot open port"); 27.
7、 /* TODO: throw an exception */ 28. return NULL; 29. } 30. } 31. 32. /* Configure device */ 33. { 34. struct termios cfg; 35. LOGD("Configuring serial port");
8、 36. if (tcgetattr(fd, &cfg)) 37. { 38. LOGE("tcgetattr() failed"); 39. close(fd); 40. /* TODO: throw an exception */ 41. return NULL; 42.
9、 } 43. 44. cfmakeraw(&cfg); 45. cfsetispeed(&cfg, speed); 46. cfsetospeed(&cfg, speed); 47. 48. if (tcsetattr(fd, TCSANOW, &cfg)) 49. { 50. LOGE("tcsetattr() failed");
10、 51. close(fd); 52. /* TODO: throw an exception */ 53. return NULL; 54. } 55. } 56. (二)FileDescritor: 文件描述符类的实例用作与基础机器有关的某种结构的不透明句柄,该结构表示开放文件、开放套接字或者字节的另一个源或接收者。文件描述符的主
11、要实际用途是创建一个包含该结构的FileInputStream 或FileOutputStream。这是API的描述,不太好理解,其实可简单的理解为:FileDescritor就是对一个文件进行读写。
(三)实现串口通信细节
1) 建工程:SerialDemo包名:org.winplus.serial,并在工程目录下新建jni和libs两个文件夹和一个org.winplus.serial.utils,如下图:
2) 新建一个类:SerialPortFinder,添加如下代码:
[java] view plaincopy
1. 12、8px;">package org.winplus.serial.utils;
2.
3. import java.io.File;
4. import java.io.FileReader;
5. import java.io.IOException;
6. import java.io.LineNumberReader;
7. import java.util.Iterator;
8. import java.util.Vector;
9.
10. import android.util.Log;
11.
12. pu 13、blic class SerialPortFinder {
13.
14. private static final String TAG = "SerialPort";
15.
16. private Vector 14、 mDeviceRoot = root;
22. }
23.
24. private String mDriverName;
25. private String mDeviceRoot;
26. Vector 15、 mDevices = new Vector 16、artsWith(mDeviceRoot)) {
36. Log.d(TAG, "Found new device: " + files[i]);
37. mDevices.add(files[i]);
38. }
39. }
40. }
41. return mDevices;
42. }
43.
44. 17、 public String getName() {
45. return mDriverName;
46. }
47. }
48.
49. Vector 18、 new LineNumberReader(new FileReader(
53. "/proc/tty/drivers"));
54. String l;
55. while ((l = r.readLine()) != null) {
56. // Issue 3:
57. // Since driver name may contain spaces, we do not extract
58. 19、 // driver name with split()
59. String drivername = l.substring(0, 0x15).trim();
60. String[] w = l.split(" +");
61. if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) {
62. Log.d(TAG, "Found new dri 20、ver " + drivername + " on "
63. + w[w.length - 4]);
64. mDrivers.add(new Driver(drivername, w[w.length - 4]));
65. }
66. }
67. r.close();
68. }
69. return mDrivers;
70. 21、 }
71.
72. public String[] getAllDevices() {
73. Vector 22、hasNext()) {
79. Driver driver = itdriv.next();
80. Iterator 23、e = String.format("%s (%s)", device,
84. driver.getName());
85. devices.add(value);
86. }
87. }
88. } catch (IOException e) {
89. e.printStackTrace();
90. }
91. ret 24、urn devices.toArray(new String[devices.size()]);
92. }
93.
94. public String[] getAllDevicesPath() {
95. Vector 25、riv = getDrivers().iterator();
100. while (itdriv.hasNext()) {
101. Driver driver = itdriv.next();
102. Iterator 26、ice = itdev.next().getAbsolutePath();
105. devices.add(device);
106. }
107. }
108. } catch (IOException e) {
109. e.printStackTrace();
110. }
111. return devices.toArray(new String[devices.size() 27、]);
112. }
113. }
114.
28、eDescriptor; 5. import java.io.FileInputStream; 6. import java.io.FileOutputStream; 7. import java.io.IOException; 8. import java.io.InputStream; 9. import java.io.OutputStream; 10. 11. import android.util.Log; 12. 13. public class SerialPort { 14. private static
29、final String TAG = "SerialPort"; 15. 16. /* 17. * Do not remove or rename the field mFd: it is used by native method 18. * close(); 19. */ 20. private FileDescriptor mFd; 21. private FileInputStream mFileInputStream; 22. private FileOutputStream m
30、FileOutputStream; 23. 24. public SerialPort(File device, int baudrate, int flags) 25. throws SecurityException, IOException { 26. 27. /* Check access permission */ 28. if (!device.canRead() || !device.canWrite()) { 29. try { 30.
31、 /* Missing read/write permission, trying to chmod the file */ 31. Process su; 32. su = Runtime.getRuntime().exec("/system/bin/su"); 33. String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" 34. +
32、 "exit\n"; 35. su.getOutputStream().write(cmd.getBytes()); 36. if ((su.waitFor() != 0) || !device.canRead() 37. || !device.canWrite()) { 38. throw new SecurityException(); 39. } 40.
33、 } catch (Exception e) { 41. e.printStackTrace(); 42. throw new SecurityException(); 43. } 44. } 45. 46. mFd = open(device.getAbsolutePath(), baudrate, flags); 47. if (mFd == null) { 48. Log.e
34、TAG, "native open returns null"); 49. throw new IOException(); 50. } 51. mFileInputStream = new FileInputStream(mFd); 52. mFileOutputStream = new FileOutputStream(mFd); 53. } 54. 55. // Getters and setters 56. public InputStre
35、am getInputStream() { 57. return mFileInputStream; 58. } 59. 60. public OutputStream getOutputStream() { 61. return mFileOutputStream; 62. } 63. 64. // JNI 65. private native static FileDescriptor open(String path, int baudrate, 66.
36、 int flags);
67.
68. public native void close();
69.
70. static {
71. System.loadLibrary("serial_port");
72. }
73. }
74.
4) 新建一个Application 继承android.app.Application,用来对串口进行初始化和关闭串口
[java] view plaincopy
1. 37、size:18px;">package org.winplus.serial;
2.
3. import java.io.File;
4. import java.io.IOException;
5. import java.security.InvalidParameterException;
6.
7. import org.winplus.serial.utils.SerialPort;
8. import org.winplus.serial.utils.SerialPortFinder;
9.
10. import andr 38、oid.content.SharedPreferences;
11.
12. public class Application extends android.app.Application {
13. public SerialPortFinder mSerialPortFinder = new SerialPortFinder();
14. private SerialPort mSerialPort = null;
15.
16. public SerialPort getSerialPort() throws Securi 39、tyException, IOException, InvalidParameterException {
17. if (mSerialPort == null) {
18. /* Read serial port parameters */
19. SharedPreferences sp = getSharedPreferences("android_serialport_api.sample_preferences", MODE_PRIVATE);
20. 40、 String path = sp.getString("DEVICE", "");
21. int baudrate = Integer.decode(sp.getString("BAUDRATE", "-1"));
22.
23. /* Check parameters */
24. if ( (path.length() == 0) || (baudrate == -1)) {
25. 41、 throw new InvalidParameterException();
26. }
27.
28. /* Open the serial port */
29. mSerialPort = new SerialPort(new File(path), baudrate, 0);
30. }
31. return mSerialPort;
3 42、2. }
33.
34. public void closeSerialPort() {
35. if (mSerialPort != null) {
36. mSerialPort.close();
37. mSerialPort = null;
38. }
39. }
40. }
41.
43、 [java] view plaincopy 1. package org.winplus.serial; 2. 3. import java.io.IOException; 4. import java.io.InputStream; 5. import java.io.OutputStream; 6. import java.security.InvalidParameterException; 7. 8. import org.winplus.serial.utils.Serial
44、Port; 9. 10. import android.app.Activity; 11. import android.app.AlertDialog; 12. import android.content.DialogInterface; 13. import android.content.DialogInterface.OnClickListener; 14. import android.os.Bundle; 15. 16. public abstract class SerialPortActivity extends Activ
45、ity { 17. protected Application mApplication; 18. protected SerialPort mSerialPort; 19. protected OutputStream mOutputStream; 20. private InputStream mInputStream; 21. private ReadThread mReadThread; 22. 23. private class ReadThread extends Thread { 2
46、4. 25. @Override 26. public void run() { 27. super.run(); 28. while (!isInterrupted()) { 29. int size; 30. try { 31. byte[] buffer = new byte[64]; 32. if (mInputSt
47、ream == null) 33. return; 34. 35. /** 36. * 这里的read要尤其注意,它会一直等待数据,等到天荒地老,海枯石烂。如果要判断是否接受完成,只有设置结束标识,或作其他特殊的处理。 37. */ 38. size = mInputStream.read(buffer);
48、 39. if (size > 0) { 40. onDataReceived(buffer, size); 41. } 42. } catch (IOException e) { 43. e.printStackTrace(); 44. return; 45. } 46.
49、 } 47. } 48. } 49. 50. private void DisplayError(int resourceId) { 51. AlertDialog.Builder b = new AlertDialog.Builder(this); 52. b.setTitle("Error"); 53. b.setMessage(resourceId); 54. b.setPositiveButton("OK", new OnClic
50、kListener() { 55. public void onClick(DialogInterface dialog, int which) { 56. SerialPortActivity.this.finish(); 57. } 58. }); 59. b.show(); 60. } 61. 62. @Override 63. protected void onCreate(Bundle save






