前言:

  最近接触蓝牙开发,主要是通过蓝牙连接获取传感器硬件设备的数据,并进行处理。

  网上学习一番,现整理出一套比较标准的 操作流程代码。

  如果大家看得懂,将来只需要改下 硬件设备的MAC码 和 改下对接收数据的处理 即可。  一切都是套路~~~

现在以一个小型项目需求来学习Android蓝牙编程

需求: 通过蓝牙获取硬件数据,并显示在一个随数据即时变化的动态折线图中。

实现思路:

(1) 配对蓝牙设备

(2) 连接蓝牙设备    ,根据MAC地址,代码中修改

(3) 接收数据

(4) 处理数据          ,根据硬件厂商提供给你的数据转换公式,在BluetoothService类中 修改

(5) 传数据给折线图,展现实时变化

-----------------------------------------------------------------------

蓝牙知识了解:

(1)、MAC地址:每个设备都有全球唯一的,根据此MAC地址判断蓝牙设备

(2)、蓝牙传输数据,通常一秒钟会传输很多个包,每个包的数据情况如下:

  此时,这个包有11个字节,0x55 是首码,通常通过他来判断一个包的开始

                                     SUM是验证码,会有一套公式来计算,判断当前包是不是一个有效的完整的包

              中间的即是数据,然后硬件方面会给我们一套计算公式,可以以此获取我们要的数据。

  当然每个硬件的包的数据大小都是不同的,有的可能有21个字节,每个硬件的数据的计算方式也不想同

代码实现:

一共就三部分,因为代码篇幅可能较大,不适合一段段代码讲解,直接贴出整个代码。所有的解释都在注释当中。

其中:

(1)、红色部分是需要大家根据个人硬件情况进行修改的

(2)、紫色部分是根据个人数据情况添加删除修改的。

一:MainActivity

public class MainActivity extends Activity {

    private BluetoothService mBluetoothService; //自定义蓝牙服务类
private BluetoothAdapter mBluetoothAdapter;
private String mConnectedDeviceName = null; //连接设备的名称 //默认是1,因为程序启动时首先会连接一个蓝牙
private int current_pos = ; //hanlder消息标识 message.what
public static final int MESSAGE_STATE_CHANGE = ; // 状态改变
public static final int MESSAGE_READ = ; // 读取数据
public static final int MESSAGE_WRITE = ; // 给硬件传数据,暂不需要,看具体需求
public static final int MESSAGE_DEVICE_NAME = ; // 设备名字
public static final int MESSAGE_TOAST = ; // Toast //传感器 ,这里默认同时需要和三个硬件连接,分别设置id 1,2,3进行区分,demo中实际只用到 MAGIKARE_SENSOR_DOWN = 1
//可以根据情况自行添加删除
public static final int MAGIKARE_SENSOR_UP = ;
public static final int MAGIKARE_SENSOR_DOWN = ;
public static final int MAGIKARE_SENSOR_CENTER = ; public static float[] m_receive_data_up; //传感器的数据
public static float[] m_receive_data_down; //传感器的数据 ,demo中我们只需要这一个,因为只有一个硬件设备,
public static float[] m_receive_data_center; //传感器的数据 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //获取蓝牙适配器
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 1、判断设备是否支持蓝牙功能
if (mBluetoothAdapter == null) {
//设备不支持蓝牙功能
Toast.makeText(this, "当前设备不支持蓝牙功能!", Toast.LENGTH_SHORT).show();
return;
} // 2、打开设备的蓝牙功能
if (!mBluetoothAdapter.isEnabled()) {
boolean enable = mBluetoothAdapter.enable(); //返回值表示 是否成功打开了蓝牙设备
if (enable) {
Toast.makeText(this, "打开蓝牙功能成功!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "打开蓝牙功能失败,请到'系统设置'中手动开启蓝牙功能!", Toast.LENGTH_SHORT).show();
return;
}
} // 3、创建自定义蓝牙服务对象
if (mBluetoothService == null) {
mBluetoothService = new BluetoothService(MainActivity.this, mHandler);
}
if (mBluetoothService != null) {
//根据MAC地址远程获取一个蓝牙设备,这里固定了,实际开发中,需要动态设置参数(MAC地址)
BluetoothDevice sensor_down = mBluetoothAdapter.getRemoteDevice("20:16:06:15:78:76");
if (sensor_down != null) {
//成功获取到远程蓝牙设备(传感器),这里默认只连接MAGIKARE_SENSOR_DOWN = 1这个设备
mBluetoothService.connect(sensor_down, MAGIKARE_SENSOR_DOWN);
}
} }
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_READ:
try {
String str=msg.getData().getString("index");
int index=Integer.valueOf(str);
switch (index)
{
//获取到蓝牙传输过来的数据
case MAGIKARE_SENSOR_UP:
m_receive_data_up=msg.getData().getFloatArray("Data");
break;
//实际只用到这个case ,因为demo只连接了一个硬件设备
case MAGIKARE_SENSOR_DOWN:
m_receive_data_down=msg.getData().getFloatArray("Data");
break;
case MAGIKARE_SENSOR_CENTER:
m_receive_data_center=msg.getData().getFloatArray("Data");
break; }
} catch (Exception e) {
// TODO: handle exception
}
break;
case MESSAGE_STATE_CHANGE:
// 连接状态
switch (msg.arg1) {
case BluetoothService.STATE_CONNECTED:
break;
case BluetoothService.STATE_CONNECTING:
break;
case BluetoothService.STATE_LISTEN:
break;
case BluetoothService.STATE_NONE:
break;
}
break;
case MESSAGE_DEVICE_NAME:
mConnectedDeviceName = msg.getData().getString("device_name");
Log.i("bluetooth","成功连接到:"+mConnectedDeviceName);
Toast.makeText(getApplicationContext(),"成功连接到设备" + mConnectedDeviceName,Toast.LENGTH_SHORT).show(); break;
case MESSAGE_TOAST:
int index=msg.getData().getInt("device_id");
Toast.makeText(getApplicationContext(),msg.getData().getString("toast"), Toast.LENGTH_SHORT).show();
//当失去设备或者不能连接设备时,重新连接
Log.d("Magikare","当失去设备或者不能连接设备时,重新连接");
            
