蓝牙开发
来源:互联网 发布:超星网络课程登录入口 编辑:程序博客网 时间:2024/05/21 11:10
关于WIFI就不多介绍啦,直接来个段子吧。
问:“WiFi对人体有伤害么?”
答:“不清楚,反正没有WiFi我就浑身不舒服。
比较重要的一点就是WifiManager wm=(WifiManager)Android_Wifi.this.getSystemService(Context.WIFI_SERVICE);
关闭打开搜索都可以通过调用wm的相关方法实现。可能要开发wifi万能钥匙那一类的程序才用到这个吧,普通应用程序主要就识别是wifi网络还是移动网络。不多讲
代码参考博客:http://blog.csdn.net/sbvfhp/article/details/7007090
重点蓝牙:
一:什么是蓝牙
1:Bluetooth是目前使用最广泛的无线通讯协议,近距离无线通讯的标准。传说瑞典有个国王特别爱吃蓝莓导致自己的牙齿天天都是蓝色的,在他执政期间这位国王非常善于交际,能说会到,和邻国的搞得关系非常好,这个Bluetooth的发明者觉得蓝牙它的作用就是在近距离沟通周围的设备,跟这个国王很类似,于是起名叫蓝牙。
2:主要针对短距离设备通讯(10米)
3:无线耳机,无线鼠标,无线键盘
二:蓝牙工作流程图
首先两个设备上都要有蓝牙设备或者专业一点叫蓝牙适配器,以手机和电脑为例我画了如下流程图。其次在手机上进行扫描,扫描周围蓝蓝牙设备,先找到手机附近的电脑,然后给它发出一个信号需要进行蓝牙的配对,再次返回一个信号说明手机和电脑已经配对成功了,最后配对成功后可以进行文件传输了。这是一个最基本的一个流程。
三:蓝牙开发相关类
在Android手机中开发蓝牙程序时,离不开几个类:
BluetoothSocket:close() connect() getInputStream()......
BluetoothServerSocket : accept()
BlutoothAdapter :代表本地的蓝牙适配器设备,通过此类可以让用户能执行基本的蓝牙任务。
BluetoothClass: 代表了一个描述设备通用特性和功能的蓝牙类。比如一个蓝牙类会指定如电话、计算机或耳机的通用设备类型。
BluetoothClass.Service:
BluetoothClass.Device:
BluetoothClass.Device.Major: 定义了主要设备类的常量
其中与蓝牙相关的最重要的两个API
1:BuletoothAdapter
这个类的对象代表了本地的蓝牙适配器,相当于蓝牙工作流程图中的手机里的蓝牙适配器,也就是说比如这个应用程序是运行在手机上,那么手机上的蓝牙适配器就是本地蓝牙适配器。
2:BuletoothDevice
这个类的对象代表了远程的蓝牙设备,相当于蓝牙工作流程图中的计算机里的蓝牙适配器,也就是说比如这个应用程序是运行在手机上,那么BuletoothDevice代表了你要连接的远程的那个设备上面的蓝牙适配器。
四:蓝牙开发步骤:
此部分转载于 http://zhouyunan2010.iteye.com/blog/1186021
从查找蓝牙设备到能够相互通信要经过几个基本步骤(本机做为服务器):
1.设置权限
在manifest中配置
<uses-permission android:name="android.permission.BLUETOOTH"/><uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
2.启动蓝牙
首先要查看本机是否支持蓝牙,获取BluetoothAdapter蓝牙适配器对象
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if(mBluetoothAdapter == null){ //表明此手机不支持蓝牙 return;}if(!mBluetoothAdapter.isEnabled()){ //蓝牙未开启,则开启蓝牙 Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableIntent, REQUEST_ENABLE_BT);}//......public void onActivityResult(int requestCode, int resultCode, Intent data){ if(requestCode == REQUEST_ENABLE_BT){ if(requestCode == RESULT_OK){ //蓝牙已经开启 } }}
3。发现蓝牙设备
这里可以细分为几个方面
(1)使本机蓝牙处于可见(即处于易被搜索到状态),便于其他设备发现本机蓝牙
//使本机蓝牙在300秒内可被搜索private void ensureDiscoverable() { if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300); startActivity(discoverableIntent); }}
(2)查找已经配对的蓝牙设备,即以前已经配对过的设备
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices(); if (pairedDevices.size() > 0) { findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); for (BluetoothDevice device : pairedDevices) { //device.getName() +" "+ device.getAddress()); } } else { mPairedDevicesArrayAdapter.add("没有找到已匹对的设备"); }
(3)通过mBluetoothAdapter.startDiscovery();搜索设备,要获得此搜索的结果需要注册
一个BroadcastReceiver来获取。先注册再获取信息,然后处理
//注册,当一个设备被发现时调用onReceiveIntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver, filter);//当搜索结束后调用onReceivefilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver, filter);//.......private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(BluetoothDevice.ACTION_FOUND.equals(action)){ BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // 已经配对的则跳过 if (device.getBondState() != BluetoothDevice.BOND_BONDED) { mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); //保存设备地址与名字 } }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { //搜索结束 if (mNewDevicesArrayAdapter.getCount() == 0) { mNewDevicesArrayAdapter.add("没有搜索到设备"); } } }};
4.建立连接
查找到设备 后,则需要建立本机与其他设备之间的连接。
一般用本机搜索其他蓝牙设备时,本机可以作为一个服务端,接收其他设备的连接。
启动一个服务器端的线程,死循环等待客户端的连接,这与ServerSocket极为相似。
这个线程在准备连接之前启动
5.交换数据
搜索到设备后可以获取设备的地址,通过此地址获取一个BluetoothDeviced对象,可以看做客户端,通过此对象device.createRfcommSocketToServiceRecord(MY_UUID);同一个UUID可与服务器建立连接获取另一个socket对象,由此服务端与客户端各有一个socket对象,此时
他们可以互相交换数据了。
创立客户端socket可建立线程
6.建立数据通信线程,进行读取数据
//建立连接后,进行数据通信的线程 private class ConnectedThread extends Thread{ private BluetoothSocket socket; private InputStream inStream; private OutputStream outStream; public ConnectedThread(BluetoothSocket socket){ this.socket = socket; try { //获得输入输出流 inStream = socket.getInputStream(); outStream = socket.getOutputStream(); } catch (IOException e) { Log.e("app", "temp sockets not created", e); } } public void run(){ byte[] buff = new byte[1024]; int len=0; //读数据需不断监听,写不需要 while(true){ try { len = inStream.read(buff); //把读取到的数据发送给UI进行显示 Message msg = handler.obtainMessage(BluetoothChat.MESSAGE_READ, len, -1, buff); msg.sendToTarget(); } catch (IOException e) { Log.e("app", "disconnected", e); connectionLost(); //失去连接 start(); //重新启动服务器 break; } } } public void write(byte[] buffer) { try { outStream.write(buffer); // Share the sent message back to the UI Activity handler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer) .sendToTarget(); } catch (IOException e) { Log.e("app", "Exception during write", e); } } public void cancel() { try { socket.close(); } catch (IOException e) { Log.e("app", "close() of connect socket failed", e); } } }
到这里,蓝牙通信的基本操作已经全部完成。
五:蓝牙聊天室开发
源码参考 http://download.csdn.net/detail/mr_raptor/8033951#comment
此demo界面优美,实现基本功能,但不能保存聊天记录,黑屏自动断线,在此基础做出修改。
相关知识:
在EditText中插入表情图片 (CharacterStyle&SpannableString)
源码分析:
- Android中pendingIntent的深入理解
Utils.java
1 package cn.com.farsgiht.bluetoothdemo.utils; 2 3 import android.app.Activity; 4 import android.app.Notification; 5 import android.app.NotificationManager; 6 import android.app.PendingIntent; 7 import android.content.Context; 8 import android.content.Intent; 9 import cn.com.farsgiht.bluetoothdemo.R;10 11 public class Utils {12 public static final int NOTIFY_ID1 = 1001;13 14 public static void notifyMessage(Context context, String msg, Activity activity){15 //Notification builder; 16 PendingIntent contentIntent = null; 17 NotificationManager nm;18 // 发送通知需要用到NotificationManager对象 19 nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);20 // 消息对象21 Intent notificationIntent = new Intent(context, activity.getClass());22 // PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags)23 // 用来获得一个挂起的PendingIntent,让该Intent去启动新的Activity来处理通知24 contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); 25 26 27 //定义通知栏展现的内容信息28 int icon = R.drawable.icon;29 long when = System.currentTimeMillis();30 Notification notification = new Notification(icon, msg, when);31 notification.defaults |= Notification.DEFAULT_VIBRATE;32 notification.defaults |= Notification.DEFAULT_SOUND; // 调用系统自带声音 33 notification.flags |= Notification.FLAG_AUTO_CANCEL; // 点击清除按钮或点击通知后会自动消失34 notification.defaults |= Notification.DEFAULT_LIGHTS;35 notification.vibrate = new long[]{300, 500};36 notification.setLatestEventInfo(context, "BluetoothChat", msg, contentIntent);37 /* // 定制我们要在状态栏显示的通知样式38 builder = new Notification(context);39 builder.setContentIntent(contentIntent)40 .setSmallIcon(R.drawable.ic_launcher)//设置状态栏里面的图标(小图标) .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.i5))//下拉下拉列表里面的图标(大图标) .setTicker("this is bitch!") //设置状态栏的显示的信息41 .setWhen(System.currentTimeMillis())//设置时间发生时间42 .setAutoCancel(true)//设置可以清除43 .setContentTitle("This is ContentTitle")//设置下拉列表里的标题44 .setContentText("this is ContentText");//设置上下文内容45 */ 46 // 获得刚才创建的通知对象47 // Notification notification = builder.getNotification();//获取一个Notification48 49 // 通过NotificationManger来发送通知消息50 // 参数1通知的ID,参数2发送哪个通知51 nm.notify(NOTIFY_ID1, notification);52 }53 }
通知栏相关代码
2. PageIndicatorView.java
1 package cn.com.farsgiht.bluetoothdemo.UI; 2 3 import cn.com.farsgiht.bluetoothdemo.R; 4 import android.content.Context; 5 import android.graphics.Bitmap; 6 import android.graphics.BitmapFactory; 7 import android.widget.ImageView; 8 import android.widget.LinearLayout; 9 10 public class PageIndicatorView extends ImageView {11 private final String TAG = "PageIndicatorView";12 private LinearLayout mPageIndicLayout;13 14 public PageIndicatorView(Context context) {15 super(context);16 setSelectedView(false);17 }18 19 public void setSelectedView(boolean selected){20 Bitmap bitmap;21 if(selected){22 bitmap = BitmapFactory.decodeResource(getResources(),23 R.drawable.page_select);24 }else{25 bitmap = BitmapFactory.decodeResource(getResources(),26 R.drawable.page_item);27 }28 this.setImageBitmap(bitmap);29 }30 }
表情页页面指示器
3. DrawerHScrollView.java
1 package cn.com.farsgiht.bluetoothdemo.UI; 2 3 import java.util.Hashtable; 4 5 import android.content.Context; 6 import android.util.AttributeSet; 7 import android.util.Log; 8 import android.view.Gravity; 9 import android.view.ViewGroup; 10 import android.view.ViewParent; 11 import android.widget.HorizontalScrollView; 12 import android.widget.LinearLayout; 13 14 public class DrawerHScrollView extends HorizontalScrollView { 15 private static final String TAG = "DrawerHScrollView"; 16 17 private int currentPage = 0; 18 private int totalPages = 1; 19 private static Hashtable<Integer, Integer> positionLeftTopOfPages = new Hashtable(); 20 private LinearLayout mPageIndicLayout; 21 private Context mContext; 22 23 public DrawerHScrollView(Context context) { 24 super(context); 25 this.mContext = context; 26 } 27 28 public DrawerHScrollView(Context context, AttributeSet attrs) { 29 super(context, attrs); 30 this.mContext = context; 31 } 32 33 public DrawerHScrollView(Context context, AttributeSet attrs, int defStyle) { 34 super(context, attrs, defStyle); 35 this.mContext = context; 36 } 37 38 public void cleanup(){ 39 currentPage = 0; 40 totalPages = 1; 41 if(positionLeftTopOfPages != null){ 42 positionLeftTopOfPages.clear(); 43 } 44 } 45 46 public void setParameters(int totalPages, int currentPage, int scrollDisX, int space) { 47 Log.d(TAG, "~~~~~setParameters totalPages:"+totalPages +",currentPage:"+ currentPage +",scrollDisX:"+scrollDisX); 48 this.totalPages = totalPages; 49 this.currentPage = currentPage; 50 positionLeftTopOfPages.clear(); 51 for (int i = 0;i < totalPages;i++){ 52 int posx = (scrollDisX) * i - space; 53 positionLeftTopOfPages.put(i, posx); 54 Log.d(TAG, "~~~~~setParameters i:"+i +",posx:"+posx); 55 } 56 smoothScrollTo(0, 0); 57 setPageIndicLayout(); 58 if(mPageIndicLayout != null){ 59 updateDrawerPageLayout(totalPages, currentPage); 60 } 61 } 62 63 public void setPageIndicLayout(){ 64 // 添加表情多页图标布局 65 mPageIndicLayout = new LinearLayout(mContext); 66 mPageIndicLayout.setGravity(Gravity.CENTER); 67 ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); 68 mPageIndicLayout.setLayoutParams(params); 69 ViewParent parent = this.getParent(); 70 if(parent instanceof LinearLayout){ 71 LinearLayout layout = (LinearLayout)parent; 72 layout.addView(mPageIndicLayout, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 73 } 74 } 75 76 @Override 77 public void fling(int velocityX) { 78 Log.v(TAG, "-->fling velocityX:"+velocityX); 79 boolean change_flag = false; 80 if (velocityX > 0 && (currentPage < totalPages - 1)){ 81 currentPage++; 82 change_flag = true; 83 } else if (velocityX < 0 && (currentPage > 0)){ 84 currentPage--; 85 change_flag = true; 86 } 87 if (change_flag){ 88 int postionTo = (Integer)positionLeftTopOfPages.get(new Integer(currentPage)).intValue(); 89 Log.v(TAG, "------smoothScrollTo posx:"+postionTo); 90 smoothScrollTo(postionTo, 0); 91 updateDrawerPageLayout(totalPages, currentPage); 92 } 93 //super.fling(velocityX); 94 } 95 96 public void updateDrawerPageLayout(int total_pages, int sel_page) { 97 Log.e(TAG, "~~~updateBooksPageLayout total_pages:" + total_pages 98 + ",sel_page:" + sel_page); 99 mPageIndicLayout.removeAllViews();100 if (total_pages <= 0 || sel_page < 0 || sel_page >= total_pages) {101 Log.e(TAG, "total_pages or sel_page is outofrange.");102 return;103 }104 for (int i = 0; i < total_pages; i++) {105 if (i != 0) {106 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(107 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);108 params.setMargins(5, 0, 0, 0);109 mPageIndicLayout.addView(new PageIndicatorView(mContext), params);110 } else {111 mPageIndicLayout.addView(new PageIndicatorView(mContext));112 }113 }114 PageIndicatorView selItem = (PageIndicatorView) mPageIndicLayout115 .getChildAt(sel_page);116 selItem.setSelectedView(true);117 }118 }
定制的表情页面,继承HorizontalScrollView,支持水平滑动。需要说明的是页面指示器的布局是通过代码实现的,而不是在xml文件中
4. ChatListViewAdapter.java
消息适配器。发送的消息存放在mDatalist中
在activity_chat.xml设置android:listSelector="#00FFFFFF"可以避免点击消息时出现的矩形框。
重写的getView中实现自己发送的消息显示在右边,别人发送的消息显示在左边。
TouchListener实现触摸消息变成密码形态,并在onclick方法中再次调用。我想大概是因为触摸和点击本来就不好区分吧。同时在点击事件里不要忘了设置消息显示的形态,因为listview在重绘时需要考虑这一点。
Adapter的作用就是ListView界面与数据之间的桥梁,当列表里的每一项显示到页面时,都会调用Adapter的getView方法返回一个View。
优化的思路两种:
1. View的重用
View的每次创建是比较耗时的,因此对于getview方法传入的convertView应充分利用 != null的判断
2.ViewHolder的应用
View的findViewById()方法也是比较耗时的,因此需要考虑只调用一次,ViewHolder就是一个持有者的类,他里面一般没有方法,只有属性,作用就是一个临时的储存器,把你getView方法中每次返回的View存起来,可以下次再用。这样做的好处就是不必每次都到布局文件中去拿到你的View,提高了效率。
5. Task.java定义了一些常量
6. TaskService.java 在Service中新开线程和直接新开线程的区别与意义
前面写到的蓝牙通信的几步走也是在这里面实现的
分为几个线程:
AcceptThread 等待客户端连接线程
ConnectThread(BluetoothDevice device) 作为客户端连接指定的蓝牙设备线程
ConnectedThread(BluetoothSocket socket) 在客户端,使用一个单独的BluetoothSocket类去初始化一个外接连接和管理该连接,发送消息也在这个线程里面
TaskThread总线程和mTaskList一起协调上面三个线程的运行工作。
要搞清TaskServic的原理,理清两个变量很重要:mServiceHandler mActivityHandler
mServiceHandler:监控着连接状态的变化,如断线,连接成功,连接中
mActivityHandler:监控着信息状态的变化,起到通知主Activity的作用。如:发消息,收消息。
7. SoundEffect.java
音效相关的类,实现了一个OnLoadCompleteListener接口,还有一个play() 方法,决定播放哪一首歌曲。
8. DataProtocol 和 Message
DataProtocol 是对不同的消息类型进行打包和解包
Message 定义了消息的几个参数
9. SelectDevice DownloadActivity ChatActivity 是应用程序里面的三个Activity
保存和恢复activity的状态数据
自己修改说明:
ChatActivity加入再按一次退出功能,更加人性化。加入消息记录功能。
消息记录实现概述:当在服务中开始收发发送消息时,就通过mActivityHandler通知ChatActivity,完成数据库读写操作。
ChatActivity.java:
1 package cn.com.farsgiht.bluetoothdemo; 2 3 import java.text.SimpleDateFormat; 4 import java.util.ArrayList; 5 import java.util.HashMap; 6 import android.app.Activity; 7 import android.app.AlertDialog; 8 import android.bluetooth.BluetoothAdapter; 9 import android.bluetooth.BluetoothDevice; 10 import android.content.ContentValues; 11 import android.content.Context; 12 import android.content.DialogInterface; 13 import android.content.DialogInterface.OnClickListener; 14 import android.content.Intent; 15 import android.database.sqlite.SQLiteDatabase; 16 import android.graphics.Bitmap; 17 import android.graphics.BitmapFactory; 18 import android.os.Bundle; 19 import android.os.Handler; 20 import android.os.Message; 21 import android.text.Spannable; 22 import android.text.SpannableString; 23 import android.text.style.ImageSpan; 24 import android.util.DisplayMetrics; 25 import android.util.Log; 26 import android.view.Display; 27 import android.view.Gravity; 28 import android.view.KeyEvent; 29 import android.view.LayoutInflater; 30 import android.view.Menu; 31 import android.view.MenuInflater; 32 import android.view.MenuItem; 33 import android.view.View; 34 import android.view.ViewGroup; 35 import android.view.inputmethod.InputMethodManager; 36 import android.widget.AdapterView; 37 import android.widget.AdapterView.OnItemClickListener; 38 import android.widget.Button; 39 import android.widget.EditText; 40 import android.widget.GridView; 41 import android.widget.ImageView; 42 import android.widget.LinearLayout; 43 import android.widget.LinearLayout.LayoutParams; 44 import android.widget.ListView; 45 import android.widget.SimpleAdapter; 46 import android.widget.Toast; 47 import cn.com.farsgiht.bluetoothdemo.UI.ChatListViewAdapter; 48 import cn.com.farsgiht.bluetoothdemo.UI.DrawerHScrollView; 49 import cn.com.farsgiht.bluetoothdemo.sound.SoundEffect; 50 import cn.com.farsgiht.bluetoothdemo.task.Task; 51 import cn.com.farsgiht.bluetoothdemo.task.TaskService; 52 import cn.com.farsgiht.bluetoothdemo.utils.Utils; 53 54 public class ChatActivity extends Activity implements View.OnClickListener{ 55 private final String TAG = "ChatActivity"; 56 public static int sAliveCount = 0; 57 public static final String EXTRA_MESSAGER = "cn.com.farsgiht.bluetoothdemo.BUNDLE"; 58 public static final String DEVICE_NAME = "device_name"; 59 // 蓝牙状态变量 60 private static int sBTState = -1; 61 62 private final int REQUES_BT_ENABLE_CODE = 123; 63 private final int REQUES_SELECT_BT_CODE = 222; 64 65 private ListView mList; 66 private EditText mInput; 67 private Button mSendBtn; 68 private ImageView mEmoButton; 69 private GridView mGridView; 70 private boolean isUpdate = false; 71 private BluetoothDevice mRemoteDevice; 72 73 private LinearLayout mRootLayout, mChatLayout; 74 75 private View mEmoView; 76 private boolean isShowEmo = false; 77 private boolean isHaspressed = false; 78 private int mScrollHeight; 79 80 private DrawerHScrollView mScrollView; 81 private ChatListViewAdapter mAdapter2; 82 private ArrayList<HashMap<String, Object>> mChatContent2 = new ArrayList<HashMap<String, Object>>(); 83 private BluetoothAdapter mBluetoothAdapter; 84 85 private ArrayList<HashMap<String, Object>> mEmoList = new ArrayList<HashMap<String, Object>>(); 86 // 已连接设备的名字 87 private String mConnectedDeviceName = null; 88 89 private Handler mHandler = new Handler(){ 90 @Override 91 public void handleMessage(Message msg) { 92 //设置聊天信息的时间 93 SimpleDateFormat df0 = new SimpleDateFormat("MM-dd HH:mm:ss"); 94 String pdate=df0.format(System.currentTimeMillis()).toString(); 95 switch(msg.what){ 96 case -1: 97 showToast("没有连接其它用户,点击\"Menu\"扫描并选择周国用户"); 98 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_ERR); 99 break;100 case Task.TASK_SEND_MSG:101 // showToast(msg.obj.toString());102 String writeMessage = msg.obj.toString();103 if(writeMessage!=null && isHaspressed)104 {105 //将发送的信息插入到数据库106 ContentValues values=new ContentValues();107 values.put("name", "我");108 values.put("pdate",pdate);109 values.put("informations", writeMessage);110 //创建数据库111 DatabaseHelper insertdbHelper=new DatabaseHelper(ChatActivity.this,"zhsf_db");112 SQLiteDatabase insertdb=insertdbHelper.getWritableDatabase();113 insertdb.insert("info", null, values);114 }115 if(sAliveCount <= 0){116 Utils.notifyMessage(ChatActivity.this, msg.obj.toString(), ChatActivity.this);117 } 118 break; 119 case Task.TASK_RECV_MSG: 120 String readMessage =((HashMap<String,Object>) msg.obj).get(ChatListViewAdapter.KEY_TEXT).toString();121 mConnectedDeviceName = ((HashMap<String,Object>) msg.obj).get(ChatListViewAdapter.KEY_NAME).toString(); 122 if(readMessage!=null)123 {124 //将接受的信息插入到数据库125 ContentValues values2=new ContentValues();126 values2.put("name", mConnectedDeviceName);127 values2.put("pdate",pdate);128 values2.put("informations", readMessage);129 DatabaseHelper insertdbHelper2=new DatabaseHelper(ChatActivity.this,"zhsf_db");130 SQLiteDatabase insertdb2=insertdbHelper2.getWritableDatabase();131 insertdb2.insert("info", null, values2); 132 }133 134 if(msg.obj == null)135 return;136 if(msg.obj instanceof HashMap<?, ?>){137 showTargetMessage((HashMap<String, Object>) msg.obj); 138 } 139 if(sAliveCount <= 0){140 Utils.notifyMessage(ChatActivity.this, "您有未读取消息", ChatActivity.this);141 } 142 break;143 case Task.TASK_GET_REMOTE_STATE:144 setTitle((String)msg.obj);145 if(sAliveCount <= 0){146 if(isBTStateChanged(msg.arg1) && msg.arg1 != TaskService.BT_STAT_WAIT)147 Utils.notifyMessage(ChatActivity.this, (String)msg.obj, ChatActivity.this);148 }149 break;150 151 }152 }153 };154 155 @Override156 protected void onCreate(Bundle savedInstanceState) {157 super.onCreate(savedInstanceState);158 setContentView(R.layout.activity_chat);159 160 // 获得蓝牙管理器161 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 162 if (mBluetoothAdapter == null) {163 Log.e(TAG, "Your device is not support Bluetooth!");164 Toast.makeText(this, "该设备没有蓝牙设备", Toast.LENGTH_LONG).show();165 return;166 }167 168 mRootLayout = (LinearLayout) findViewById(R.id.root);169 mChatLayout = (LinearLayout) findViewById(R.id.topPanel);170 mList = (ListView) findViewById(R.id.listView1);171 172 mAdapter2 = new ChatListViewAdapter(this, mChatContent2);173 174 mList.setAdapter(mAdapter2);175 176 // 初始化表情177 mEmoView = initEmoView();178 179 mInput = (EditText) findViewById(R.id.inputEdit);180 mInput.setOnClickListener(new android.view.View.OnClickListener() {181 @Override182 public void onClick(View v) {183 // 点击输入框后,隐藏表情,显示输入法184 InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 185 imm.showSoftInput(mInput, 0);186 showEmoPanel(false);187 }188 });189 190 mSendBtn = (Button) findViewById(R.id.sendBtn);191 mEmoButton = (ImageView) findViewById(R.id.emotionBtn);192 193 mSendBtn.setOnClickListener(this);194 mEmoButton.setOnClickListener(this);195 196 //---------------------------------------------------------------------197 // 打开蓝牙设备198 if (!mBluetoothAdapter.isEnabled()) {199 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);200 startActivityForResult(enableBtIntent, REQUES_BT_ENABLE_CODE); 201 }else{202 // 默认设备作为服务端203 startServiceAsServer();204 }205 //---------------------------------------------------------------------206 }207 208 private View initEmoView(){209 if(mEmoView == null){210 LayoutInflater inflater = getLayoutInflater();211 mEmoView = inflater.inflate(R.layout.emo_layout, null);212 213 mScrollView = (DrawerHScrollView) mEmoView.findViewById(R.id.scrollView);214 mGridView = (GridView) mEmoView.findViewById(R.id.gridView);215 mGridView.setOnItemClickListener(new OnItemClickListener() {216 @Override217 public void onItemClick(AdapterView<?> parent, View view,218 int position, long id) {219 // 在android中要显示图片信息,必须使用Bitmap位图的对象来装载 220 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), (Integer) mEmoList.get(position).get("img")); 221 ImageSpan imageSpan = new ImageSpan(ChatActivity.this, bitmap); 222 SpannableString spannableString = new SpannableString((String) mEmoList.get(position).get("text"));//face就是图片的前缀名 223 spannableString.setSpan(imageSpan, 0, 8,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 224 mInput.append(spannableString);225 System.out.println("mInput:"+mInput.getText());226 }227 });228 229 mScrollHeight = setScrollGridView(mScrollView, mGridView, 3);230 System.out.println("mScrollHeight:" + mScrollHeight);231 }232 return mEmoView;233 }234 235 236 237 private void startServiceAsServer(){238 TaskService.start(this, mHandler);239 TaskService.newTask(new Task(mHandler, Task.TASK_START_ACCEPT, null));240 SoundEffect.getInstance(this).play(SoundEffect.SOUND_PLAY);241 }242 243 @Override244 protected void onResume() {245 sAliveCount++;246 super.onResume();247 }248 249 @Override250 protected void onPause() {251 sAliveCount--;252 super.onPause();253 }254 255 @Override256 protected void onDestroy() {257 super.onDestroy();258 // 关闭蓝牙259 if(mBluetoothAdapter.isEnabled())260 mBluetoothAdapter.disable();261 // 停止服务262 TaskService.stop(this);263 }264 265 266 @Override267 public void onClick(View v) {268 if(v == mSendBtn){269 String msg = mInput.getText().toString().trim();270 TaskService.newTask(new Task(mHandler, Task.TASK_GET_REMOTE_STATE, null));//通过点击按钮触发相应线程的启动,比较巧妙,值得学习271 if(msg.length() == 0){272 showToast("聊天内容为空");273 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_ERR);274 return;275 }276 277 //------ DEUBG ------ 278 TaskService.newTask(new Task(mHandler, Task.TASK_SEND_MSG, new Object[]{msg}));279 showOwnMessage(msg);//立马显示自己发送的消息,所以在handler里面就没有再做处理280 isHaspressed = true;//数据库可以开始记录消息啦281 mInput.setText("");282 }else if(v == mEmoButton){283 System.out.println("Emo btn clicked");284 // 关闭输入法285 InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); 286 imm.hideSoftInputFromWindow(mInput.getWindowToken(),0);287 if(isShowEmo){288 showEmoPanel(false);289 }else{290 showEmoPanel(true);291 }292 }293 }294 295 private void showEmoPanel(boolean show){296 if(show && !isShowEmo){297 mEmoView.setVisibility(View.VISIBLE);298 mEmoButton.setImageResource(R.drawable.emo_collapse);299 ViewGroup.LayoutParams params = mChatLayout.getLayoutParams();300 params.height = mChatLayout.getHeight() - mScrollHeight;301 mChatLayout.setLayoutParams(params);302 isShowEmo = true;303 }else if(!show && isShowEmo){304 mEmoView.setVisibility(View.GONE);305 mEmoButton.setImageResource(R.drawable.emo_bkg);306 ViewGroup.LayoutParams params = mChatLayout.getLayoutParams();307 params.height = mChatLayout.getHeight() + mScrollHeight;308 mChatLayout.setLayoutParams(params);309 isShowEmo = false;310 }311 if(!isUpdate && show){312 LinearLayout.LayoutParams para = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);313 mRootLayout.addView(mEmoView, para);314 isUpdate = true;315 }316 }317 318 319 320 private boolean isBTStateChanged(int now){321 if(sBTState != now){322 sBTState = now;323 return true;324 }else{325 return false;326 }327 }328 329 /**330 * 显示对方信息331 * @param data332 */333 private void showTargetMessage(HashMap<String, Object> data){334 SimpleDateFormat df1 = new SimpleDateFormat("E MM月dd日 HH:mm ");335 data.put(ChatListViewAdapter.KEY_DATE, df1.format(System.currentTimeMillis()).toString());336 data.put(ChatListViewAdapter.KEY_SHOW_MSG, true);337 mChatContent2.add(data);338 mAdapter2.notifyDataSetChanged();339 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_RECV);340 }341 342 /**343 * 显示自己信息344 * @param data345 */346 private void showOwnMessage(String msg){347 HashMap<String, Object> map = new HashMap<String, Object>();348 map.put(ChatListViewAdapter.KEY_ROLE, ChatListViewAdapter.ROLE_OWN);//哪个角色的消息349 map.put(ChatListViewAdapter.KEY_NAME, mBluetoothAdapter.getName());350 map.put(ChatListViewAdapter.KEY_TEXT, msg);351 SimpleDateFormat df2 = new SimpleDateFormat("E MM月dd日 HH:mm ");352 map.put(ChatListViewAdapter.KEY_DATE, df2.format(System.currentTimeMillis()).toString());353 map.put(ChatListViewAdapter.KEY_SHOW_MSG, true);354 mChatContent2.add(map);355 mAdapter2.notifyDataSetChanged();356 SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_SEND);357 }358 359 @Override360 public boolean onCreateOptionsMenu(Menu menu) {361 MenuInflater inflater = getMenuInflater();362 inflater.inflate(R.menu.option_menu, menu);363 return true;364 }365 366 @Override367 public boolean onOptionsItemSelected(MenuItem item) {368 switch (item.getItemId()) {369 case R.id.scan:370 startActivityForResult(new Intent(this, SelectDevice.class), REQUES_SELECT_BT_CODE);371 break;372 case R.id.discoverable:373 // 调用设置用户名方法374 AlertDialog.Builder dlg = new AlertDialog.Builder(this);375 final EditText devNameEdit = new EditText(this);376 dlg.setView(devNameEdit);377 dlg.setTitle("请输入用户名");378 dlg.setPositiveButton("设置", new OnClickListener() {379 public void onClick(DialogInterface dialog, int which) {380 if(devNameEdit.getText().toString().length() != 0)381 mBluetoothAdapter.setName(devNameEdit.getText().toString());382 }383 });384 dlg.create();385 dlg.show();386 return true;387 case R.id.record:388 Intent recordIntent = new Intent(ChatActivity.this, RecordListActivity.class);389 startActivity(recordIntent);390 return true;391 case R.id.exit:392 Intent aboutIntent = new Intent(ChatActivity.this, DownloadActivity.class);393 startActivity(aboutIntent);394 return true;395 }396 return false;397 }398 399 @Override400 protected void onActivityResult(int requestCode, int resultCode, Intent data) {401 if(requestCode == REQUES_BT_ENABLE_CODE && resultCode == RESULT_OK){402 startServiceAsServer();403 }else if(requestCode == REQUES_SELECT_BT_CODE && resultCode == RESULT_OK){404 mRemoteDevice = data.getParcelableExtra("DEVICE");405 if(mRemoteDevice == null)406 return;407 TaskService.newTask(new Task(mHandler, Task.TASK_START_CONN_THREAD, new Object[]{mRemoteDevice}));408 }409 super.onActivityResult(requestCode, resultCode, data);410 }411 412 private void showToast(String msg){413 Toast tst = Toast.makeText(this, msg, Toast.LENGTH_SHORT);414 tst.setGravity(Gravity.CENTER | Gravity.TOP, 0, 240);415 tst.show();416 }417 418 419 // 设置表情的多页滚动显示控件420 public int setScrollGridView(DrawerHScrollView scrollView, GridView gridView, 421 int lines) {422 423 DisplayMetrics dm = new DisplayMetrics();424 getWindowManager().getDefaultDisplay().getMetrics(dm); 425 Display display = getWindowManager().getDefaultDisplay();426 System.out.println("Width:" + display.getWidth());427 System.out.println("Height:" + display.getHeight());428 429 430 int scrollWid = display.getWidth();431 int scrollHei;432 System.out.println("scrollWid:" + scrollWid);433 if (scrollWid <= 0 ){434 Log.d(TAG, "scrollWid or scrollHei is less than 0");435 return 0;436 }437 438 439 float density = dm.density; // 屏幕密度(像素比例:0.75/1.0/1.5/2.0)440 441 int readlViewWidht = 56;442 // 图片都放在了Hdpi中,所以计算出图片的像素独立宽度443 int viewWidth = (int) (readlViewWidht * density / 1.5);444 int viewHeight = viewWidth;445 System.out.println("viewWidth:" + viewWidth + " viewHeight:" + viewHeight);446 447 int numColsPage = scrollWid / viewWidth;448 int spaceing = (scrollWid - viewWidth * numColsPage)/(numColsPage);449 System.out.println("Space:" + spaceing);450 451 452 SimpleAdapter adapter = getEmoAdapter();453 int pages = adapter.getCount() / (numColsPage * lines);454 455 if (pages * numColsPage * lines < adapter.getCount()){456 pages++;457 }458 459 System.out.println("pages:" + pages);460 461 scrollHei = lines * viewHeight + spaceing * (lines + 1);462 463 LayoutParams params = new LayoutParams(pages * scrollWid, LayoutParams.WRAP_CONTENT);464 gridView.setLayoutParams(params);465 gridView.setColumnWidth(viewWidth);466 gridView.setHorizontalSpacing(spaceing);467 gridView.setVerticalSpacing(spaceing);468 gridView.setStretchMode(GridView.NO_STRETCH);469 gridView.setNumColumns(numColsPage * pages);470 471 //adapter = new DrawerListAdapter(this, colWid, colHei);472 //listener = new DrawerItemClickListener();473 gridView.setAdapter(adapter);474 //mGridView.setOnItemClickListener(listener);475 476 scrollView.setParameters(pages, 0, scrollWid, spaceing);477 //updateDrawerPageLayout(pageNum, 0);478 // 表情区域还要加上分布显示区479 int pageNumHei = (int) (18 * density); 480 return scrollHei + pageNumHei;481 }482 483 484 private SimpleAdapter getEmoAdapter(){485 HashMap<String, Object> map = new HashMap<String, Object>();486 map.put("img", R.drawable.emo001);487 map.put("text", "<emo001>");488 mEmoList.add(map);489 map = new HashMap<String, Object>();490 map.put("img", R.drawable.emo002);491 map.put("text", "<emo002>");492 mEmoList.add(map);493 map = new HashMap<String, Object>();494 map.put("img", R.drawable.emo003);495 map.put("text", "<emo003>");496 mEmoList.add(map);497 map = new HashMap<String, Object>();498 map.put("img", R.drawable.emo004);499 map.put("text", "<emo004>");500 mEmoList.add(map);501 map = new HashMap<String, Object>();502 map.put("img", R.drawable.emo005);503 map.put("text", "<emo005>");504 mEmoList.add(map);505 map = new HashMap<String, Object>();506 map.put("img", R.drawable.emo006);507 map.put("text", "<emo006>");508 mEmoList.add(map);509 map = new HashMap<String, Object>();510 map.put("img", R.drawable.emo007);511 map.put("text", "<emo007>");512 mEmoList.add(map);513 map = new HashMap<String, Object>();514 map.put("img", R.drawable.emo008);515 map.put("text", "<emo008>");516 mEmoList.add(map);517 map = new HashMap<String, Object>();518 map.put("img", R.drawable.emo009);519 map.put("text", "<emo009>");520 mEmoList.add(map);521 map = new HashMap<String, Object>();522 map.put("img", R.drawable.emo010);523 map.put("text", "<emo010>");524 mEmoList.add(map);525 map = new HashMap<String, Object>();526 map.put("img", R.drawable.emo011);527 map.put("text", "<emo011>");528 mEmoList.add(map);529 map = new HashMap<String, Object>();530 map.put("img", R.drawable.emo012);531 map.put("text", "<emo012>");532 mEmoList.add(map);533 map = new HashMap<String, Object>();534 map.put("img", R.drawable.emo013);535 map.put("text", "<emo013>");536 mEmoList.add(map);537 map = new HashMap<String, Object>();538 map.put("img", R.drawable.emo014);539 map.put("text", "<emo014>");540 mEmoList.add(map);541 map = new HashMap<String, Object>();542 map.put("img", R.drawable.emo015);543 map.put("text", "<emo015>");544 mEmoList.add(map);545 map = new HashMap<String, Object>();546 map.put("img", R.drawable.emo016);547 map.put("text", "<emo016>");548 mEmoList.add(map);549 map = new HashMap<String, Object>();550 map.put("img", R.drawable.emo017);551 map.put("text", "<emo017>");552 mEmoList.add(map);553 map = new HashMap<String, Object>();554 map.put("img", R.drawable.emo018);555 map.put("text", "<emo018>");556 mEmoList.add(map);557 map = new HashMap<String, Object>();558 map.put("img", R.drawable.emo019);559 map.put("text", "<emo019>");560 mEmoList.add(map);561 map = new HashMap<String, Object>();562 map.put("img", R.drawable.emo020);563 map.put("text", "<emo020>");564 mEmoList.add(map);565 map = new HashMap<String, Object>();566 map.put("img", R.drawable.emo021);567 map.put("text", "<emo021>");568 mEmoList.add(map);569 map = new HashMap<String, Object>();570 map.put("img", R.drawable.emo022);571 map.put("text", "<emo022>");572 mEmoList.add(map);573 map = new HashMap<String, Object>();574 map.put("img", R.drawable.emo023);575 map.put("text", "<emo023>");576 mEmoList.add(map);577 map = new HashMap<String, Object>();578 map.put("img", R.drawable.emo024);579 map.put("text", "<emo024>");580 mEmoList.add(map);581 map = new HashMap<String, Object>();582 map.put("img", R.drawable.emo025);583 map.put("text", "<emo025>");584 mEmoList.add(map);585 map = new HashMap<String, Object>();586 map.put("img", R.drawable.emo026);587 map.put("text", "<emo026>");588 mEmoList.add(map);589 map = new HashMap<String, Object>();590 map.put("img", R.drawable.emo027);591 map.put("text", "<emo027>");592 mEmoList.add(map);593 map = new HashMap<String, Object>();594 map.put("img", R.drawable.emo028);595 map.put("text", "<emo028>");596 mEmoList.add(map);597 map = new HashMap<String, Object>();598 map.put("img", R.drawable.emo029);599 map.put("text", "<emo029>");600 mEmoList.add(map);601 map = new HashMap<String, Object>();602 map.put("img", R.drawable.emo030);603 map.put("text", "<emo030>");604 mEmoList.add(map);605 map = new HashMap<String, Object>();606 map.put("img", R.drawable.emo031);607 map.put("text", "<emo031>");608 mEmoList.add(map);609 map = new HashMap<String, Object>();610 map.put("img", R.drawable.emo032);611 map.put("text", "<emo032>");612 mEmoList.add(map);613 map = new HashMap<String, Object>();614 map.put("img", R.drawable.emo033);615 map.put("text", "<emo033>");616 mEmoList.add(map);617 map = new HashMap<String, Object>();618 map.put("img", R.drawable.emo034);619 map.put("text", "<emo034>");620 mEmoList.add(map);621 map = new HashMap<String, Object>();622 map.put("img", R.drawable.emo035);623 map.put("text", "<emo035>");624 mEmoList.add(map);625 map = new HashMap<String, Object>();626 map.put("img", R.drawable.emo036);627 map.put("text", "<emo036>");628 mEmoList.add(map);629 map = new HashMap<String, Object>();630 map.put("img", R.drawable.emo037);631 map.put("text", "<emo037>");632 mEmoList.add(map);633 map = new HashMap<String, Object>();634 map.put("img", R.drawable.emo038);635 map.put("text", "<emo038>");636 mEmoList.add(map);637 map = new HashMap<String, Object>();638 map.put("img", R.drawable.emo039);639 map.put("text", "<emo039>");640 mEmoList.add(map);641 map = new HashMap<String, Object>();642 map.put("img", R.drawable.emo040);643 map.put("text", "<emo040>");644 mEmoList.add(map);645 map = new HashMap<String, Object>();646 map.put("img", R.drawable.emo041);647 map.put("text", "<emo041>");648 mEmoList.add(map);649 map = new HashMap<String, Object>();650 map.put("img", R.drawable.emo042);651 map.put("text", "<emo042>");652 mEmoList.add(map);653 map = new HashMap<String, Object>();654 map.put("img", R.drawable.emo043);655 map.put("text", "<emo043>");656 mEmoList.add(map);657 map = new HashMap<String, Object>();658 map.put("img", R.drawable.emo044);659 map.put("text", "<emo044>");660 mEmoList.add(map);661 map = new HashMap<String, Object>();662 map.put("img", R.drawable.emo045);663 map.put("text", "<emo045>");664 mEmoList.add(map);665 map = new HashMap<String, Object>();666 map.put("img", R.drawable.emo046);667 map.put("text", "<emo046>");668 mEmoList.add(map);669 map = new HashMap<String, Object>();670 map.put("img", R.drawable.emo047);671 map.put("text", "<emo047>");672 mEmoList.add(map);673 map = new HashMap<String, Object>();674 map.put("img", R.drawable.emo048);675 map.put("text", "<emo048>");676 mEmoList.add(map);677 map = new HashMap<String, Object>();678 map.put("img", R.drawable.emo049);679 map.put("text", "<emo049>");680 mEmoList.add(map);681 map = new HashMap<String, Object>();682 map.put("img", R.drawable.emo050);683 map.put("text", "<emo050>");684 mEmoList.add(map);685 map = new HashMap<String, Object>();686 map.put("img", R.drawable.emo051);687 map.put("text", "<emo051>");688 mEmoList.add(map);689 map = new HashMap<String, Object>();690 map.put("img", R.drawable.emo052);691 map.put("text", "<emo052>");692 mEmoList.add(map);693 map = new HashMap<String, Object>();694 map.put("img", R.drawable.emo053);695 map.put("text", "<emo053>");696 mEmoList.add(map);697 map = new HashMap<String, Object>();698 map.put("img", R.drawable.emo054);699 map.put("text", "<emo054>");700 mEmoList.add(map);701 map = new HashMap<String, Object>();702 map.put("img", R.drawable.emo055);703 map.put("text", "<emo055>");704 mEmoList.add(map);705 map = new HashMap<String, Object>();706 map.put("img", R.drawable.emo056);707 map.put("text", "<emo056>");708 mEmoList.add(map);709 map = new HashMap<String, Object>();710 map.put("img", R.drawable.emo057);711 map.put("text", "<emo057>");712 mEmoList.add(map);713 map = new HashMap<String, Object>();714 map.put("img", R.drawable.emo058);715 map.put("text", "<emo058>");716 mEmoList.add(map);717 map = new HashMap<String, Object>();718 map.put("img", R.drawable.emo059);719 map.put("text", "<emo059>");720 mEmoList.add(map);721 map = new HashMap<String, Object>();722 map.put("img", R.drawable.emo060);723 map.put("text", "<emo060>");724 mEmoList.add(map);725 726 /**727 * 上述添加表情效率高,但是代码太冗余,下面的方式代码简单,但是效率较低728 */729 /*730 HashMap<String, Integer> map;731 for(int i = 0; i < 100; i++){732 map = new HashMap<String, Integer>();733 Field field=R.drawable.class.getDeclaredField("image"+i); 734 int resourceId=Integer.parseInt(field.get(null).toString());735 map.put("img", resourceId);736 mEmoList.add(map);737 }738 */739 return new SimpleAdapter(this, mEmoList, R.layout.grid_view_item, 740 new String[]{"img"}, new int[]{R.id.imageView});741 }742 743 744 long waitTime = 2000;745 long touchTime = 0;746 @Override747 public boolean onKeyDown(int keyCode, KeyEvent event) {748 if(event.getAction() == KeyEvent.ACTION_DOWN && KeyEvent.KEYCODE_BACK == keyCode) {749 long currentTime = System.currentTimeMillis();750 if((currentTime-touchTime)>=waitTime) {751 Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();752 touchTime = currentTime;753 }else {754 finish();755 }756 return true;757 }758 return super.onKeyDown(keyCode, event);759 }760 761 }
TaskService.java:
1 package cn.com.farsgiht.bluetoothdemo.task; 2 3 import java.io.BufferedInputStream; 4 import java.io.BufferedOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 import java.io.UnsupportedEncodingException; 9 import java.util.ArrayList; 10 import java.util.Calendar; 11 import java.util.HashMap; 12 import java.util.UUID; 13 import android.app.Service; 14 import android.bluetooth.BluetoothAdapter; 15 import android.bluetooth.BluetoothDevice; 16 import android.bluetooth.BluetoothServerSocket; 17 import android.bluetooth.BluetoothSocket; 18 import android.content.ContentValues; 19 import android.content.Context; 20 import android.content.Intent; 21 import android.database.sqlite.SQLiteDatabase; 22 import android.os.Bundle; 23 import android.os.Handler; 24 import android.os.IBinder; 25 import android.util.Log; 26 import android.widget.ArrayAdapter; 27 import android.widget.Toast; 28 import cn.com.farsgiht.bluetoothdemo.ChatActivity; 29 import cn.com.farsgiht.bluetoothdemo.UI.ChatListViewAdapter; 30 import cn.com.farsgiht.bluetoothdemo.protocol.DataProtocol; 31 import cn.com.farsgiht.bluetoothdemo.protocol.Message; 32 import cn.com.farsgiht.bluetoothdemo.sound.SoundEffect; 33 34 /** 35 * 任务处理服务 36 * @author Administrator 37 */ 38 public class TaskService extends Service { 39 public static final int BT_STAT_WAIT = 0; 40 public static final int BT_STAT_CONN = 1; 41 public static final int BT_STAT_ONLINE = 2; 42 public static final int BT_STAT_UNKNOWN = 3; 43 44 45 public static final String DEVICE_NAME = "device_name"; 46 47 private final String TAG = "TaskService"; 48 private TaskThread mThread; 49 50 private BluetoothAdapter mBluetoothAdapter; 51 52 private AcceptThread mAcceptThread; 53 private ConnectThread mConnectThread; 54 private ConnectedThread mCommThread; 55 56 private boolean isServerMode = true; 57 58 private static Handler mActivityHandler; 59 60 // 任务队列 61 private static ArrayList<Task> mTaskList = new ArrayList<Task>(); 62 63 private Handler mServiceHandler = new Handler() { 64 @Override 65 public void handleMessage(android.os.Message msg) { 66 switch (msg.what) { 67 case Task.TASK_GET_REMOTE_STATE: 68 android.os.Message activityMsg = mActivityHandler.obtainMessage(); 69 activityMsg.what = msg.what; 70 if (mAcceptThread != null && mAcceptThread.isAlive()) { 71 activityMsg.obj = "等待连接..."; 72 activityMsg.arg1 = BT_STAT_WAIT; 73 } else if (mCommThread != null && mCommThread.isAlive()) { 74 activityMsg.obj = mCommThread.getRemoteName() + "[在线]"; 75 activityMsg.arg1 = BT_STAT_ONLINE; 76 } else if (mConnectThread != null && mConnectThread.isAlive()) { 77 SoundEffect.getInstance(TaskService.this).play(3); 78 activityMsg.obj = "正在连接:" 79 + mConnectThread.getDevice().getName(); 80 activityMsg.arg1 = BT_STAT_CONN; 81 } else { 82 activityMsg.obj = "未知状态"; 83 activityMsg.arg1 = BT_STAT_UNKNOWN; 84 SoundEffect.getInstance(TaskService.this).play(2); 85 // 重新等待连接 86 mAcceptThread = new AcceptThread(); 87 mAcceptThread.start(); 88 isServerMode = true; 89 } 90 91 mActivityHandler.sendMessage(activityMsg); 92 break; 93 default: 94 break; 95 } 96 super.handleMessage(msg); 97 } 98 }; 99 100 101 102 @Override103 public void onCreate() {104 super.onCreate();105 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();106 if (mBluetoothAdapter == null) {107 Log.e(TAG, "Your device is not support Bluetooth!");108 return;109 }110 mThread = new TaskThread();111 mThread.start();112 }113 114 115 public static void start(Context c, Handler handler){116 mActivityHandler = handler;117 Intent intent = new Intent(c, TaskService.class);118 c.startService(intent);119 }120 121 public static void stop(Context c){122 Intent intent = new Intent(c, TaskService.class);123 c.stopService(intent);124 }125 126 127 128 public static void newTask(Task target) {129 synchronized (mTaskList) {130 mTaskList.add(target);131 }132 }133 134 private class TaskThread extends Thread {135 private boolean isRun = true;136 private int mCount = 0;137 138 public void cancel() {139 isRun = false;140 }141 142 @Override143 public void run() {144 Task task;145 while (isRun) {146 147 // 有任务148 if (mTaskList.size() > 0) {149 synchronized (mTaskList) {150 // 获得任务151 task = mTaskList.get(0);152 doTask(task);153 }154 } else {155 try {156 Thread.sleep(200);157 mCount++;158 } catch (InterruptedException e) {159 }160 // 每过10秒钟进行一次状态检查161 if (mCount >= 50) {162 mCount = 0;163 // 检查远程设备状态164 android.os.Message handlerMsg = mServiceHandler165 .obtainMessage();166 handlerMsg.what = Task.TASK_GET_REMOTE_STATE;167 mServiceHandler.sendMessage(handlerMsg);168 }169 }170 }171 }172 173 }174 //对应三个线程,其中mCommThread是在mConnectThread的run()方法中new出来的175 private void doTask(Task task) {176 switch (task.getTaskID()) {177 case Task.TASK_START_ACCEPT:178 mAcceptThread = new AcceptThread();179 mAcceptThread.start();180 isServerMode = true;181 break;182 case Task.TASK_START_CONN_THREAD:183 if (task.mParams == null || task.mParams.length == 0) {184 break;185 }186 BluetoothDevice remote = (BluetoothDevice) task.mParams[0];187 mConnectThread = new ConnectThread(remote);188 mConnectThread.start();189 isServerMode = false;190 break;191 case Task.TASK_SEND_MSG:192 boolean sucess = false;193 if (mCommThread == null || !mCommThread.isAlive()194 || task.mParams == null || task.mParams.length == 0) {195 Log.e(TAG, "mCommThread or task.mParams null");196 }else{197 byte[] msg = null;198 try {199 200 msg = DataProtocol.packMsg((String) task.mParams[0]);201 sucess = mCommThread.write(msg);202 203 } catch (UnsupportedEncodingException e) {204 sucess = false;205 }206 }207 if (!sucess) {208 android.os.Message returnMsg = mActivityHandler.obtainMessage();209 returnMsg.what = Task.TASK_SEND_MSG_FAIL;210 returnMsg.obj = "消息发送失败";211 mActivityHandler.sendMessage(returnMsg);212 }213 break;214 }215 216 // 移除任务217 mTaskList.remove(task);//每次保证任务列表里面只有一个任务,task = mTaskList.get(0);218 }219 220 @Override221 public void onDestroy() {222 super.onDestroy();223 mThread.cancel();224 }225 226 private final String UUID_STR = "00001101-0000-1000-8000-00805F9B34FB";227 228 /**229 * 等待客户端连接线程230 * 231 * @author Administrator232 */233 private class AcceptThread extends Thread {234 private final BluetoothServerSocket mmServerSocket;235 private boolean isCancel = false;236 237 public AcceptThread() {238 Log.d(TAG, "AcceptThread");239 BluetoothServerSocket tmp = null;240 try {241 tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(242 "MT_Chat_Room", UUID.fromString(UUID_STR));243 } catch (IOException e) {244 }245 mmServerSocket = tmp;246 }247 248 public void run() {249 BluetoothSocket socket = null;250 while (true) {251 try {252 // 阻塞等待253 socket = mmServerSocket.accept();254 } catch (IOException e) {255 if (!isCancel) {256 try {257 mmServerSocket.close();258 } catch (IOException e1) {259 }260 mAcceptThread = new AcceptThread();261 mAcceptThread.start();262 isServerMode = true;263 }264 break;265 }266 if (socket != null) {267 manageConnectedSocket(socket);268 try {269 mmServerSocket.close();270 } catch (IOException e) {271 }272 mAcceptThread = null;273 break;274 }275 }276 }277 278 public void cancel() {279 try {280 Log.d(TAG, "AcceptThread canceled");281 isCancel = true;282 isServerMode = false;283 mmServerSocket.close();284 mAcceptThread = null;285 if (mCommThread != null && mCommThread.isAlive()) {286 mCommThread.cancel();287 }288 } catch (IOException e) {289 }290 }291 }292 293 /**294 * 作为客户端连接指定的蓝牙设备线程295 * 296 * @author Administrator297 */298 private class ConnectThread extends Thread {299 private final BluetoothSocket mmSocket;300 private final BluetoothDevice mmDevice;301 302 public ConnectThread(BluetoothDevice device) {303 304 Log.d(TAG, "ConnectThread");305 306 if (mAcceptThread != null && mAcceptThread.isAlive()) {307 mAcceptThread.cancel();308 }309 310 if (mCommThread != null && mCommThread.isAlive()) {311 mCommThread.cancel();312 }313 314 // Use a temporary object that is later assigned to mmSocket,315 // because mmSocket is final316 BluetoothSocket tmp = null;317 mmDevice = device;318 try {319 tmp = device.createRfcommSocketToServiceRecord(UUID320 .fromString(UUID_STR));321 } catch (IOException e) {322 Log.d(TAG, "createRfcommSocketToServiceRecord error!");323 }324 325 mmSocket = tmp;326 }327 328 public BluetoothDevice getDevice() {329 return mmDevice;330 }331 332 public void run() {333 // Cancel discovery because it will slow down the connection334 mBluetoothAdapter.cancelDiscovery();335 try {336 // Connect the device through the socket. This will block337 // until it succeeds or throws an exception338 mmSocket.connect();339 } catch (IOException connectException) {340 // Unable to connect; close the socket and get out341 Log.e(TAG, "Connect server failed");342 try {343 mmSocket.close();344 } catch (IOException closeException) {345 }346 mAcceptThread = new AcceptThread();347 mAcceptThread.start();348 isServerMode = true;349 return;350 } // Do work to manage the connection (in a separate thread)351 manageConnectedSocket(mmSocket);352 }353 354 public void cancel() {355 try {356 mmSocket.close();357 } catch (IOException e) {358 }359 mConnectThread = null;360 }361 }362 363 private void manageConnectedSocket(BluetoothSocket socket) {364 // 启动子线程来维持连接365 mCommThread = new ConnectedThread(socket);366 mCommThread.start();367 }368 369 private class ConnectedThread extends Thread {370 private final BluetoothSocket mmSocket;371 private final InputStream mmInStream;372 private final OutputStream mmOutStream;373 private BufferedOutputStream mmBos;374 private byte[] buffer;375 376 public ConnectedThread(BluetoothSocket socket) {377 Log.d(TAG, "ConnectedThread");378 mmSocket = socket;379 InputStream tmpIn = null;380 OutputStream tmpOut = null;381 try {382 tmpIn = socket.getInputStream();383 tmpOut = socket.getOutputStream();384 } catch (IOException e) {385 }386 mmInStream = tmpIn;387 mmOutStream = tmpOut;388 mmBos = new BufferedOutputStream(mmOutStream);389 }390 391 public OutputStream getOutputStream() {392 return mmOutStream;393 }394 395 public boolean write(byte[] msg) {396 if (msg == null)397 return false;398 try {399 mmBos.write(msg);400 mmBos.flush();401 402 mActivityHandler.obtainMessage(Task.TASK_SEND_MSG, -1, -1, new String(msg)).sendToTarget();403 System.out.println("Write:" + msg);404 } catch (IOException e) {405 return false;406 }407 return true;408 }409 410 public String getRemoteName() {411 return mmSocket.getRemoteDevice().getName();412 }413 414 415 416 417 public void cancel() {418 try {419 mmSocket.close();420 } catch (IOException e) {421 }422 mCommThread = null;423 }424 425 public void run() {426 try {427 write(DataProtocol.packMsg(mBluetoothAdapter.getName()428 + "已经上线\n"));//获取本地蓝牙适配器的蓝牙名称,这一条消息默认发送出去啦,429 //但消息记录里面不应该有这条消息,消息记录里面记录按过发送键的消息430 } catch (UnsupportedEncodingException e2) {431 }432 int size;433 Message msg;434 android.os.Message handlerMsg;435 buffer = new byte[1024];436 437 BufferedInputStream bis = new BufferedInputStream(mmInStream);438 // BufferedReader br = new BufferedReader(new439 // InputStreamReader(mmInStream));440 HashMap<String, Object> data;441 while (true) {442 try {443 size = bis.read(buffer);444 msg = DataProtocol.unpackData(buffer);445 if (msg == null)446 continue;447 448 if (mActivityHandler == null) {449 return;450 }451 452 msg.remoteDevName = mmSocket.getRemoteDevice().getName();//得到对方设备的名字453 if (msg.type == DataProtocol.TYPE_FILE) {454 // 文件接收处理忽略455 456 } else if (msg.type == DataProtocol.TYPE_MSG) {457 data = new HashMap<String, Object>();458 System.out.println("Read data.");459 data.put(ChatListViewAdapter.KEY_ROLE,460 ChatListViewAdapter.ROLE_TARGET);461 data.put(ChatListViewAdapter.KEY_NAME,462 msg.remoteDevName);463 data.put(ChatListViewAdapter.KEY_TEXT, msg.msg);464 465 // 通过Activity更新到UI上466 handlerMsg = mActivityHandler.obtainMessage();467 handlerMsg.what = Task.TASK_RECV_MSG;468 handlerMsg.obj = data;469 mActivityHandler.sendMessage(handlerMsg);470 }471 } catch (IOException e) {472 try {473 mmSocket.close();474 } catch (IOException e1) {475 }476 mCommThread = null;477 if (isServerMode) {478 // 检查远程设备状态479 handlerMsg = mServiceHandler.obtainMessage();480 handlerMsg.what = Task.TASK_GET_REMOTE_STATE;481 mServiceHandler.sendMessage(handlerMsg);482 SoundEffect.getInstance(TaskService.this).play(2);483 mAcceptThread = new AcceptThread();484 mAcceptThread.start();485 }486 break;487 }488 }489 }490 }491 492 // ================================================================493 494 @Override495 public IBinder onBind(Intent intent) {496 // TODO Auto-generated method stub497 return null;498 }499 500 }
实现效果:
- 蓝牙开发
- 蓝牙开发
- 蓝牙开发
- 蓝牙开发
- 蓝牙开发
- 蓝牙开发
- 蓝牙----Android的蓝牙开发
- ios 蓝牙开发,蓝牙应用
- Android 蓝牙开发-蓝牙通信
- Android蓝牙开发【三】蓝牙Hid开发
- OBEX和蓝牙开发
- CSR蓝牙开发手记
- 蓝牙开发工具
- 手机蓝牙开发
- WINCE蓝牙开发
- J2me蓝牙打印开发
- J2me蓝牙打印开发
- J2ME蓝牙开发心得
- CentOS 7.2 安装MariaDB
- javascript:history.go()和History.back()的区别
- 教你微信对接图灵机器人
- 【JZOJ4877】【NOIP2016提高A组集训第10场11.8】力场护盾
- Less的学习&资料整理
- 蓝牙开发
- 关于深度学习的数据集
- NOIp模拟 俄罗斯方块
- Tinyxml 创建xml 并以string形式进行解析 及中文处理
- Python Web开发用到的知识
- 包含对象为Integer的List列表转为Integer数组并排序的简单方法
- 使用python对中文文档进行词频统计
- 做自动化测试的时候如何应对验证码问题
- req.body is undefined