本篇是继上篇的jni层后的app应用层。
由于app相对比较大,说起来也比较麻烦,这里就用一个串口来说明即可,实际上也就串口最具代表性,这个明白了,其他都不是问题。
串口涉及的java文件如下图红色圈所示:
具体的代码下面一一贴出来,捡重要的说明:
MainActivity.java:
package com.example.realarmapp; import realarm.can.RealarmCanActivity; import realarm.serial.RealarmUartActivity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity implements OnClickListener { private Button btnLED, btnUART, btnRS485, btnCAN; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnLED = (Button) findViewById(R.id.btnLED); btnUART = (Button) findViewById(R.id.btnUART); btnRS485 = (Button) findViewById(R.id.btnRS485); btnCAN = (Button) findViewById(R.id.btnCAN); btnLED.setOnClickListener(this); btnUART.setOnClickListener(this); btnRS485.setOnClickListener(this); btnCAN.setOnClickListener(this); } @Override public void onClick(View v) { if(v.equals(btnLED)){ Intent i = new Intent(MainActivity.this, RealarmLedActivity.class); startActivity(i); } else if(v.equals(btnUART)) { Intent i = new Intent(MainActivity.this, RealarmUartActivity.class); startActivity(i); } else if(v.equals(btnRS485)) { Intent i = new Intent(MainActivity.this, RealarmUartActivity.class); startActivity(i); } else if(v.equals(btnCAN)) { Intent i = new Intent(MainActivity.this, RealarmCanActivity.class); startActivity(i); } } }这个文件是主activity文件,没什么好说的,就是四个按钮执行不同的功能。
HardwareControl.java:
package realarm.hardware;
package realarm.hardware; import java.io.FileDescriptor; public class HardwareControl { public native int LedSetState(int ledNum,int ledState); public native static FileDescriptor OpenSerialPort(String path, int baudrate, int flags); public native static void CloseSerialPort(); public native static void InitCan(int baudrate); public native static int OpenCan(); public native static int CanWrite(int canId,String data); public native static CanFrame CanRead(CanFrame mcanFrame, int time); public native static void CloseCan(); static { System.loadLibrary("RealarmHardwareJni"); } }
该文件是声明native接口,并加载libRealarmHardwareJNI.so库。
MyApplication.java:
package realarm.serial; import java.io.File; import java.io.IOException; import java.security.InvalidParameterException; import realarm.serial.utils.SerialPort; import realarm.serial.utils.SerialPortFinder; import android.app.Application; import android.content.SharedPreferences; import realarm.hardware.HardwareControl; public class MyApplication extends Application { public SerialPortFinder mSerialPortFinder = new SerialPortFinder(); private SerialPort mSerialPort = null; public SerialPort getSerialPort() throws SecurityException, IOException, InvalidParameterException { if (mSerialPort == null) { /* Read serial port parameters */ SharedPreferences sp = getSharedPreferences("android_serialport_api.sample_preferences", MODE_PRIVATE); String path = sp.getString("DEVICE", "/dev/ttyAMA4"); int baudrate = Integer.decode(sp.getString("BAUDRATE", "115200")); /* Check parameters */ if ( (path.length() == 0) || (baudrate == -1)) { throw new InvalidParameterException(); } /* Open the serial port */ mSerialPort = new SerialPort(new File(path), baudrate, 0); } return mSerialPort; } public SerialPort getSerialPort(String path, String baudrate, int flag) throws NumberFormatException, SecurityException, IOException{ if (mSerialPort == null) { /* Open the serial port */ mSerialPort = new SerialPort(new File(path), Integer.decode(baudrate), flag); } return mSerialPort; } public void closeSerialPort() { if (mSerialPort != null) { HardwareControl.CloseSerialPort(); mSerialPort = null; } } }该文件实现串口的创建和关闭方法。
package realarm.serial.utils; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import android.util.Log; import realarm.hardware.HardwareControl; public class SerialPort { private static final String TAG = "SerialPort"; /* * Do not remove or rename the field mFd: it is used by native method * close(); */ private FileDescriptor mFd; private FileInputStream mFileInputStream; private FileOutputStream mFileOutputStream; public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { /* Check access permission */ if (!device.canRead() || !device.canWrite()) { try { /* Missing read/write permission, trying to chmod the file */ Process su; su = Runtime.getRuntime().exec("/system/xbin/su"); String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + "exit\n"; su.getOutputStream().write(cmd.getBytes()); if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { throw new SecurityException(); } } catch (Exception e) { e.printStackTrace(); throw new SecurityException(); } } mFd = HardwareControl.OpenSerialPort(device.getAbsolutePath(), baudrate, flags); if (mFd == null) { Log.e(TAG, "native open returns null"); throw new IOException(); } mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); } // Getters and setters public InputStream getInputStream() { return mFileInputStream; } public OutputStream getOutputStream() { return mFileOutputStream; } }该文件实现串口的打开、数据输入、数据输出的方法。
SerialPortFinder.java:
package realarm.serial.utils; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.LineNumberReader; import java.util.Iterator; import java.util.Vector; import android.util.Log; public class SerialPortFinder { private static final String TAG = "SerialPort"; private Vector<Driver> mDrivers = null; public class Driver { public Driver(String name, String root) { mDriverName = name; mDeviceRoot = root; } private String mDriverName; private String mDeviceRoot; Vector<File> mDevices = null; public Vector<File> getDevices() { if (mDevices == null) { mDevices = new Vector<File>(); File dev = new File("/dev"); File[] files = dev.listFiles(); int i; for (i = 0; i < files.length; i++) { if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) { Log.d(TAG, "Found new device: " + files[i]); mDevices.add(files[i]); } } } return mDevices; } public String getName() { return mDriverName; } } Vector<Driver> getDrivers() throws IOException { if (mDrivers == null) { mDrivers = new Vector<Driver>(); LineNumberReader r = new LineNumberReader(new FileReader( "/proc/tty/drivers")); String l; while ((l = r.readLine()) != null) { // Issue 3: // Since driver name may contain spaces, we do not extract // driver name with split() String drivername = l.substring(0, 0x15).trim(); String[] w = l.split(" +"); if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) { Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length - 4]); mDrivers.add(new Driver(drivername, w[w.length - 4])); } } r.close(); } return mDrivers; } public String[] getAllDevices() { Vector<String> devices = new Vector<String>(); // Parse each driver Iterator<Driver> itdriv; try { itdriv = getDrivers().iterator(); while (itdriv.hasNext()) { Driver driver = itdriv.next(); Iterator<File> itdev = driver.getDevices().iterator(); while (itdev.hasNext()) { String device = itdev.next().getName(); String value = String.format("%s (%s)", device, driver.getName()); devices.add(value); } } } catch (IOException e) { e.printStackTrace(); } return devices.toArray(new String[devices.size()]); } public String[] getAllDevicesPath() { Vector<String> devices = new Vector<String>(); // Parse each driver Iterator<Driver> itdriv; try { itdriv = getDrivers().iterator(); while (itdriv.hasNext()) { Driver driver = itdriv.next(); Iterator<File> itdev = driver.getDevices().iterator(); while (itdev.hasNext()) { String device = itdev.next().getAbsolutePath(); devices.add(device); } } } catch (IOException e) { e.printStackTrace(); } return devices.toArray(new String[devices.size()]); } }该文件实现应用程序在/dev目录下检索tty*节点的方法。
RealarmUartActivity.java:
package realarm.serial; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import com.example.realarmapp.R; import realarm.serial.utils.SerialPort; import realarm.serial.utils.SerialPortFinder; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.Spinner; import android.widget.ToggleButton; public class RealarmUartActivity extends Activity { protected MyApplication mApplication; protected SerialPort mSerialPort; protected OutputStream mOutputStream; private InputStream mInputStream; private ReadThread mReadThread; EditText EditTextEmission, EditTextReception; SerialPortFinder mSerialPortFinder;// 串口设备搜索 Spinner spinnerCOM; Spinner spinnerBaudRateCOM; CheckBox checkBoxAutoCOM; ToggleButton toggleButtonCOM; Button buttonSendCOM, buttonClean; 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; /** * 这里的read要尤其注意,它会一直等待数据,等到天荒地老,海枯石烂。如果要判断是否接受完成,只有设置结束标识, * 或作其他特殊的处理。 */ size = mInputStream.read(buffer); if (size > 0) { onDataReceived(buffer, size); } } catch (IOException e) { e.printStackTrace(); return; } } } } protected void onDataReceived(final byte[] buffer, final int size) { runOnUiThread(new Runnable() { public void run() { if (EditTextReception != null) { EditTextReception.append(new String(buffer, 0, size)); } } }); } 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) { RealarmUartActivity.this.finish(); } }); b.show(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.uart); <span style="color:#ff0000;">mApplication = (MyApplication) getApplication();</span> mSerialPortFinder = new SerialPortFinder(); EditTextReception = (EditText) findViewById(R.id.EditTextReception); EditTextEmission = (EditText) findViewById(R.id.EditTextEmission); buttonClean = (Button) findViewById(R.id.buttonClean); buttonSendCOM = (Button) findViewById(R.id.buttonSendCOM); spinnerCOM = (Spinner) findViewById(R.id.spinnerCOM); spinnerBaudRateCOM = (Spinner) findViewById(R.id.spinnerBaudRateCOM); checkBoxAutoCOM = (CheckBox) findViewById(R.id.checkBoxAutoCOM); toggleButtonCOM = (ToggleButton) findViewById(R.id.toggleButtonCOM); ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( this, R.array.baudrates_value, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerBaudRateCOM.setAdapter(adapter); spinnerBaudRateCOM.setSelection(16); String[] entryValues = mSerialPortFinder.getAllDevicesPath(); List<String> allDevices = new ArrayList<String>(); for (int i = 0; i < entryValues.length; i++) { allDevices.add(entryValues[i]); } ArrayAdapter<String> aspnDevices = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, allDevices); aspnDevices .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerCOM.setAdapter(aspnDevices); if (allDevices.size() > 0) { spinnerCOM.setSelection(4); } buttonClean.setOnClickListener(new ButtonClickEvent()); buttonSendCOM.setOnClickListener(new ButtonClickEvent()); toggleButtonCOM.setOnCheckedChangeListener(new ToggleButtonCheckedChangeEvent()); spinnerCOM.setOnItemSelectedListener(new ItemSelectedEvent()); spinnerBaudRateCOM.setOnItemSelectedListener(new ItemSelectedEvent()); if (!toggleButtonCOM.isChecked()) {/*如果没有打开串口,那么发送按钮灰色不允许操作*/ buttonSendCOM.setEnabled(false); } // try { // mSerialPort = mApplication.getSerialPort(); // mOutputStream = mSerialPort.getOutputStream(); // mInputStream = mSerialPort.getInputStream(); // // /* Create a receiving thread */ // mReadThread = new ReadThread(); // mReadThread.start(); // } catch (SecurityException e) { // DisplayError(R.string.error_security); // } catch (IOException e) { // DisplayError(R.string.error_unknown); // } catch (InvalidParameterException e) { // DisplayError(R.string.error_configuration); // } } // protected abstract void onDataReceived(final byte[] buffer, final int size); @Override protected void onDestroy() { if (mReadThread != null) mReadThread.interrupt(); mApplication.closeSerialPort(); mSerialPort = null; super.onDestroy(); } // ----------------------------------------------------串口号或波特率变化时,关闭打开的串口 class ItemSelectedEvent implements Spinner.OnItemSelectedListener { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { if ((arg0 == spinnerCOM) || (arg0 == spinnerBaudRateCOM)) { mApplication.closeSerialPort(); checkBoxAutoCOM.setChecked(false); toggleButtonCOM.setChecked(false); } } public void onNothingSelected(AdapterView<?> arg0) { } } // ----------------------------------------------------清除按钮、发送按钮 class ButtonClickEvent implements View.OnClickListener { public void onClick(View v) { if (v == buttonClean) { EditTextReception.setText(""); }else if (v == buttonSendCOM) { if (toggleButtonCOM.isChecked()) { try { mOutputStream.write(EditTextEmission.getText().toString().getBytes()); mOutputStream.write('\n'); System.out.println("串口数据发送成功"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else{ System.out.println("串口未打开"); } } } } //----------------------------------------------------打开关闭串口 class ToggleButtonCheckedChangeEvent implements ToggleButton.OnCheckedChangeListener{ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (buttonView == toggleButtonCOM){ if (isChecked){ try { // mSerialPort = new SerialPort(new File(spinnerCOM.getSelectedItem().toString()), // Integer.decode(spinnerBaudRateCOM.getSelectedItem().toString()), 0); mSerialPort = mApplication.getSerialPort(spinnerCOM.getSelectedItem().toString(), spinnerBaudRateCOM.getSelectedItem().toString(), 0); mOutputStream = mSerialPort.getOutputStream(); mInputStream = mSerialPort.getInputStream(); /* Create a receiving thread */ mReadThread = new ReadThread(); mReadThread.start(); buttonSendCOM.setEnabled(true);/*使能发送按钮,这时可以发送数据了*/ System.out.println("打开串口"+spinnerCOM.getSelectedItem().toString()+"成功!"); } catch (NumberFormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else { mApplication.closeSerialPort(); checkBoxAutoCOM.setChecked(false); buttonSendCOM.setEnabled(false);/*关闭发送按钮使能*/ System.out.println("关闭串口"+spinnerCOM.getSelectedItem().toString()+"成功!"); } } } } }该文件就是串口操作的主activity了,加载布局文件等。在该文件中,这里重要说明MyApplication这个类的调用。就是上面的红色代码。
也许有朋友在http://blog.csdn.net/tangcheng_ok/article/details/7021470这篇文章时,就遇到了这个问题,虽然编译通过,可是运行就会蹦掉。当然了,我也看了,而且遇到了。
那么这是怎么回事呢,通过报错提示“找不到资源”,可以知道当执行到红色部分时,应用程序无法找到资源。可是我们明明定义了MyApplication这个类,且继承与Application,为什么会找不到呢。代码定义当然没问题,实际上问题出在没有告诉app我定义了这个MyApplication。只需要在AndroidManifest.xml文件中添加下面红色的代码即可解决问题:
这句代码就是告诉app要使用MyApplication这个继承与Application的类。
下面贴出运行的界面,O(∩_∩)O。
主界面:
led操作界面:
串口操作界面:
485与串口区别就是打开的串口号不同:
can操作界面:
再来个竖屏的,在布局时考虑到了适应问题,所以没有采用绝对布局,可以适应各分辨率大小的屏幕,只要分辨率不要太小,完全显示都是没问题的。
源码下载地址:http://download.csdn.net/detail/u010406724/8539209
转载请注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44676937