            //重新连接硬件设备
if(mBluetoothService!=null)
{
switch (index) {
case MAGIKARE_SENSOR_DOWN:
                    //根据你的硬件的MAC地址写参数,每一个硬件设备都有一个MAC地址,此方法是根据MAC地址得到蓝牙设备
BluetoothDevice sensor_down = mBluetoothAdapter.getRemoteDevice("20:16:06:15:78:76");
if (sensor_down != null)
mBluetoothService.connect(sensor_down, MAGIKARE_SENSOR_DOWN);
break;
case MAGIKARE_SENSOR_UP:
BluetoothDevice sensor_up = mBluetoothAdapter.getRemoteDevice(""); //参数写你这个设备的MAC码
if (sensor_up != null)
mBluetoothService.connect(sensor_up, MAGIKARE_SENSOR_UP);
break;
case MAGIKARE_SENSOR_CENTER:
BluetoothDevice center = mBluetoothAdapter.getRemoteDevice(""); //参数写你这个设备的MAC码
if (center != null)
mBluetoothService.connect(center, MAGIKARE_SENSOR_CENTER);
break;
}
} break;
}
return false;
}
}); public synchronized void onResume() {
super.onResume(); if (mBluetoothService != null) {
if (mBluetoothService.getState() == BluetoothService.STATE_NONE) {
mBluetoothService.start();
}
}
} @Override
public void onDestroy() {
super.onDestroy();
if (mBluetoothService != null) mBluetoothService.stop();
}
  
  
  // 硬件通过蓝牙传输的byte类型已经转换为float类型,并且通过handler传输到 m_receive_data_down[]数组中,一下操作是获取这个数据,根据个人情况使用
