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在此!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章