《安卓网络编程》之第六篇 Android中的WIFI和蓝牙
关于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来获取。先注册再获取信息,然后处理
//注册,当一个设备被发现时调用onReceive
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter); //当搜索结束后调用onReceive
filter = 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极为相似。
这个线程在准备连接之前启动
//UUID可以看做一个端口号
private static final UUID MY_UUID =
UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
//像一个服务器一样时刻监听是否有连接建立
private class AcceptThread extends Thread{
private BluetoothServerSocket serverSocket; public AcceptThread(boolean secure){
BluetoothServerSocket temp = null;
try {
temp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
NAME_INSECURE, MY_UUID);
} catch (IOException e) {
Log.e("app", "listen() failed", e);
}
serverSocket = temp;
} public void run(){
BluetoothSocket socket=null;
while(true){
try {
socket = serverSocket.accept();
} catch (IOException e) {
Log.e("app", "accept() failed", e);
break;
}
}
if(socket!=null){
//此时可以新建一个数据交换线程,把此socket传进去
}
} //取消监听
public void cancel(){
try {
serverSocket.close();
} catch (IOException e) {
Log.e("app", "Socket Type" + socketType + "close() of server failed", e);
}
} }
5.交换数据
搜索到设备后可以获取设备的地址,通过此地址获取一个BluetoothDeviced对象,可以看做客户端,通过此对象device.createRfcommSocketToServiceRecord(MY_UUID);同一个UUID可与服务器建立连接获取另一个socket对象,由此服务端与客户端各有一个socket对象,此时
他们可以互相交换数据了。
创立客户端socket可建立线程
//另一个设备去连接本机,相当于客户端
private class ConnectThread extends Thread{
private BluetoothSocket socket;
private BluetoothDevice device;
public ConnectThread(BluetoothDevice device,boolean secure){
this.device = device;
BluetoothSocket tmp = null;
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
} catch (IOException e) {
Log.e("app", "create() failed", e);
}
} public void run(){
mBluetoothAdapter.cancelDiscovery(); //取消设备查找
try {
socket.connect();
} catch (IOException e) {
try {
socket.close();
} catch (IOException e1) {
Log.e("app", "unable to close() "+
" socket during connection failure", e1);
}
connetionFailed(); //连接失败
return;
}
//此时可以新建一个数据交换线程,把此socket传进去
} public void cancel() {
try {
socket.close();
} catch (IOException e) {
Log.e("app", "close() of connect socket failed", e);
}
}
}
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)
源码分析:
Utils.java
package cn.com.farsgiht.bluetoothdemo.utils; import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import cn.com.farsgiht.bluetoothdemo.R; public class Utils {
public static final int NOTIFY_ID1 = 1001; public static void notifyMessage(Context context, String msg, Activity activity){
//Notification builder;
PendingIntent contentIntent = null;
NotificationManager nm;
// 发送通知需要用到NotificationManager对象
nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
// 消息对象
Intent notificationIntent = new Intent(context, activity.getClass());
// PendingIntent.getActivity(Context context, int requestCode, Intent intent, int flags)
// 用来获得一个挂起的PendingIntent,让该Intent去启动新的Activity来处理通知
contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0); //定义通知栏展现的内容信息
int icon = R.drawable.icon;
long when = System.currentTimeMillis();
Notification notification = new Notification(icon, msg, when);
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.defaults |= Notification.DEFAULT_SOUND; // 调用系统自带声音
notification.flags |= Notification.FLAG_AUTO_CANCEL; // 点击清除按钮或点击通知后会自动消失
notification.defaults |= Notification.DEFAULT_LIGHTS;
notification.vibrate = new long[]{300, 500};
notification.setLatestEventInfo(context, "BluetoothChat", msg, contentIntent);
/* // 定制我们要在状态栏显示的通知样式
builder = new Notification(context);
builder.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.ic_launcher)//设置状态栏里面的图标(小图标) .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.i5))//下拉下拉列表里面的图标(大图标) .setTicker("this is bitch!") //设置状态栏的显示的信息
.setWhen(System.currentTimeMillis())//设置时间发生时间
.setAutoCancel(true)//设置可以清除
.setContentTitle("This is ContentTitle")//设置下拉列表里的标题
.setContentText("this is ContentText");//设置上下文内容
*/
// 获得刚才创建的通知对象
// Notification notification = builder.getNotification();//获取一个Notification // 通过NotificationManger来发送通知消息
// 参数1通知的ID,参数2发送哪个通知
nm.notify(NOTIFY_ID1, notification);
}
}
通知栏相关代码
2. PageIndicatorView.java
package cn.com.farsgiht.bluetoothdemo.UI; import cn.com.farsgiht.bluetoothdemo.R;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import android.widget.LinearLayout; public class PageIndicatorView extends ImageView {
private final String TAG = "PageIndicatorView";
private LinearLayout mPageIndicLayout; public PageIndicatorView(Context context) {
super(context);
setSelectedView(false);
} public void setSelectedView(boolean selected){
Bitmap bitmap;
if(selected){
bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.page_select);
}else{
bitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.page_item);
}
this.setImageBitmap(bitmap);
}
}
表情页页面指示器
3. DrawerHScrollView.java
package cn.com.farsgiht.bluetoothdemo.UI; import java.util.Hashtable; import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout; public class DrawerHScrollView extends HorizontalScrollView {
private static final String TAG = "DrawerHScrollView"; private int currentPage = 0;
private int totalPages = 1;
private static Hashtable<Integer, Integer> positionLeftTopOfPages = new Hashtable();
private LinearLayout mPageIndicLayout;
private Context mContext; public DrawerHScrollView(Context context) {
super(context);
this.mContext = context;
} public DrawerHScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
} public DrawerHScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.mContext = context;
} public void cleanup(){
currentPage = 0;
totalPages = 1;
if(positionLeftTopOfPages != null){
positionLeftTopOfPages.clear();
}
} public void setParameters(int totalPages, int currentPage, int scrollDisX, int space) {
Log.d(TAG, "~~~~~setParameters totalPages:"+totalPages +",currentPage:"+ currentPage +",scrollDisX:"+scrollDisX);
this.totalPages = totalPages;
this.currentPage = currentPage;
positionLeftTopOfPages.clear();
for (int i = 0;i < totalPages;i++){
int posx = (scrollDisX) * i - space;
positionLeftTopOfPages.put(i, posx);
Log.d(TAG, "~~~~~setParameters i:"+i +",posx:"+posx);
}
smoothScrollTo(0, 0);
setPageIndicLayout();
if(mPageIndicLayout != null){
updateDrawerPageLayout(totalPages, currentPage);
}
} public void setPageIndicLayout(){
// 添加表情多页图标布局
mPageIndicLayout = new LinearLayout(mContext);
mPageIndicLayout.setGravity(Gravity.CENTER);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
mPageIndicLayout.setLayoutParams(params);
ViewParent parent = this.getParent();
if(parent instanceof LinearLayout){
LinearLayout layout = (LinearLayout)parent;
layout.addView(mPageIndicLayout, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
}
} @Override
public void fling(int velocityX) {
Log.v(TAG, "-->fling velocityX:"+velocityX);
boolean change_flag = false;
if (velocityX > 0 && (currentPage < totalPages - 1)){
currentPage++;
change_flag = true;
} else if (velocityX < 0 && (currentPage > 0)){
currentPage--;
change_flag = true;
}
if (change_flag){
int postionTo = (Integer)positionLeftTopOfPages.get(new Integer(currentPage)).intValue();
Log.v(TAG, "------smoothScrollTo posx:"+postionTo);
smoothScrollTo(postionTo, 0);
updateDrawerPageLayout(totalPages, currentPage);
}
//super.fling(velocityX);
} public void updateDrawerPageLayout(int total_pages, int sel_page) {
Log.e(TAG, "~~~updateBooksPageLayout total_pages:" + total_pages
+ ",sel_page:" + sel_page);
mPageIndicLayout.removeAllViews();
if (total_pages <= 0 || sel_page < 0 || sel_page >= total_pages) {
Log.e(TAG, "total_pages or sel_page is outofrange.");
return;
}
for (int i = 0; i < total_pages; i++) {
if (i != 0) {
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(5, 0, 0, 0);
mPageIndicLayout.addView(new PageIndicatorView(mContext), params);
} else {
mPageIndicLayout.addView(new PageIndicatorView(mContext));
}
}
PageIndicatorView selItem = (PageIndicatorView) mPageIndicLayout
.getChildAt(sel_page);
selItem.setSelectedView(true);
}
}
定制的表情页面,继承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:
package cn.com.farsgiht.bluetoothdemo; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import cn.com.farsgiht.bluetoothdemo.UI.ChatListViewAdapter;
import cn.com.farsgiht.bluetoothdemo.UI.DrawerHScrollView;
import cn.com.farsgiht.bluetoothdemo.sound.SoundEffect;
import cn.com.farsgiht.bluetoothdemo.task.Task;
import cn.com.farsgiht.bluetoothdemo.task.TaskService;
import cn.com.farsgiht.bluetoothdemo.utils.Utils; public class ChatActivity extends Activity implements View.OnClickListener{
private final String TAG = "ChatActivity";
public static int sAliveCount = 0;
public static final String EXTRA_MESSAGER = "cn.com.farsgiht.bluetoothdemo.BUNDLE";
public static final String DEVICE_NAME = "device_name";
// 蓝牙状态变量
private static int sBTState = -1; private final int REQUES_BT_ENABLE_CODE = 123;
private final int REQUES_SELECT_BT_CODE = 222; private ListView mList;
private EditText mInput;
private Button mSendBtn;
private ImageView mEmoButton;
private GridView mGridView;
private boolean isUpdate = false;
private BluetoothDevice mRemoteDevice; private LinearLayout mRootLayout, mChatLayout; private View mEmoView;
private boolean isShowEmo = false;
private boolean isHaspressed = false;
private int mScrollHeight; private DrawerHScrollView mScrollView;
private ChatListViewAdapter mAdapter2;
private ArrayList<HashMap<String, Object>> mChatContent2 = new ArrayList<HashMap<String, Object>>();
private BluetoothAdapter mBluetoothAdapter; private ArrayList<HashMap<String, Object>> mEmoList = new ArrayList<HashMap<String, Object>>();
// 已连接设备的名字
private String mConnectedDeviceName = null; private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
//设置聊天信息的时间
SimpleDateFormat df0 = new SimpleDateFormat("MM-dd HH:mm:ss");
String pdate=df0.format(System.currentTimeMillis()).toString();
switch(msg.what){
case -1:
showToast("没有连接其它用户,点击\"Menu\"扫描并选择周国用户");
SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_ERR);
break;
case Task.TASK_SEND_MSG:
// showToast(msg.obj.toString());
String writeMessage = msg.obj.toString();
if(writeMessage!=null && isHaspressed)
{
//将发送的信息插入到数据库
ContentValues values=new ContentValues();
values.put("name", "我");
values.put("pdate",pdate);
values.put("informations", writeMessage);
//创建数据库
DatabaseHelper insertdbHelper=new DatabaseHelper(ChatActivity.this,"zhsf_db");
SQLiteDatabase insertdb=insertdbHelper.getWritableDatabase();
insertdb.insert("info", null, values);
}
if(sAliveCount <= 0){
Utils.notifyMessage(ChatActivity.this, msg.obj.toString(), ChatActivity.this);
}
break;
case Task.TASK_RECV_MSG:
String readMessage =((HashMap<String,Object>) msg.obj).get(ChatListViewAdapter.KEY_TEXT).toString();
mConnectedDeviceName = ((HashMap<String,Object>) msg.obj).get(ChatListViewAdapter.KEY_NAME).toString();
if(readMessage!=null)
{
//将接受的信息插入到数据库
ContentValues values2=new ContentValues();
values2.put("name", mConnectedDeviceName);
values2.put("pdate",pdate);
values2.put("informations", readMessage);
DatabaseHelper insertdbHelper2=new DatabaseHelper(ChatActivity.this,"zhsf_db");
SQLiteDatabase insertdb2=insertdbHelper2.getWritableDatabase();
insertdb2.insert("info", null, values2);
} if(msg.obj == null)
return;
if(msg.obj instanceof HashMap<?, ?>){
showTargetMessage((HashMap<String, Object>) msg.obj);
}
if(sAliveCount <= 0){
Utils.notifyMessage(ChatActivity.this, "您有未读取消息", ChatActivity.this);
}
break;
case Task.TASK_GET_REMOTE_STATE:
setTitle((String)msg.obj);
if(sAliveCount <= 0){
if(isBTStateChanged(msg.arg1) && msg.arg1 != TaskService.BT_STAT_WAIT)
Utils.notifyMessage(ChatActivity.this, (String)msg.obj, ChatActivity.this);
}
break; }
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat); // 获得蓝牙管理器
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Your device is not support Bluetooth!");
Toast.makeText(this, "该设备没有蓝牙设备", Toast.LENGTH_LONG).show();
return;
} mRootLayout = (LinearLayout) findViewById(R.id.root);
mChatLayout = (LinearLayout) findViewById(R.id.topPanel);
mList = (ListView) findViewById(R.id.listView1); mAdapter2 = new ChatListViewAdapter(this, mChatContent2); mList.setAdapter(mAdapter2); // 初始化表情
mEmoView = initEmoView(); mInput = (EditText) findViewById(R.id.inputEdit);
mInput.setOnClickListener(new android.view.View.OnClickListener() {
@Override
public void onClick(View v) {
// 点击输入框后,隐藏表情,显示输入法
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mInput, 0);
showEmoPanel(false);
}
}); mSendBtn = (Button) findViewById(R.id.sendBtn);
mEmoButton = (ImageView) findViewById(R.id.emotionBtn); mSendBtn.setOnClickListener(this);
mEmoButton.setOnClickListener(this); //---------------------------------------------------------------------
// 打开蓝牙设备
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUES_BT_ENABLE_CODE);
}else{
// 默认设备作为服务端
startServiceAsServer();
}
//---------------------------------------------------------------------
} private View initEmoView(){
if(mEmoView == null){
LayoutInflater inflater = getLayoutInflater();
mEmoView = inflater.inflate(R.layout.emo_layout, null); mScrollView = (DrawerHScrollView) mEmoView.findViewById(R.id.scrollView);
mGridView = (GridView) mEmoView.findViewById(R.id.gridView);
mGridView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
// 在android中要显示图片信息,必须使用Bitmap位图的对象来装载
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), (Integer) mEmoList.get(position).get("img"));
ImageSpan imageSpan = new ImageSpan(ChatActivity.this, bitmap);
SpannableString spannableString = new SpannableString((String) mEmoList.get(position).get("text"));//face就是图片的前缀名
spannableString.setSpan(imageSpan, 0, 8,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mInput.append(spannableString);
System.out.println("mInput:"+mInput.getText());
}
}); mScrollHeight = setScrollGridView(mScrollView, mGridView, 3);
System.out.println("mScrollHeight:" + mScrollHeight);
}
return mEmoView;
} private void startServiceAsServer(){
TaskService.start(this, mHandler);
TaskService.newTask(new Task(mHandler, Task.TASK_START_ACCEPT, null));
SoundEffect.getInstance(this).play(SoundEffect.SOUND_PLAY);
} @Override
protected void onResume() {
sAliveCount++;
super.onResume();
} @Override
protected void onPause() {
sAliveCount--;
super.onPause();
} @Override
protected void onDestroy() {
super.onDestroy();
// 关闭蓝牙
if(mBluetoothAdapter.isEnabled())
mBluetoothAdapter.disable();
// 停止服务
TaskService.stop(this);
} @Override
public void onClick(View v) {
if(v == mSendBtn){
String msg = mInput.getText().toString().trim();
TaskService.newTask(new Task(mHandler, Task.TASK_GET_REMOTE_STATE, null));//通过点击按钮触发相应线程的启动,比较巧妙,值得学习
if(msg.length() == 0){
showToast("聊天内容为空");
SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_ERR);
return;
} //------ DEUBG ------
TaskService.newTask(new Task(mHandler, Task.TASK_SEND_MSG, new Object[]{msg}));
showOwnMessage(msg);//立马显示自己发送的消息,所以在handler里面就没有再做处理
isHaspressed = true;//数据库可以开始记录消息啦
mInput.setText("");
}else if(v == mEmoButton){
System.out.println("Emo btn clicked");
// 关闭输入法
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mInput.getWindowToken(),0);
if(isShowEmo){
showEmoPanel(false);
}else{
showEmoPanel(true);
}
}
} private void showEmoPanel(boolean show){
if(show && !isShowEmo){
mEmoView.setVisibility(View.VISIBLE);
mEmoButton.setImageResource(R.drawable.emo_collapse);
ViewGroup.LayoutParams params = mChatLayout.getLayoutParams();
params.height = mChatLayout.getHeight() - mScrollHeight;
mChatLayout.setLayoutParams(params);
isShowEmo = true;
}else if(!show && isShowEmo){
mEmoView.setVisibility(View.GONE);
mEmoButton.setImageResource(R.drawable.emo_bkg);
ViewGroup.LayoutParams params = mChatLayout.getLayoutParams();
params.height = mChatLayout.getHeight() + mScrollHeight;
mChatLayout.setLayoutParams(params);
isShowEmo = false;
}
if(!isUpdate && show){
LinearLayout.LayoutParams para = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
mRootLayout.addView(mEmoView, para);
isUpdate = true;
}
} private boolean isBTStateChanged(int now){
if(sBTState != now){
sBTState = now;
return true;
}else{
return false;
}
} /**
* 显示对方信息
* @param data
*/
private void showTargetMessage(HashMap<String, Object> data){
SimpleDateFormat df1 = new SimpleDateFormat("E MM月dd日 HH:mm ");
data.put(ChatListViewAdapter.KEY_DATE, df1.format(System.currentTimeMillis()).toString());
data.put(ChatListViewAdapter.KEY_SHOW_MSG, true);
mChatContent2.add(data);
mAdapter2.notifyDataSetChanged();
SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_RECV);
} /**
* 显示自己信息
* @param data
*/
private void showOwnMessage(String msg){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put(ChatListViewAdapter.KEY_ROLE, ChatListViewAdapter.ROLE_OWN);//哪个角色的消息
map.put(ChatListViewAdapter.KEY_NAME, mBluetoothAdapter.getName());
map.put(ChatListViewAdapter.KEY_TEXT, msg);
SimpleDateFormat df2 = new SimpleDateFormat("E MM月dd日 HH:mm ");
map.put(ChatListViewAdapter.KEY_DATE, df2.format(System.currentTimeMillis()).toString());
map.put(ChatListViewAdapter.KEY_SHOW_MSG, true);
mChatContent2.add(map);
mAdapter2.notifyDataSetChanged();
SoundEffect.getInstance(ChatActivity.this).play(SoundEffect.SOUND_SEND);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_menu, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.scan:
startActivityForResult(new Intent(this, SelectDevice.class), REQUES_SELECT_BT_CODE);
break;
case R.id.discoverable:
// 调用设置用户名方法
AlertDialog.Builder dlg = new AlertDialog.Builder(this);
final EditText devNameEdit = new EditText(this);
dlg.setView(devNameEdit);
dlg.setTitle("请输入用户名");
dlg.setPositiveButton("设置", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if(devNameEdit.getText().toString().length() != 0)
mBluetoothAdapter.setName(devNameEdit.getText().toString());
}
});
dlg.create();
dlg.show();
return true;
case R.id.record:
Intent recordIntent = new Intent(ChatActivity.this, RecordListActivity.class);
startActivity(recordIntent);
return true;
case R.id.exit:
Intent aboutIntent = new Intent(ChatActivity.this, DownloadActivity.class);
startActivity(aboutIntent);
return true;
}
return false;
} @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == REQUES_BT_ENABLE_CODE && resultCode == RESULT_OK){
startServiceAsServer();
}else if(requestCode == REQUES_SELECT_BT_CODE && resultCode == RESULT_OK){
mRemoteDevice = data.getParcelableExtra("DEVICE");
if(mRemoteDevice == null)
return;
TaskService.newTask(new Task(mHandler, Task.TASK_START_CONN_THREAD, new Object[]{mRemoteDevice}));
}
super.onActivityResult(requestCode, resultCode, data);
} private void showToast(String msg){
Toast tst = Toast.makeText(this, msg, Toast.LENGTH_SHORT);
tst.setGravity(Gravity.CENTER | Gravity.TOP, 0, 240);
tst.show();
} // 设置表情的多页滚动显示控件
public int setScrollGridView(DrawerHScrollView scrollView, GridView gridView,
int lines) { DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
Display display = getWindowManager().getDefaultDisplay();
System.out.println("Width:" + display.getWidth());
System.out.println("Height:" + display.getHeight()); int scrollWid = display.getWidth();
int scrollHei;
System.out.println("scrollWid:" + scrollWid);
if (scrollWid <= 0 ){
Log.d(TAG, "scrollWid or scrollHei is less than 0");
return 0;
} float density = dm.density; // 屏幕密度(像素比例:0.75/1.0/1.5/2.0) int readlViewWidht = 56;
// 图片都放在了Hdpi中,所以计算出图片的像素独立宽度
int viewWidth = (int) (readlViewWidht * density / 1.5);
int viewHeight = viewWidth;
System.out.println("viewWidth:" + viewWidth + " viewHeight:" + viewHeight); int numColsPage = scrollWid / viewWidth;
int spaceing = (scrollWid - viewWidth * numColsPage)/(numColsPage);
System.out.println("Space:" + spaceing); SimpleAdapter adapter = getEmoAdapter();
int pages = adapter.getCount() / (numColsPage * lines); if (pages * numColsPage * lines < adapter.getCount()){
pages++;
} System.out.println("pages:" + pages); scrollHei = lines * viewHeight + spaceing * (lines + 1); LayoutParams params = new LayoutParams(pages * scrollWid, LayoutParams.WRAP_CONTENT);
gridView.setLayoutParams(params);
gridView.setColumnWidth(viewWidth);
gridView.setHorizontalSpacing(spaceing);
gridView.setVerticalSpacing(spaceing);
gridView.setStretchMode(GridView.NO_STRETCH);
gridView.setNumColumns(numColsPage * pages); //adapter = new DrawerListAdapter(this, colWid, colHei);
//listener = new DrawerItemClickListener();
gridView.setAdapter(adapter);
//mGridView.setOnItemClickListener(listener); scrollView.setParameters(pages, 0, scrollWid, spaceing);
//updateDrawerPageLayout(pageNum, 0);
// 表情区域还要加上分布显示区
int pageNumHei = (int) (18 * density);
return scrollHei + pageNumHei;
} private SimpleAdapter getEmoAdapter(){
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("img", R.drawable.emo001);
map.put("text", "<emo001>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo002);
map.put("text", "<emo002>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo003);
map.put("text", "<emo003>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo004);
map.put("text", "<emo004>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo005);
map.put("text", "<emo005>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo006);
map.put("text", "<emo006>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo007);
map.put("text", "<emo007>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo008);
map.put("text", "<emo008>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo009);
map.put("text", "<emo009>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo010);
map.put("text", "<emo010>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo011);
map.put("text", "<emo011>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo012);
map.put("text", "<emo012>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo013);
map.put("text", "<emo013>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo014);
map.put("text", "<emo014>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo015);
map.put("text", "<emo015>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo016);
map.put("text", "<emo016>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo017);
map.put("text", "<emo017>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo018);
map.put("text", "<emo018>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo019);
map.put("text", "<emo019>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo020);
map.put("text", "<emo020>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo021);
map.put("text", "<emo021>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo022);
map.put("text", "<emo022>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo023);
map.put("text", "<emo023>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo024);
map.put("text", "<emo024>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo025);
map.put("text", "<emo025>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo026);
map.put("text", "<emo026>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo027);
map.put("text", "<emo027>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo028);
map.put("text", "<emo028>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo029);
map.put("text", "<emo029>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo030);
map.put("text", "<emo030>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo031);
map.put("text", "<emo031>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo032);
map.put("text", "<emo032>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo033);
map.put("text", "<emo033>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo034);
map.put("text", "<emo034>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo035);
map.put("text", "<emo035>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo036);
map.put("text", "<emo036>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo037);
map.put("text", "<emo037>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo038);
map.put("text", "<emo038>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo039);
map.put("text", "<emo039>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo040);
map.put("text", "<emo040>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo041);
map.put("text", "<emo041>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo042);
map.put("text", "<emo042>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo043);
map.put("text", "<emo043>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo044);
map.put("text", "<emo044>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo045);
map.put("text", "<emo045>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo046);
map.put("text", "<emo046>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo047);
map.put("text", "<emo047>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo048);
map.put("text", "<emo048>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo049);
map.put("text", "<emo049>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo050);
map.put("text", "<emo050>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo051);
map.put("text", "<emo051>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo052);
map.put("text", "<emo052>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo053);
map.put("text", "<emo053>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo054);
map.put("text", "<emo054>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo055);
map.put("text", "<emo055>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo056);
map.put("text", "<emo056>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo057);
map.put("text", "<emo057>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo058);
map.put("text", "<emo058>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo059);
map.put("text", "<emo059>");
mEmoList.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.emo060);
map.put("text", "<emo060>");
mEmoList.add(map); /**
* 上述添加表情效率高,但是代码太冗余,下面的方式代码简单,但是效率较低
*/
/*
HashMap<String, Integer> map;
for(int i = 0; i < 100; i++){
map = new HashMap<String, Integer>();
Field field=R.drawable.class.getDeclaredField("image"+i);
int resourceId=Integer.parseInt(field.get(null).toString());
map.put("img", resourceId);
mEmoList.add(map);
}
*/
return new SimpleAdapter(this, mEmoList, R.layout.grid_view_item,
new String[]{"img"}, new int[]{R.id.imageView});
} long waitTime = 2000;
long touchTime = 0;
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN && KeyEvent.KEYCODE_BACK == keyCode) {
long currentTime = System.currentTimeMillis();
if((currentTime-touchTime)>=waitTime) {
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
touchTime = currentTime;
}else {
finish();
}
return true;
}
return super.onKeyDown(keyCode, event);
} }
TaskService.java:
package cn.com.farsgiht.bluetoothdemo.task; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.UUID;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.Toast;
import cn.com.farsgiht.bluetoothdemo.ChatActivity;
import cn.com.farsgiht.bluetoothdemo.UI.ChatListViewAdapter;
import cn.com.farsgiht.bluetoothdemo.protocol.DataProtocol;
import cn.com.farsgiht.bluetoothdemo.protocol.Message;
import cn.com.farsgiht.bluetoothdemo.sound.SoundEffect; /**
* 任务处理服务
* @author Administrator
*/
public class TaskService extends Service {
public static final int BT_STAT_WAIT = 0;
public static final int BT_STAT_CONN = 1;
public static final int BT_STAT_ONLINE = 2;
public static final int BT_STAT_UNKNOWN = 3; public static final String DEVICE_NAME = "device_name"; private final String TAG = "TaskService";
private TaskThread mThread; private BluetoothAdapter mBluetoothAdapter; private AcceptThread mAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mCommThread; private boolean isServerMode = true; private static Handler mActivityHandler; // 任务队列
private static ArrayList<Task> mTaskList = new ArrayList<Task>(); private Handler mServiceHandler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case Task.TASK_GET_REMOTE_STATE:
android.os.Message activityMsg = mActivityHandler.obtainMessage();
activityMsg.what = msg.what;
if (mAcceptThread != null && mAcceptThread.isAlive()) {
activityMsg.obj = "等待连接...";
activityMsg.arg1 = BT_STAT_WAIT;
} else if (mCommThread != null && mCommThread.isAlive()) {
activityMsg.obj = mCommThread.getRemoteName() + "[在线]";
activityMsg.arg1 = BT_STAT_ONLINE;
} else if (mConnectThread != null && mConnectThread.isAlive()) {
SoundEffect.getInstance(TaskService.this).play(3);
activityMsg.obj = "正在连接:"
+ mConnectThread.getDevice().getName();
activityMsg.arg1 = BT_STAT_CONN;
} else {
activityMsg.obj = "未知状态";
activityMsg.arg1 = BT_STAT_UNKNOWN;
SoundEffect.getInstance(TaskService.this).play(2);
// 重新等待连接
mAcceptThread = new AcceptThread();
mAcceptThread.start();
isServerMode = true;
} mActivityHandler.sendMessage(activityMsg);
break;
default:
break;
}
super.handleMessage(msg);
}
}; @Override
public void onCreate() {
super.onCreate();
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Your device is not support Bluetooth!");
return;
}
mThread = new TaskThread();
mThread.start();
} public static void start(Context c, Handler handler){
mActivityHandler = handler;
Intent intent = new Intent(c, TaskService.class);
c.startService(intent);
} public static void stop(Context c){
Intent intent = new Intent(c, TaskService.class);
c.stopService(intent);
} public static void newTask(Task target) {
synchronized (mTaskList) {
mTaskList.add(target);
}
} private class TaskThread extends Thread {
private boolean isRun = true;
private int mCount = 0; public void cancel() {
isRun = false;
} @Override
public void run() {
Task task;
while (isRun) { // 有任务
if (mTaskList.size() > 0) {
synchronized (mTaskList) {
// 获得任务
task = mTaskList.get(0);
doTask(task);
}
} else {
try {
Thread.sleep(200);
mCount++;
} catch (InterruptedException e) {
}
// 每过10秒钟进行一次状态检查
if (mCount >= 50) {
mCount = 0;
// 检查远程设备状态
android.os.Message handlerMsg = mServiceHandler
.obtainMessage();
handlerMsg.what = Task.TASK_GET_REMOTE_STATE;
mServiceHandler.sendMessage(handlerMsg);
}
}
}
} }
//对应三个线程,其中mCommThread是在mConnectThread的run()方法中new出来的
private void doTask(Task task) {
switch (task.getTaskID()) {
case Task.TASK_START_ACCEPT:
mAcceptThread = new AcceptThread();
mAcceptThread.start();
isServerMode = true;
break;
case Task.TASK_START_CONN_THREAD:
if (task.mParams == null || task.mParams.length == 0) {
break;
}
BluetoothDevice remote = (BluetoothDevice) task.mParams[0];
mConnectThread = new ConnectThread(remote);
mConnectThread.start();
isServerMode = false;
break;
case Task.TASK_SEND_MSG:
boolean sucess = false;
if (mCommThread == null || !mCommThread.isAlive()
|| task.mParams == null || task.mParams.length == 0) {
Log.e(TAG, "mCommThread or task.mParams null");
}else{
byte[] msg = null;
try { msg = DataProtocol.packMsg((String) task.mParams[0]);
sucess = mCommThread.write(msg); } catch (UnsupportedEncodingException e) {
sucess = false;
}
}
if (!sucess) {
android.os.Message returnMsg = mActivityHandler.obtainMessage();
returnMsg.what = Task.TASK_SEND_MSG_FAIL;
returnMsg.obj = "消息发送失败";
mActivityHandler.sendMessage(returnMsg);
}
break;
} // 移除任务
mTaskList.remove(task);//每次保证任务列表里面只有一个任务,task = mTaskList.get(0);
} @Override
public void onDestroy() {
super.onDestroy();
mThread.cancel();
} private final String UUID_STR = "00001101-0000-1000-8000-00805F9B34FB"; /**
* 等待客户端连接线程
*
* @author Administrator
*/
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
private boolean isCancel = false; public AcceptThread() {
Log.d(TAG, "AcceptThread");
BluetoothServerSocket tmp = null;
try {
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
"MT_Chat_Room", UUID.fromString(UUID_STR));
} catch (IOException e) {
}
mmServerSocket = tmp;
} public void run() {
BluetoothSocket socket = null;
while (true) {
try {
// 阻塞等待
socket = mmServerSocket.accept();
} catch (IOException e) {
if (!isCancel) {
try {
mmServerSocket.close();
} catch (IOException e1) {
}
mAcceptThread = new AcceptThread();
mAcceptThread.start();
isServerMode = true;
}
break;
}
if (socket != null) {
manageConnectedSocket(socket);
try {
mmServerSocket.close();
} catch (IOException e) {
}
mAcceptThread = null;
break;
}
}
} public void cancel() {
try {
Log.d(TAG, "AcceptThread canceled");
isCancel = true;
isServerMode = false;
mmServerSocket.close();
mAcceptThread = null;
if (mCommThread != null && mCommThread.isAlive()) {
mCommThread.cancel();
}
} catch (IOException e) {
}
}
} /**
* 作为客户端连接指定的蓝牙设备线程
*
* @author Administrator
*/
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { Log.d(TAG, "ConnectThread"); if (mAcceptThread != null && mAcceptThread.isAlive()) {
mAcceptThread.cancel();
} if (mCommThread != null && mCommThread.isAlive()) {
mCommThread.cancel();
} // Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
try {
tmp = device.createRfcommSocketToServiceRecord(UUID
.fromString(UUID_STR));
} catch (IOException e) {
Log.d(TAG, "createRfcommSocketToServiceRecord error!");
} mmSocket = tmp;
} public BluetoothDevice getDevice() {
return mmDevice;
} public void run() {
// Cancel discovery because it will slow down the connection
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
Log.e(TAG, "Connect server failed");
try {
mmSocket.close();
} catch (IOException closeException) {
}
mAcceptThread = new AcceptThread();
mAcceptThread.start();
isServerMode = true;
return;
} // Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket);
} public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
mConnectThread = null;
}
} private void manageConnectedSocket(BluetoothSocket socket) {
// 启动子线程来维持连接
mCommThread = new ConnectedThread(socket);
mCommThread.start();
} private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private BufferedOutputStream mmBos;
private byte[] buffer; public ConnectedThread(BluetoothSocket socket) {
Log.d(TAG, "ConnectedThread");
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
mmBos = new BufferedOutputStream(mmOutStream);
} public OutputStream getOutputStream() {
return mmOutStream;
} public boolean write(byte[] msg) {
if (msg == null)
return false;
try {
mmBos.write(msg);
mmBos.flush(); mActivityHandler.obtainMessage(Task.TASK_SEND_MSG, -1, -1, new String(msg)).sendToTarget();
System.out.println("Write:" + msg);
} catch (IOException e) {
return false;
}
return true;
} public String getRemoteName() {
return mmSocket.getRemoteDevice().getName();
} public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
}
mCommThread = null;
} public void run() {
try {
write(DataProtocol.packMsg(mBluetoothAdapter.getName()
+ "已经上线\n"));//获取本地蓝牙适配器的蓝牙名称,这一条消息默认发送出去啦,
//但消息记录里面不应该有这条消息,消息记录里面记录按过发送键的消息
} catch (UnsupportedEncodingException e2) {
}
int size;
Message msg;
android.os.Message handlerMsg;
buffer = new byte[1024]; BufferedInputStream bis = new BufferedInputStream(mmInStream);
// BufferedReader br = new BufferedReader(new
// InputStreamReader(mmInStream));
HashMap<String, Object> data;
while (true) {
try {
size = bis.read(buffer);
msg = DataProtocol.unpackData(buffer);
if (msg == null)
continue; if (mActivityHandler == null) {
return;
} msg.remoteDevName = mmSocket.getRemoteDevice().getName();//得到对方设备的名字
if (msg.type == DataProtocol.TYPE_FILE) {
// 文件接收处理忽略 } else if (msg.type == DataProtocol.TYPE_MSG) {
data = new HashMap<String, Object>();
System.out.println("Read data.");
data.put(ChatListViewAdapter.KEY_ROLE,
ChatListViewAdapter.ROLE_TARGET);
data.put(ChatListViewAdapter.KEY_NAME,
msg.remoteDevName);
data.put(ChatListViewAdapter.KEY_TEXT, msg.msg); // 通过Activity更新到UI上
handlerMsg = mActivityHandler.obtainMessage();
handlerMsg.what = Task.TASK_RECV_MSG;
handlerMsg.obj = data;
mActivityHandler.sendMessage(handlerMsg);
}
} catch (IOException e) {
try {
mmSocket.close();
} catch (IOException e1) {
}
mCommThread = null;
if (isServerMode) {
// 检查远程设备状态
handlerMsg = mServiceHandler.obtainMessage();
handlerMsg.what = Task.TASK_GET_REMOTE_STATE;
mServiceHandler.sendMessage(handlerMsg);
SoundEffect.getInstance(TaskService.this).play(2);
mAcceptThread = new AcceptThread();
mAcceptThread.start();
}
break;
}
}
}
} // ================================================================ @Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
} }
实现效果:
《安卓网络编程》之第六篇 Android中的WIFI和蓝牙的更多相关文章
- 《安卓网络编程》之第一篇 java环境下模拟客户端、服务器端
1.Socket简介 在网络上的两个程序通过一个双向的通信连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket是TCP/IP协议的一个 ...
- 《安卓网络编程》之第二篇 java环境下网络通信的综合应用
经过前面内容的学习,我们了解了Java技术中实现网络通信的基本知识.下面将通过一个具体视力的实现过程,讲解客户端和服务器端通信的流程. 服务器端的实现文件是 Server.java,代码如下: imp ...
- 第六篇 ANDROID窗口系统机制之显示机制
第六篇 ANDROID窗口系统机制之显示机制 ANDROID的显示系统是整个框架中最复杂的系统之一,涉及包括窗口管理服务.VIEW视图系统.SurfaceFlinger本地服务.硬件加速等.窗口管理服 ...
- 四十六、android中的Bitmap
四十六.android中的Bitmap: http://www.cnblogs.com/linjiqin/archive/2011/12/28/2304940.html 四十七.实现调用Android ...
- Android 中的WiFi剖析
Android的WiFi 我们通常看到WiFi的守护进程wpa_supplicant在我们的ps的进程列表中,这个就是我们的wifi守护进程.wpa_supplicant在external/wpa_s ...
- Android中的WiFi P2P
Android中的WiFi P2P可以同意一定范围内的设备通过Wifi直接互连而不必通过热点或互联网. 使用WiFi P2P须要Android API Level >= 14才干够,并且不要忘记 ...
- 《安卓网络编程》之第三篇 使用Apache接口
在Android系统中,提供了一下三种通信接口: 标准的Java 接口:java.net Apache接口:org.apache.http Android网络接口:android.net.http 在 ...
- 安卓网络编程学习(1)——java原生网络编程(1)
写在前面 马上要进行第二轮冲刺,考虑到自己的APP在第一轮冲刺的效果不尽人意,有很多网络方面的小BUG,这里就系统学习一下网络编程,了解来龙去脉,以便更好的对项目进行优化处理. http协议 http ...
- [C# 网络编程系列]专题六:UDP编程
转自:http://www.cnblogs.com/zhili/archive/2012/09/01/2659167.html 引用: 前一个专题简单介绍了TCP编程的一些知识,UDP与TCP地位相当 ...
随机推荐
- [原]C#与非托管——封送和自动封送
之前说到了如何从C函数声明通过简单的查找替换生成一份C#的静态引用声明(C#与非托管——初体验),因为只是简单说明,所以全部采用的是基础类型匹配和自动封送.自动封送虽然能省去我们不少编码时间,但如果不 ...
- 接口测试培训:HTTP协议基础
接口测试培训:HTTP协议基础 引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展. ...
- lumen 中的 .env 配置文件简介和适用场景
lumen 是 laravel 的衍生品,核心功能的使用和 laravel 都是一致的,但配置文件这一方面,lumen 在 laravel 的基础上增加了更简便的配置方式: lumen 采用了 Dot ...
- MYSQL中 ENUM 类型的详细解释
ENUM 是一个字符串对象,其值通常选自一个允许值列表中,该列表在表创建时的列规格说明中被明确地列举. 在下列某些情况下,值也可以是空串("") 或 NULL: 如果将一个无效值插 ...
- List去除重复的元素
有两种方法,一种是去重不带顺序的,一种是去重带顺序的. /* * 方法1: 无顺序 * Hastset根据hashcode判断是否重复,数据不会重复 */ public static Lis ...
- 1136: 零起点学算法43——多组测试数据输出I
1136: 零起点学算法43--多组测试数据输出I Time Limit: 1 Sec Memory Limit: 128 MB 64bit IO Format: %lldSubmitted: ...
- MYSQL表记录字段换行符回车符处理
), ), ''); CHAR(10): 换行符 CHAR(13): 回车符
- lua 数据类型
lua 数据类型 8 种数据类型 类型 说明 nil 空类型 boolean 布尔类型 number 数值型, 浮点型 string 字符串 function 函数 userdata 用户自定义结构 ...
- 学习MVC之租房网站(五)-权限、角色、用户管理
在上一篇<学习MVC之租房网站(四)-实现Service层并进行单元测试>中,记录了实现Service层并进行单元测试的过程,接下来该到"正题"-MVC了,也就是UI层 ...
- java映射(map用法)
主要分两个接口:collection和Map 主要分三类:集合(set).列表(List).映射(Map)1.集合:没有重复对象,没有特定排序方式2.列表:对象按索引位置排序,可以有重复对象3.映射: ...