正式接触Android也有差不多一年多的时间了,在这一年里多多少少也有一些成长和积累。今天就跟大家分享一下这一年多以来的给验与积累。
这一年多以来在公司多多少少也做出三四个小项目,虽然不多但也是一些积累。先说用到的技术点(不分先后):
- 多线程与线程同步
- 串口通讯
- 数据结构的封闭(Java是没有数据结构的,下面会详细介绍这个结构封闭)
- AOP切面编程
- AES加解密
- HTTP网络通讯
- MVVM的封装
- 数据库(SqlLite)的使用
- 自定义注解
- 条件编译的设置
- MsgBox的封装
暂先列这几个吧,至于自下定义控件、和控件的使用、还有常 用的转换函数等等类似这样的我都没有列入技术点。
以上的技术点我做了一个叫做【App】的小项目,我叫做Android开发小框架。说是框架其实也就是一些函数封装,说是函数封装吧,其实也包含了一些框架:
- AOP框架
- 所有的Activity的基类(BaseActivity)。该对象的派生类支持动态申请权限、和支持长按的功能。还有一些常用函数。
- 所有实体对象的基类(BaseEntity)。从该类派生的对象支持LoadJson和ToJson的功能。
- 所有数据结构的基类(StructBase)。该类是一个数据结构的基类,把一个数据结构封装成一个实体对象,支持loadJson、ToJson、LoadBinary等功能。
- 所有数据库操作的基类(DataAccess)。所有从该类派生的对象支持Insert、Updata、Delete、SelectAll方法。不用写语句即可使用。
- 串口通讯的基类(ComDevice)。该对象的派生类支持串口通讯的功能,使用时就和Tcp通讯一样方便。
先贴一下该小框架的下在地址:
https://download.csdn.net/download/bluesky_wypeng/10738057
程序结构图:
下面依次介绍这个小框架。
串口通讯
串口通讯的低层
低层通讯有两个对象,一个与驱动进行交互,一个是转换后的一个封闭。说白了都是低层交互。在低层交互的基础上我又做了一层封装,该封装把一些不用的参数信息隐藏然后放出三个接口:
打开串口:
public void Open(final String comport, final int baudrate, final int databits,
final int stopbits, final char parity)
发送数据(该方法发送数据后由IComPortRecv的派生类进行处理):
/**
* 发送数据
*
* @param data
* @return 返回发送的数据长度
*/
public int SendData(byte[] data)
发送数据支持回调:
/**
* 发送数据
* @param data 发送的数据
* @param recvCallBack 收到数据后的事件处理
*/
public void SendData(byte[] data,IAsynListener recvCallBack)
IComPortRecv接口声明如下:
package ComPort;
/**
* 串口数据回调事件
* Created by 王彦鹏 on 2018-01-31.
*/
public interface IComPortRecv {
/**
* 串口数据回调
* @param data
* @param size
*/
void RecvData(byte[] data, int size);
}
串口通讯代码清单:
SerialPort:
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android_serialport_api;
import android.util.Log;
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;
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 databits, int stopbits, char parity) 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 = open(device.getAbsolutePath(), baudrate, databits,stopbits,parity);
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;
}
// JNI
private native static FileDescriptor open(String path, int baudrate, int databits, int stopbits, char parity);
public native void close();
static {
System.loadLibrary("serial_port");
}
}
SerialPortFinder:
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android_serialport_api;
import android.util.Log;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Iterator;
import java.util.Vector;
public class SerialPortFinder {
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;
}
}
private static final String TAG = "SerialPort";
private Vector<Driver> mDrivers = null;
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()]);
}
}
业务封装:
ComDevice:
package ComPort;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Process;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import Helper.EnjoyTools;
import Helper.Log;
import Listener.IAsynListener;
import android_serialport_api.SerialPort;
import android_serialport_api.SerialPortFinder;
/**
* Created by 王彦鹏 on 2018-01-31.
*/
public class ComDevice extends ContextWrapper {
private SerialPort mSerialPort=null;
private InputStream inputStream = null;
private OutputStream outputStream = null;
private byte[] buffer=new byte[128];
private int bufferSize=0;
private Object syncobj = new Object();
private boolean[] recvFlag=new boolean[]{false};
private String comName="";
private Context ctx;
private ExecutorService executor;
/**
* 收到数据的处理事件
*/
private IAsynListener RecvCallBack;
public List<String> ComDevicesList = new ArrayList<String>();
class callTAsk implements Callable<Integer> {
private byte[] buf;
public callTAsk( byte[] data)
{
buf=data;
}
@Override
public Integer call() throws Exception {
recvFlag[0] = true;
if (RecvCallBack!=null)
{
Log.write(comName, "收到数据执行回调");
RecvCallBack.onFinish(buf,buf.length);
Log.write(comName, "接收回调清除");
//RecvCallBack=null;
}
else
{
Log.write(comName, "收到数据但未发现回调地址");
}
if (ctx instanceof IComPortRecv) {
((IComPortRecv) ctx).RecvData(buf, buf.length);
}
return 0;
}
};
private Runnable mRunnable = new Runnable() {
public void run() {
Object obj=new Object();
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (!executor.isShutdown()) {
try {
synchronized (syncobj) {
int size; //读取数据的大小
if (inputStream != null) {
size = inputStream.available();
if (size > 0) {
size = inputStream.read(buffer);
byte[] buf = new byte[size];
bufferSize=size;
System.arraycopy(buffer, 0, buf, 0, size);
Log.write(comName, String.format(" %s 收到数据:%s", comName, EnjoyTools.ByteArrayToHexString(buf)));
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new callTAsk(buf));//将任务提交给线程池
}
}
else
{
break;
}
}
synchronized (obj)
{
obj.wait(10);
}
} catch (Exception e) {
}
/*if (inputStream != null) {
mHandler.postDelayed(mRunnable, 10); //给自己发送消息,自运行
}*/
}
Log.write(comName, String.format(" %s 关闭", comName));
}
};
public ComDevice(Context base) {
super(base);
ctx=this;
SerialPortFinder serialPortFinder=new SerialPortFinder();
String[] entryValues = serialPortFinder.getAllDevicesPath();
for (int i = 0; i < entryValues.length; i++) {
ComDevicesList.add(entryValues[i]);
};
}
public void destroy()
{
Log.write(comName, String.format(" %s 准备退出",comName));
executor.shutdown();
Close();
Log.write(comName, String.format(" %s 已退出",comName));
}
/**
* 打开串口
*/
public void Open(final String comport, final int baudrate, final int databits,
final int stopbits, final char parity) throws Exception {
comName = comport;
mSerialPort = new SerialPort(new File(comport), baudrate, databits, stopbits, parity);
//获取打开的串口中的输入输出流,以便于串口数据的收发
inputStream = mSerialPort.getInputStream();
outputStream = mSerialPort.getOutputStream();
Log.write(comName, String.format("打开串口:%s", comport));
executor = Executors.newSingleThreadExecutor();
executor.execute(mRunnable);
}
/**
* 关闭串口
*/
public void Close() {
try {
mSerialPort.close();
inputStream.close();
outputStream.close();
inputStream=null;
outputStream=null;
mSerialPort.close();
Log.write(comName, String.format("串口 %s 已关闭", comName));
} catch (IOException e) {
Log.write(comName, String.format("关闭串口 %s 异常:%s", comName, e.toString()));
return;
}
}
/**
* 发送数据
*
* @param data
* @return 返回发送的数据长度
*/
public int SendData(byte[] data) {
synchronized (syncobj) {
recvFlag[0] = false;
if (outputStream != null) {
Log.write(comName, String.format("%s 发送数据:%s", comName, EnjoyTools.ByteArrayToHexString(data)));
try {
outputStream.write(data);
} catch (IOException e) {
Log.write(comName, String.format("%s 发送失败:", comName, e.toString()));
}
return data.length;
} else {
return 0;
}
}
}
/**
* 发送数据
* @param data 发送的数据
* @param recvCallBack 收到数据后的事件处理
*/
public void SendData(byte[] data,IAsynListener recvCallBack) {
synchronized (syncobj) {
Log.write(comName, "开始发送数据");
if (outputStream != null) {
RecvCallBack=recvCallBack;
Log.write(comName, String.format("%s 发送数据:%s", comName, EnjoyTools.ByteArrayToHexString(data)));
try {
outputStream.write(data);
} catch (IOException e) {
Log.write(comName, String.format("%s 发送失败:", comName, e.toString()));
}
} else {
recvCallBack.onError(this,new Exception("设备未连接"));
}
}
Log.write(comName, "数据发送完毕");
}
}
串口接收接口的声明(IComPortRecv):
package ComPort;
/**
* 串口数据回调事件
* Created by 王彦鹏 on 2018-01-31.
*/
public interface IComPortRecv {
/**
* 串口数据回调
* @param data
* @param size
*/
void RecvData(byte[] data, int size);
}
注解:
这些注解都是为了AOP和数据库基类自动生成语句来使用的,看看即可。在此只列出一个AOP注解的定义:
package Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import Filter.IFilter;
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Filter {
Class<?>[] value() default {IFilter.class};// 属性类型为Class;
}
对象太多,以下只贴图了,请多包涵,如需要请下载这个小框架来研究吧,有任何问题可以在线回复。
BaseActivity接口:
数据库基类:
AOPDemo:
数据结构的一个Demo
AOP工厂类
网络通讯类(多线程)
几个MVVM用到的控件
有问题大家可以询问。