//获取角度
public float[] GetAngle(int index)
{
float[] angles=new float[3];
if(m_receive_data_up==null
||m_receive_data_down==null
)
{
return angles;
}
switch (index)
{
case MAGIKARE_SENSOR_DOWN:
angles[0]=m_receive_data_down[6];
angles[1]=m_receive_data_down[7];
angles[2]=m_receive_data_down[8];
break;
case MAGIKARE_SENSOR_UP:
angles[0]=m_receive_data_up[6];
angles[1]=m_receive_data_up[7];
angles[2]=m_receive_data_up[8];
Log.d("安卓 Up 角度",angles[0]+","+angles[1]+","+angles[2]);
break;
}
return angles;
}
//获取角速度
public static float[] GetAngleSpeed(int index)
{ float [] anglespeed=new float[3]; if(m_receive_data_down==null)
{ return anglespeed;
}
switch (index)
{
case MAGIKARE_SENSOR_DOWN: anglespeed[0]=m_receive_data_down[3];
anglespeed[1]=m_receive_data_down[4];
anglespeed[2]=m_receive_data_down[5];
break;
case MAGIKARE_SENSOR_UP:
anglespeed[0]=m_receive_data_up[3];
anglespeed[1]=m_receive_data_up[4];
anglespeed[2]=m_receive_data_up[5];
break;
}
return anglespeed;
} public float[] GetQuaternion(int index)
{
float[] quaternion=new float[4]; if(m_receive_data_down==null)
{
return quaternion;
}
switch (index)
{
case MAGIKARE_SENSOR_DOWN:
quaternion[0]=m_receive_data_down[23];
quaternion[1]=m_receive_data_down[24];
quaternion[2]=m_receive_data_down[25];
quaternion[3]=m_receive_data_down[26];
Log.i("saveinfo","m_receive_data_down23"+m_receive_data_down[23]);
Log.i("saveinfo","m_receive_data_down24"+m_receive_data_down[24]);
Log.i("saveinfo","m_receive_data_down25"+m_receive_data_down[25]);
Log.i("saveinfo","m_receive_data_down26"+m_receive_data_down[26]);
break;
case MAGIKARE_SENSOR_UP:
quaternion[0]=m_receive_data_up[23];
quaternion[1]=m_receive_data_up[24];
quaternion[2]=m_receive_data_up[25];
quaternion[3]=m_receive_data_up[26];
break;
case MAGIKARE_SENSOR_CENTER:
quaternion[0]=m_receive_data_center[23];
quaternion[1]=m_receive_data_center[24];
quaternion[2]=m_receive_data_center[25];
quaternion[3]=m_receive_data_center[26];
}
return quaternion;
} }

二、BluetoothService

