Android串口开发实践

        因为要写串口功能检测串口是否正常,也是参考了网上很多博客和例程,从中取出自己需要的,舍弃不需要的,再加以优化,就是这么个过程了.

        这是我的串口功能的目录结构:


        实际上把jni和libs文件夹建好,导入网上下载的相关文件,然后把SerialPort.java和SerialPortFinder.java放入android_serialport_api包下(要注意这两个类必须放入这个包下,听说和libs中的动态链接库有关系,反正你就放到这个包里面就行了,不用多想).

        先创建一个SerialPortActivity,源码如下:

public abstract class SerialPortActivity extends Activity {

	protected SerialPort mSerialPort;
	protected OutputStream mOutputStream;
	private InputStream mInputStream;
	private ReadThread mReadThread;

	private class ReadThread extends Thread {

		@Override
		public void run() {
			super.run();
			while (!isInterrupted()) {
				int size;
				try {
					byte[] buffer = new byte[64];
					if (mInputStream == null) {
						return;
					}
					size = mInputStream.read(buffer);
					if (size > 0) {
						onDataReceived(buffer, size);
					}
				} catch (IOException e) {
					e.printStackTrace();
					return;
				}
			}
		}
	}

	private void DisplayError(int resourceId) {
		AlertDialog.Builder b = new AlertDialog.Builder(this);
		b.setTitle("Error");
		b.setMessage(resourceId);
		b.setPositiveButton("OK", new OnClickListener() {
			public void onClick(DialogInterface dialog, int which) {
				//SerialPortActivity.this.finish();
			}
		});
		b.show();
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
	}

	public boolean OpenSerialPort(String path, int baudrate) {
		try {
			mSerialPort = getSerialPort(path, baudrate);
			mOutputStream = mSerialPort.getOutputStream();
			mInputStream = mSerialPort.getInputStream();

			/* Create a receiving thread 创建一个接受数据的线程 */
			mReadThread = new ReadThread();
			mReadThread.start();
		} catch (SecurityException e) {
			DisplayError(R.string.error_security);
			return false;
		} catch (IOException e) {
			DisplayError(R.string.error_unknown);
			return false;
		} catch (InvalidParameterException e) {
			DisplayError(R.string.error_configuration);
			return false;
		}
		return true;
	}
	
	public SerialPort getSerialPort(String path, int baudrate) throws SecurityException, IOException, InvalidParameterException {
		if (mSerialPort == null) {
			/* Check parameters */
			if ((path.length() == 0) || (baudrate == -1)) {
				throw new InvalidParameterException();
			}

			/* Open the serial port */
			// mSerialPort = new SerialPort(new File("/dev/ttySAC1"), 9600, 0);
			mSerialPort = new SerialPort(new File(path), baudrate, 0);
		}
		return mSerialPort;
	}

	protected abstract void onDataReceived(final byte[] buffer, final int size);
	
	@Override
	protected void onDestroy() {
		if (mReadThread != null)
			mReadThread.interrupt();
		if (mSerialPort != null) {
			mSerialPort.close();
			mSerialPort = null;
		}
		super.onDestroy();
	}
}

         在这个Activity里面打开串口,创建ReadThread读取数据,还有提示串口的各种错误信息.

        再创建一个活动ConsoleActivity继承自SerialPortActivity,源码如下:

public class ConsoleActivity extends SerialPortActivity {
	
	private TextView serialNumTxt,baudrateTxt;
	private EditText mReception;
	private EditText Emission;
	private TextView stateTxt;
	
