1. 程式人生 > >android 使用服務讀取串列埠資料例項

android 使用服務讀取串列埠資料例項

一些android 手機是有串列埠可以提供開啟,讀取一些底層的資料。如果手機本身沒有串列埠的話,那麼開啟估計會報錯。串列埠讀取資料肯定是使用jni呼叫c程式碼來完成的。有一個開源的串列埠庫android-serialport-api。其主頁在這裡http://code.google.com/p/android-serialport-api/  ,這裡可以下到APK及對源碼。谷歌的程式碼庫,無奈國內無法下載https://github.com/cepr/android-serialport-api ,GITHUB的地址,這個可以下載 但是下載原始碼之後發現原始碼不能直接使用,而且原始碼結構較為複雜。

在這裡我是使用服務來讀取串列埠資料,都出來資料直接寫入到txt檔案裡面。

1,注意事項

選擇串列埠時候,需要加上dev/。如果我選擇ttyMT2,路徑就是dev/ttyMT2。波特率就是整型38400,或者其他。


開啟串列埠以及讀取資料的串列埠類呼叫載入so庫一定放在這個包下面android_serialport_api。不能放在其他包的下面。

SerialPortDevice.class

/**
 * 
 */
package android_serialport_api;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;

public class SerialPortDevice{

	private FileDescriptor mFd;
	private InputStream mInputStream;
	private OutputStream mOutputStream;
	
	private String path;
	private int baudrate;
	int flags=0;

	public SerialPortDevice(String path, int baudrate, int flags) {
		mFd = new FileDescriptor();
		this.path=path;
		this.baudrate=baudrate;
		this.flags=flags;
	}

	/* (non-Javadoc)
	 * @see com.gps.device.IDevice#connect()
	 */
	public boolean connect()  {
		mFd = open(path, baudrate, 0);
		if (mFd == null) {
			return false;
		}
		else
		{
			return true;
		}
	}	

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.gps.device.IDevice#getInputStream()
	 */
	public InputStream getInputStream() {
		// TODO Auto-generated method stub
		return new FileInputStream(mFd);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.gps.device.IDevice#getOutputStream()
	 */
	public OutputStream getOutputStream() {
		// TODO Auto-generated method stub
		return new FileOutputStream(mFd);
	}

	// JNI
	private native static FileDescriptor open(String path, int baudrate, int flags);
	public native void close();
	static {
		System.loadLibrary("serial_port");
	}	
}

2,服務類,以及封裝的串列埠管理類

SerialPortService.class 服務

package com.mmsx.serial;
import com.mmsx.serial.SerialPortIOManage.onSerialPortIOListener;

import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;

public  class SerialPortService extends Service {
 
    @Override
    public void onCreate() { 
    	SerialPortIOManage.getInstance().setonSerialPortIOListener(new onSerialPortIOListener() {
			
			@Override
			public void OnIOCallBack(byte[] data, int length) {
				
			}
			
			@Override
			public void OnConnectStatusCallBack(boolean statue) {
				if (statue) {
		    		String fileString = Environment.getExternalStorageDirectory().getPath() + 
						"/serial.txt";
		    		FileWrite.GetInstance().Create(fileString);
				}
			}
		});
    }

    @Override
    public void onDestroy() {
    	SerialPortIOManage.getInstance().disConnect();
        super.onDestroy();
    }

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		SerialPortIOManage.getInstance().Connect("dev/ttyMT2", 38400);
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}
}

service服務也是android 的四大元件,別忘了註冊

       <service
            android:name="com.mmsx.serial.SerialPortService"
            android:icon="@drawable/ic_launcher">
        </service>


SerialPortIOManage.class串列埠開啟獲取資料的管理類
package com.mmsx.serial;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android_serialport_api.SerialPortDevice;
public class SerialPortIOManage{
	SerialPortDevice temPortDevice = null;
	private InputStream mInputStream = null;
	private OutputStream mOutputStream = null;

	private String mstrComPath = "";
	private int mnBaudrate = 57600;

	ReadThread mReadThread = null;
	
	onSerialPortIOListener mListener = null;
	
    private static class SingletonHolder {  
    private static final SerialPortIOManage INSTANCE = new SerialPortIOManage();  
    }  
    private SerialPortIOManage (){}  
    public static final SerialPortIOManage getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
	
    public void setonSerialPortIOListener(onSerialPortIOListener listener){
    	mListener = listener;
    }
    