public class BluetoothService {
  private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private Context context;
//蓝牙适配器
private BluetoothAdapter mAdapter;
private Handler mHandler; //当前传感器设备的个数,即要开启的线程个数,用于设置线程数组的大小
//这里默认为1,因为我们目前只需要和一个传感器连接, 比如:你要连接两个硬件设备,那就设置值为2,这样就会开启两个线程,分别去执行想要操作
public static final int SENSEOR_NUM=; private AcceptThread mAcceptThread;// 请求连接的监听进程
private ConnectThread mConnectThread;// 连接一个设备的进程
public ConnectedThread[] mConnectedThread=new ConnectedThread[SENSEOR_NUM];// 已经连接之后的管理进程 private int mState;// 当前状态 // 指明连接状态的常量
public static final int STATE_NONE = ; //没有连接
public static final int STATE_LISTEN = ; //等待连接
public static final int STATE_CONNECTING = ; //正在连接
public static final int STATE_CONNECTED = ; //已经连接 public BluetoothService(Context context, Handler mHandler) {
this.context = context;
this.mHandler = mHandler;
mAdapter = BluetoothAdapter.getDefaultAdapter();//获取蓝牙适配器
mState = STATE_NONE ; //当前连接状态:未连接
} // 参数 index 是 硬件设备的id ,随便设的,目的在于当 同时连接多个硬件设备的时候,根据此id进行区分
public synchronized void connect(BluetoothDevice device, int index) { //连接一个蓝牙时,将该设备 的蓝牙连接线程关闭,如果有的话
//demo 就只有一个硬件设备,默认该设备id 取值index=1;
if (mConnectedThread[index-] != null) {
mConnectedThread[index-].cancel();
mConnectedThread[index-]=null;
}
mConnectThread=new ConnectThread(device,index);
mConnectThread.start();
setState(STATE_CONNECTING);
} private class ConnectThread extends Thread{
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private int index;
public ConnectThread(BluetoothDevice device,int index) {
mmDevice = device;
this.index=index;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);// Get a BluetoothSocket for a connection with the given BluetoothDevice
}
catch (IOException e) {}
mmSocket = tmp;
} public void run() { setName("ConnectThread");
//当连接成功,取消蓝牙适配器搜索蓝牙设备的操作,因为搜索操作非常耗时
mAdapter.cancelDiscovery();// Always cancel discovery because it will slow down a connection try {
mmSocket.connect();// This is a blocking call and will only return on a successful connection or an exception
}
catch (IOException e) {
connectionFailed(this.index);
try {
mmSocket.close();
} catch (IOException e2) {} BluetoothService.this.start();// 引用来说明要调用的是外部类的方法 run
return;
} synchronized (BluetoothService.this) {// Reset the ConnectThread because we're done
mConnectThread = null;
}
connected(mmSocket, mmDevice,index);// Start the connected thread
} public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
}
} class ConnectedThread extends Thread{
private BluetoothSocket mmSocket;
private InputStream mmInStream;
private OutputStream mmOutStream;
private int index;
private Queue<Byte> queueBuffer = new LinkedList<Byte>();
private byte[] packBuffer = new byte[]; //构造方法
public ConnectedThread(BluetoothSocket socket,int index) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
this.index=index;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {} mmInStream = tmpIn;
mmOutStream = tmpOut;
} // 数组大小看你的数据需求,这里存的是你处理蓝牙传输来的字节数据之后实际要用到的数据
private float [] fData=new float[]; @Override
public void run() {
byte[] tempInputBuffer = new byte[];
int acceptedLen = ; //记录每次读取数据的数据长度
byte sHead;
long lLastTime = System.currentTimeMillis(); //获取开始时间
while(true){
try {
acceptedLen = mmInStream.read(tempInputBuffer);//返回接收的长度
//从缓冲区中读取数据
for (int i = ; i < acceptedLen; i++) {
queueBuffer.add(tempInputBuffer[i]);
}
// 这里需要按个人硬件数据的情况自行修改了
// 如果你的硬件蓝牙传输 一个包有11个字节,那queueBuffer.size()>=11
// 如果你的硬件蓝牙传输 一个包有21个字节,那queueBuffer.size()>=21
while (queueBuffer.size()>=11){
//返回队首并删除,判断队首是不是0x55,如果不是,说明不是一个包的数据,跳过,
//注意这里的0x55是你的包的首字节
if (queueBuffer.poll()!=0x55)
continue;
// 进入到这里,说明得到一个包的数据了,然后就要根据个人硬件的数据情况,将byte类型的数据转换为float类型的数据
sHead = queueBuffer.poll(); //返回队首并删除
        
              // 现在得到的就是你数据部分了,如果有9位字节代表数据,j<9 ,如果有19位字节代表数据,j<19
                 
              //将字节数组存到packBuffer[]数据中,用于byte-->float数据的转换
for (int j = 0; j < 9; j++) {
packBuffer[j] = queueBuffer.poll();
}
switch (sHead) {//
case 0x52://角速度
fData[3] = ((((short) packBuffer[1]) << 8) | ((short) packBuffer[0] & 0xff)) / 2000;
fData[4] = ((((short) packBuffer[3]) << 8) | ((short) packBuffer[2] & 0xff)) / 200;
fData[5] = ((((short) packBuffer[5]) << 8) | ((short) packBuffer[4] & 0xff)) / 200;
fData[17] = ((((short) packBuffer[7]) << 8) | ((short) packBuffer[6] & 0xff)) / 100.0f;
break;
case 0x53://角度
fData[6] = ((((short) packBuffer[1]) << 8) | ((short) packBuffer[0] & 0xff)) / 180;
fData[7] = ((((short) packBuffer[3]) << 8) | ((short) packBuffer[2] & 0xff)) / 180;
fData[8] = ((((short) packBuffer[5]) << 8) | ((short) packBuffer[4] & 0xff)) / 180;
fData[17] = ((((short) packBuffer[7]) << 8) | ((short) packBuffer[6] & 0xff)) / 100.0f;
break;
case 0x59://四元数
fData[23] = ((((short) packBuffer[1]) << 8) | ((short) packBuffer[0] & 0xff)) / 327.0f;
fData[24] = ((((short) packBuffer[3]) << 8) | ((short) packBuffer[2] & 0xff)) /327.0f;
fData[25] = ((((short) packBuffer[5]) << 8) | ((short) packBuffer[4] & 0xff)) /327.0f;
fData[26] = ((((short) packBuffer[7]) << 8) | ((short) packBuffer[6] & 0xff)) /327.0f;
break;
}
}
long lTimeNow = System.currentTimeMillis(); // 获取收据转换之后的时间
// 如果数据处理后的时间 与 接收到数据的时间 的时间差>80 则发送消息传输数据,
// 这个时间需要看你硬件一秒钟发送的包的个数
if (lTimeNow - lLastTime > 80) {
lLastTime = lTimeNow;
Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_READ);
Bundle bundle = new Bundle();
bundle.putString("index",String.valueOf(this.index));
bundle.putFloatArray("Data", fData);
msg.setData(bundle);
mHandler.sendMessage(msg);
}
} catch (IOException e) {
connectionLost(this.index);
e.printStackTrace();
}
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {}
}
}
//连接失败
private void connectionFailed(int index) {
setState(STATE_LISTEN);
// Send a failure message back to the Activity
Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString("toast", "未能连接设备"+index);
bundle.putInt("device_id",index);
msg.setData(bundle);
mHandler.sendMessage(msg);
}
// 连接丢失
private void connectionLost(int index) {
setState(STATE_LISTEN);
Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString("toast", "设备丢失"+index);
bundle.putInt("device_id",index);
msg.setData(bundle);
mHandler.sendMessage(msg);
} //用于 蓝牙连接的Activity onResume()方法
public synchronized void start() {
// Cancel any thread attempting to make a connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
} if (mAcceptThread == null) {
mAcceptThread = new AcceptThread();
mAcceptThread.start();
}
setState(STATE_LISTEN);
} public synchronized void connected(BluetoothSocket socket,BluetoothDevice device,int index) {
Log.d("MAGIKARE","连接到线程"+index);
// Cancel the thread that completed the connection
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
// Cancel the accept thread because we only want to connect to one device
if (mAcceptThread != null) {
mAcceptThread.cancel();
mAcceptThread = null;
} // Start the thread to manage the connection and perform transmissions
mConnectedThread[index-] = new ConnectedThread(socket,index); mConnectedThread[index-].start(); // Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString("device_name", device.getName()+" "+index); msg.setData(bundle);
mHandler.sendMessage(msg); setState(STATE_CONNECTED);
} private synchronized void setState(int state) {
mState = state;
// Give the new state to the Handler so the UI Activity can update
mHandler.obtainMessage(MainActivity.MESSAGE_STATE_CHANGE, state, -).sendToTarget();
} private class AcceptThread extends Thread {
// The local server socket
private final BluetoothServerSocket mmServerSocket;
//private int index;
public AcceptThread() {
BluetoothServerSocket tmp = null;
// this.index=index;
// Create a new listening server socket
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord("BluetoothData", MY_UUID);
}
catch (IOException e) {}
mmServerSocket = tmp;
} public void run() {
new Thread(new Runnable() {
@Override
public void run() { }
}).start(); } public void cancel() { try {
if(mmServerSocket!=null) {
mmServerSocket.close();
}
}
catch (IOException e) {}
}
}
public synchronized int getState() {
return mState;
} public synchronized void stop() {
if (mConnectedThread != null) {
for(int i=;i<mConnectedThread.length;i++)
{
mConnectedThread[i].cancel();
}
mConnectedThread = null;
}
if (mAcceptThread != null) {
mAcceptThread.cancel();
mAcceptThread = null;
}
setState(STATE_NONE);
}
}