	static String serialDevPath = "/dev/ttyS0";//待测试串口路径
	String str;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.console);
		//初始化控件
		initView();
		boolean flag = ConsoleActivity.super.OpenSerialPort(serialDevPath, 9600);
		//发送数据
		if (!flag) {
			stateTxt.setText(R.string.falseState);
			stateTxt.setTextColor(Color.RED);
			return;
		}
		stateTxt.setText(R.string.trueState);
		stateTxt.setTextColor(Color.GREEN);
		CharSequence t = Emission.getText();
		char[] text = new char[t.length()];
		for (int i = 0; i < t.length(); i++) {
			text[i] = t.charAt(i);
		}
		try {

			mOutputStream.write(new String(text).getBytes());
			//mOutputStream.write(' ');

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

	private void initView() {
		// TODO Auto-generated method stub
		mReception = (EditText) findViewById(R.id.EditTextReception);
		mReception.setEnabled(false);
		mReception.setFocusable(false);
		mReception.setTextColor(Color.WHITE);
		Emission = (EditText) findViewById(R.id.EditTextEmission);
		Emission.setEnabled(false);
		Emission.setFocusable(false);
		Emission.setText("Hello,World!");
		stateTxt=(TextView)findViewById(R.id.stateTxt);
		
		serialNumTxt = (TextView)findViewById(R.id.serialportNum);
		serialNumTxt.setText(this.getResources().getString(R.string.serialName)+serialDevPath);
		baudrateTxt = (TextView)findViewById(R.id.baundrate);
		baudrateTxt.setText(this.getResources().getString(R.string.baundrate)+"9600");
	}

	@Override
	protected void onDataReceived(final byte[] buffer, final int size) {
		runOnUiThread(new Runnable() {
			public void run() {
				// if (mReception != null) {
				mReception.append(new String(buffer, 0, size));
				//mReception.append(" ");
				// }
			}
		});
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			// 创建退出对话框
			AlertDialog.Builder isExit = new Builder(this);
			// 设置对话框标题
			isExit.setTitle("BRIGHTNESS Test");
			// 设置对话框消息
			isExit.setMessage("测试通过");
			// 添加选择按钮并注册监听
			isExit.setPositiveButton("同意", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					dialog.dismiss();
					str = "yes";
					Uri data = Uri.parse(str);
					Intent result = new Intent(null, data);
					setResult(RESULT_OK, result);
					finish();
				}
			});
			isExit.setNegativeButton("否定", new android.content.DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
					dialog.dismiss();
					str = "no";
					Uri data = Uri.parse(str);
					Intent result = new Intent(null, data);
					setResult(RESULT_OK, result);
					finish();
				}
			});
			// 对话框显示
			isExit.create().show();
		}
		return false;
	}
}

        这里我是固定设置的串口号,你可以通过SerialPortFinder类的getAllDevices方法找到所有的串口号,然后加到一个Spinner中,再设置OnItemSelectedLisener就可以实现选择串口号的功能.

        布局文件源码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal" >
    <TextView 
        android:id="@+id/serialportNum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="100dp"
        android:gravity="center_horizontal"
        android:text="@string/serialName"
        android:textSize="@dimen/serialSize" /> 
    
    <TextView 
        android:id="@+id/baundrate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/serialportNum"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@id/serialportNum"
        android:text="@string/baundrate"
        android:textSize="@dimen/serialSize" /> 
        
   	<TextView 
        android:id="@+id/sendArea"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/baundrate"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@id/serialportNum"
        android:text="@string/send"
        android:textSize="@dimen/serialSize" /> 
        
    <EditText
        android:id="@+id/EditTextEmission"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/sendArea"
        android:layout_alignBottom="@id/sendArea"
        android:layout_weight="1" />
        
    <TextView 
        android:id="@+id/recArea"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/sendArea"
        android:layout_marginTop="10dp"
        android:layout_alignLeft="@id/sendArea"
        android:text="@string/rec"
        android:textSize="@dimen/serialSize" /> 
        
    <EditText
        android:id="@+id/EditTextReception"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@id/recArea"
        android:layout_alignBottom="@id/recArea"
        android:layout_weight="1" />
    
    <TextView android:id="@+id/serialState" 
        android:layout_below="@id/recArea"
        android:layout_alignLeft="@id/serialportNum"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:text="@string/serialState"
        android:textSize="@dimen/serialSize"
        android:textAlignment="viewStart" />
    
    <TextView android:id="@+id/stateTxt"
        android:layout_toRightOf="@id/serialState"
        android:layout_alignBottom="@id/serialState"
        android:layout_alignLeft="@id/EditTextReception"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:textSize="@dimen/serialSize" />        
        
</RelativeLayout>

        最后实现的界面如下:


        网上例程很多,我之前是已经把网上的一个例程给调通了,但是想把这个串口功能作为一个子活动加入我的项目中,却是各种崩溃,原因也找不到,现在能OK也是经历一番摸索,不容易.网上的例程有的是通过Application来把找到的串口号列表加入全局变量,有的是sharedpreferrce保存设置过的参数,这些我都没要,做到了简化的极限了.

        在网上看到说在6.0以上版本运行会报错,但是我没有发现这情况,只是使用外部库总是会弹窗提示text location,这个问题还不知道怎么解决.

        想把这个功能加入某个项目中作为子活动的话只要在主活动中通过Intent跳到ConsoleActivity中就可以了,很方便.

        调试用的设备不知道什么原因没有LOG打印,只能在代码中通过注释某一部分来确定问题所在.


2018年3月28日 16:38:29

        现在此功能已经完全OK了,解决弹窗请看解决串口弹窗

2018年4月24日 20:23:17

        相比大的项目,各种独立的小Demo才应该是我们要保留的对象,我对此感受颇深.

        又有Android串口相关的项目,我从大项目中将串口测试模块单独拿出来,却莫名其妙崩溃,一整天弄的我心态都要炸了,终于在此时此刻,运行OK,没有崩溃的情况了,在此把Demo分享出来,免得下次又有意外.

        Android串口通信Demo在此!

猜你喜欢

转载自blog.csdn.net/qq_37069563/article/details/79585341