    public interface onSerialPortIOListener {
    	abstract void OnConnectStatusCallBack(boolean statue);
      	abstract void OnIOCallBack(byte[] data, int length);
    }
	
	
	public void Connect(String path, int baudrate) {
		mstrComPath = path;
		mnBaudrate = baudrate;
		
		temPortDevice = new SerialPortDevice(path, baudrate, 0);
		if (temPortDevice.connect()) {
			mInputStream = temPortDevice.getInputStream();
			mOutputStream = temPortDevice.getOutputStream();
			mReadThread = new ReadThread();
			mReadThread.start(); 
			
			if (null != mListener) 
				mListener.OnConnectStatusCallBack(true);
		}
		else
		{
			if (null != mListener) 
				mListener.OnConnectStatusCallBack(false);
		}
	}
	
	public void disConnect() {
		if(mReadThread != null){
			mReadThread.interrupt();
			mReadThread = null;
		}

		try {
			if (mInputStream != null) {
				mInputStream.close();
				mInputStream = null;
			}
			if (mOutputStream != null) {
				mOutputStream.close();
				mOutputStream = null;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		if (temPortDevice != null) {
			temPortDevice.close();
		}
		
	}
	
	class ReadThread extends Thread{
		public void run(){
			int nMaxBufLength = 1024;
			byte[] buffer = new byte[nMaxBufLength];
			while(!isInterrupted()){
				try{
					int byteRead = -1;
					Thread.sleep(200);
					if(mInputStream != null){
						byteRead = mInputStream.read(buffer);
					    if(byteRead > 0){
					    	if(mListener != null){
					    		mListener.OnIOCallBack(buffer, byteRead);
					    		FileWrite.GetInstance().Write(buffer, byteRead);
					    	}
					    }
				    }
					else {
						break;
					}
				}catch (IOException e)
				{
					if (mListener != null) {
						mListener.OnConnectStatusCallBack(false);
					}
					break;
				} catch (InterruptedException e) {
					e.printStackTrace();
					continue;
				}
			}//while(!isInterrupted())
		}
	}

}

3,實現效果



4,建立檔案,持續寫入資料管理類

package com.mmsx.serial;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileWrite {
	private long  nDataLength = 0;
    private static class SerialPortFileWriteHolder {  
    private static final FileWrite INSTANCE = new FileWrite();  
    }  
    private FileWrite (){}  
    public static final FileWrite GetInstance() {  
    return SerialPortFileWriteHolder.INSTANCE;  
    }  
	
	FileOutputStream moutStream = null;
	private String mstrFileName ="";
	
	public void Create(String strFileName) {
		Close();
		
		mstrFileName = strFileName;
		File newFile = new File(strFileName);
		try {
			nDataLength = 0;
			moutStream = new FileOutputStream(newFile);
		} catch (FileNotFoundException e) {
		}
	}
	
	public void Close() {
		if (moutStream != null) {
			try {
				nDataLength = 0;
				moutStream.close();
			} catch (IOException e) {
			}
			moutStream = null;
		}
	}
	
	public boolean IsOpen() {
		return (moutStream != null);
	}
	
	public void Write(byte[] bytes, int nLength) {
		if (!IsOpen()) {
			if (!mstrFileName.isEmpty()) {
				File newFile = new File(mstrFileName);
				if (newFile.exists()) {
					try {
						nDataLength = 0;
						//檔案存在,往檔案後面增加內容
						moutStream = new FileOutputStream(newFile,true);
					} catch (FileNotFoundException e) {
					}
				}else {
					Create(mstrFileName);
				}
			}else {
				return;
			}
		}else {
			try {
				moutStream.write(bytes, 0, nLength);
				nDataLength = nDataLength + nLength;
				//檔案大於10M就停止寫入
				if (nDataLength > 10485760) {
					Close();
				}
			} catch (IOException e) {
				Close();
			}
		}
	}
	
}

往儲存建立檔案,寫入資料,別忘了許可權
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_SETTINGS"/>

5,最後總結

  當前進入一個activity頁面,開啟串列埠拿到資料持續寫入資料到檔案裡面,別忘了生命週期的影響。如果點選了home,back,或者返回finish等。如果讀取資料執行緒關閉了,怎麼重新開啟等問題。手機應用的大多數情況下我們只能在手機上看到一個程式的一個介面,使用者除了通過程式介面上的功能按鈕來在不同的窗體間切換,還可以通過Back鍵和 Home鍵來返回上一個視窗,而使用者使用Back或者Home的時機是非常不確定的,任何時候使用者都可以使用Home或Back來強行切換當前的介面。

HOME鍵的執行順序:onPause->onStop->onRestart->onStart->onResume

BACK鍵的順序: onPause->onStop->onDestroy->onCreate->onStart->onResume

onPause不要做太耗時的工作

解決辦法:

可以在onResume裡面重新呼叫檢視一下執行緒的狀態。

在這篇部落格我就沒有寫這個方法onResume。之前在專案中遇到過,記錄一下。就是開啟串列埠採集時候,然後點選home,有概率會停止存資料。