三、自定义即时变化的折线图:

public class MyView extends View {
/*http://www.cnblogs.com/aibuli/p/950c34f2bc0d02cbd290dd6a8339d42a.html*/
//坐标轴原点的位置
private int xPoint=;
private int yPoint=;
//刻度长度
private int xScale=; //8个单位构成一个刻度
private int yScale=;
//x与y坐标轴的长度
private int xLength=;
private int yLength=; private int MaxDataSize=xLength/xScale; //横坐标 最多可绘制的点 private List<Float> data=new ArrayList<Float>(); //存放 纵坐标 所描绘的点 private String[] yLabel=new String[yLength/yScale]; //Y轴的刻度上显示字的集合 private Handler mh=new Handler(){
public void handleMessage(android.os.Message msg) {
if(msg.what==){ //判断接受消息类型
MyView.this.invalidate(); //刷新View
}
};
};
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
for (int i = ; i <yLabel.length; i++) {
yLabel[i]=(i+)+"M/s";
}
new Thread(new Runnable() {
@Override
public void run() {
while(true){ //在线程中不断往集合中增加数据
try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(data.size()>MaxDataSize){ //判断集合的长度是否大于最大绘制长度
data.remove(); //删除头数据
}
// 这里得到蓝牙设备得到的数据
float[] floats = MainActivity.GetAngleSpeed(1);
data.add(floats[]);
mh.sendEmptyMessage(); //发送空消息通知刷新
}
}
}).start();
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint=new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setColor(Color.RED);
//绘制Y轴
canvas.drawLine(xPoint, yPoint-yLength, xPoint, yPoint, paint);
//绘制Y轴左右两边的箭头
canvas.drawLine(xPoint, yPoint-yLength, xPoint-,yPoint-yLength+, paint);
canvas.drawLine(xPoint, yPoint-yLength, xPoint+,yPoint-yLength+, paint);
//Y轴上的刻度与文字
for (int i = ; i * yScale< yLength; i++) {
canvas.drawLine(xPoint, yPoint-i*yScale, xPoint+, yPoint-i*yScale, paint); //刻度
canvas.drawText(yLabel[i], xPoint-, yPoint-i*yScale, paint);//文字
}
//X轴
canvas.drawLine(xPoint, yPoint, xPoint+xLength, yPoint, paint);
//如果集合中有数据
if(data.size()>){
for (int i = ; i < data.size(); i++) { //依次取出数据进行绘制
canvas.drawLine(xPoint+(i-)*xScale, yPoint-data.get(i-)*yScale, xPoint+i*xScale, yPoint-data.get(i)*yScale, paint);
}
} }
}

相关知识:浅谈Bluetooth蓝牙开发

有问题欢迎留言交流!

最新文章

  1. 给IDEA设置单独的JDK
  2. GeoServer中利用SLD配图之矢量图层配图
  3. [ASP.NET MVC] Real-time之HTML5 服务器发送事件(server-sent event)
  4. Android学习笔记——CheckBox
  5. 检测当前运行环境——移动端与PC端。
  6. PAT (Basic Level) Practise:1010. 一元多项式求导
  7. Oracle用户信息查询
  8. activiti工作流数据库表详细介绍 (23张表)
  9. MyBatis学习总结_13_Mybatis查询之resultMap和resultType区别
  10. lex&amp;yacc2
  11. 加快modelsim仿真速度的方法(原创)
  12. Day 3 @ RSA Conference Asia Pacific & Japan 2016 (afternoon)
  13. ural 1052 Rabbit Hunt
  14. 限制容器的 Block IO - 每天5分钟玩转 Docker 容器技术(29)
  15. 第2章 rsync算法原理和工作流程分析
  16. C# 反向生成工具(DAL BLL Modle)
  17. palacehoder的自定义样式【输入框input /文本域textarea】
  18. 【SparkStreaming学习之四】 SparkStreaming+kafka管理消费offset
  19. Idea环境下git 图形化操作
  20. windows修复分区卷:chkdsk

热门文章

  1. .NET平台开源项目速览(12)哈希算法集合类库HashLib
  2. Javascript优化细节:短路表达式
  3. PHP中AJAX的使用(完整实例【大牛可飘过】)
  4. html规范
  5. spring笔记--通过注解(annotation)配置Bean
  6. 将DataTable转换成CSV文件
  7. HTML5网页打开摄像头,并拍照
  8. GridView的使用
  9. ipsec IP安全策略操作 win7
  10. php导入excel表格