Android Bluetooth 蓝牙通信(二)

来源:互联网 发布:mac装win10多少钱 编辑:程序博客网 时间:2024/06/06 10:07

前面一篇文章实现了最简单的蓝牙通信,本篇文章对其进行优化。

首先由于项目需求,将蓝牙的搜索和连接做成Dialog形式,并且在搜索过程中加入弹窗。其次,将蓝牙连接和数据发送改成Service,便于整个项目里使用,不局限于某个activity里。然后,由于之前Server端的线程是在onCreat里开启的,所以Client端只有第一次能连接成功,断开之后就无法连接了,优化为加了一个Button,点击Button开启线程。最后之前run方法里的while(true)没有设置跳出循环的条件,影响性能,优化为当Client端断开连接之前,发送”close”消息,Server端收到即跳出循环。其他的一些小的优化直接看代码。

效果图:(两个GIF是分开录的,时间有点不同步,请见谅)

BlueToothServerBlueToothServer























Client端代码:

NewActivity(实际上就是MainActivity,我改了下名字)

public class NewActivity extends ActionBarActivity {    private List<String> lstDevices = new ArrayList<String>();    private ArrayAdapter<String> adtDevices;    private ToggleButton tbtnSwitch;    private Button btnSearch, btn_showDialog, btn_stopService, btn_send;    private EditText edit;    private BluetoothAdapter btAdapt;    private String address;    private ListView lvBTDevices;    private ProgressDialog pDialog = null;//“请稍等”Dialog    private boolean isFinish = false;//搜索结束标志    //蓝牙设置Dialog    private AlertDialog.Builder dialogBuilder;    private AlertDialog dialog;    private int serviceBindStatus = 2;//service绑定状态    private MyService blueToothService;//蓝牙服务    private ServiceConnection connection = new ServiceConnection() {//蓝牙服务连接        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            blueToothService = ((MyService.BlueToothBinder) service).getService();            blueToothService.connect(address, btAdapt);            if (blueToothService.getConnectionStatus() == 1) {                Toast.makeText(NewActivity.this, "connect succeeded", Toast.LENGTH_SHORT).show();            } else {                Toast.makeText(NewActivity.this, "connect fail", Toast.LENGTH_LONG).show();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main2);        edit = (EditText) findViewById(R.id.edit);        btn_showDialog = (Button) findViewById(R.id.btn_showDialog);        btn_showDialog.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                showDialog();//显示蓝牙设置Dialog            }        });        btn_stopService = (Button) findViewById(R.id.btn_stopService);        btn_stopService.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                //解绑只能在服务绑定之后才能进行,否则会引起程序崩溃,这里要进行判断                if (serviceBindStatus == 1) {                    unbindService(connection);                    serviceBindStatus = 2;                    Log.d("tag", "unbind");                    Toast.makeText(NewActivity.this, "Service stop", Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(NewActivity.this, "Service dose not start", Toast.LENGTH_SHORT).show();                }            }        });        btn_send = (Button) findViewById(R.id.btn_send);        btn_send.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                blueToothService.send(edit.getText().toString());                if (blueToothService.getSendStatus() == 1) {                    Toast.makeText(NewActivity.this, "send succeed", Toast.LENGTH_SHORT).show();                } else {                    Toast.makeText(NewActivity.this, "send fail", Toast.LENGTH_SHORT).show();                }            }        });    }    private void showDialog() {        dialogBuilder = new AlertDialog.Builder(NewActivity.this);        View dialogView = LayoutInflater.from(NewActivity.this)                .inflate(R.layout.activity_new, null);//蓝牙设置Dialog布局        dialogBuilder.setTitle("蓝牙设置");        dialogBuilder.setView(dialogView);        dialog = dialogBuilder.show();        //调整dialog大小        Window window = dialog.getWindow();        WindowManager.LayoutParams lp = window.getAttributes();        lp.gravity = Gravity.CENTER;        lp.width = 750;//宽高可设置具体大小        lp.height = 900;        dialog.getWindow().setAttributes(lp);        tbtnSwitch = (ToggleButton) dialogView.findViewById(R.id.tbtnSwitch);        btnSearch = (Button) dialogView.findViewById(R.id.btnSearch);        tbtnSwitch.setOnClickListener(new ClickEvent());        btnSearch.setOnClickListener(new ClickEvent());        // ListView及其数据源 适配器        lvBTDevices = (ListView) dialogView.findViewById(R.id.lvDevices);        adtDevices = new ArrayAdapter<String>(NewActivity.this,                android.R.layout.simple_list_item_1, lstDevices);        lvBTDevices.setAdapter(adtDevices);        lvBTDevices.setOnItemClickListener(new ItemClickEvent());//设置监听器获取数据        btAdapt = BluetoothAdapter.getDefaultAdapter();// 初始化本机蓝牙功能        if (btAdapt.getState() == BluetoothAdapter.STATE_OFF)// 读取蓝牙状态并显示于双状态按钮            tbtnSwitch.setChecked(false);        else if (btAdapt.getState() == BluetoothAdapter.STATE_ON)            tbtnSwitch.setChecked(true);        // 注册Receiver来获取蓝牙设备相关的结果,onReceive()里取得搜索所得的蓝牙设备信息        IntentFilter intent = new IntentFilter();        intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果        intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);        intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);        intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);        registerReceiver(searchDevices, intent);    }    private BroadcastReceiver searchDevices = new BroadcastReceiver() {        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            Bundle b = intent.getExtras();            Object[] lstName = b.keySet().toArray();            // 显示所有收到的消息及其细节            for (int i = 0; i < lstName.length; i++) {                String keyName = lstName[i].toString();                Log.e(keyName, String.valueOf(b.get(keyName)));            }            //搜索设备时,取得设备的MAC地址            if (BluetoothDevice.ACTION_FOUND.equals(action)) {                BluetoothDevice device = intent                        .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);                String str = device.getName() + "|" + device.getAddress();                if (lstDevices.indexOf(str) == -1)// 防止重复添加,                    lstDevices.add(str); // 获取设备名称和mac地址                adtDevices.notifyDataSetChanged();//通知Activity刷新数据                isFinish = true;            }        }    };    class ItemClickEvent implements AdapterView.OnItemClickListener {        @Override        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,                                long arg3) {            btAdapt.cancelDiscovery();            String str = lstDevices.get(arg2);            String[] values = str.split("\\|");            address = values[1];            Log.d("address", values[1]);            Intent i = new Intent(NewActivity.this, MyService.class);            //绑定服务同样只能绑定一次,如果已绑定首先要解绑            if (serviceBindStatus == 1) {                unbindService(connection);                serviceBindStatus = 2;                Log.d("tag", "unbind");            }            bindService(i, connection, BIND_AUTO_CREATE);            serviceBindStatus = 1;            Log.d("tag", "bind");            dialog.dismiss();//取消蓝牙设置Dialog            lstDevices.clear();//清空搜索到的设备列表        }    }    //按钮监听器,打开本机蓝牙的设置    class ClickEvent implements View.OnClickListener {        @Override        public void onClick(View v) {            if (v == btnSearch)// 搜索蓝牙设备,在BroadcastReceiver显示结果            {                if (btAdapt.getState() == BluetoothAdapter.STATE_OFF) {// 如果蓝牙还没开启                    Toast.makeText(NewActivity.this, "请先打开蓝牙", Toast.LENGTH_LONG).show();                    return;                }                setTitle("本机蓝牙地址:" + btAdapt.getAddress());                lstDevices.clear();                btAdapt.startDiscovery();                showProgressDialog();            } else if (v == tbtnSwitch) {// 本机蓝牙启动/关闭                if (tbtnSwitch.isChecked() == false)                    btAdapt.enable();                else if (tbtnSwitch.isChecked() == true)                    btAdapt.disable();            }        }    }    //“请稍等”Dialog    private void showProgressDialog() {        pDialog = new ProgressDialog(NewActivity.this);        pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);        pDialog.setProgress(100);        pDialog.setMessage("请稍等...");        pDialog.setIndeterminate(false);        pDialog.show();        WindowManager.LayoutParams lp = pDialog.getWindow().getAttributes();        lp.gravity = Gravity.CENTER;        Window win = pDialog.getWindow();        win.setAttributes(lp);        new Thread(new Runnable() {            @Override            public void run() {                long startTime = System.currentTimeMillis();                int progress = 0;                //搜索结束或者搜索时间超过10秒跳出循环                while (!isFinish && System.currentTimeMillis() - startTime <= 10000) {                    try {                        progress += 10;                        pDialog.setProgress(progress);                        Thread.sleep(100);                    } catch (InterruptedException e) {                        pDialog.dismiss();                    }                }                pDialog.dismiss();                //如果超过10秒,通知handler来Toast                if (System.currentTimeMillis() - startTime >= 10000) {                    Message msg = new Message();                    handler.sendMessage(msg);                }                isFinish = false;//标志重新置为false,等待下一次搜索            }        }).start();    }    public Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            Toast.makeText(NewActivity.this, "搜索时间过长,请重试", Toast.LENGTH_SHORT).show();            super.handleMessage(msg);        }    };}

MyService:

public class MyService extends Service {    static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";    private BlueToothBinder binder=new BlueToothBinder();//自定义BlueToothBinder类    private BluetoothSocket btSocket;    private int connectionStatus,sendStatus;//蓝牙连接状态和发送状态    @Override    public IBinder onBind(Intent intent) {        return binder;    }    public void connect(String address,BluetoothAdapter btAdapt){        UUID uuid = UUID.fromString(SPP_UUID);        BluetoothDevice btDev = btAdapt.getRemoteDevice(address);        try {            btSocket = btDev                    .createRfcommSocketToServiceRecord(uuid);            try {                // 连接建立之前的先配对                if (btDev.getBondState() == BluetoothDevice.BOND_NONE) {                    Method creMethod = BluetoothDevice.class                            .getMethod("createBond");                    Log.e("TAG", "开始配对");                    creMethod.invoke(btDev);                }            } catch (Exception e) {                e.printStackTrace();            }            btSocket.connect();            Log.d("tag","connect succeeded");            connectionStatus=1;        } catch (IOException e) {            // TODO Auto-generated catch block            e.printStackTrace();            Log.d("tag","connect fail");            connectionStatus=2;        }    }    public int getConnectionStatus(){        return connectionStatus;    }    public int getSendStatus() {        return sendStatus;    }    public void send(String str){        OutputStream os = null;        try {            os = btSocket.getOutputStream();        } catch (IOException e) {            e.printStackTrace();        }        if (os != null) {            try {                os.write(str.getBytes("UTF-8"));                Log.d("tag","send succeed");                sendStatus=1;            } catch (IOException e) {                sendStatus=2;                e.printStackTrace();            }        }    }    @Override    public void onDestroy() {        super.onDestroy();        try {            send("close");//连接关闭之前手动发送close,通知Server端连接关闭            btSocket.close();//socket要记得关闭            Log.d("tag","socket close");        } catch (IOException e) {            e.printStackTrace();        }    }    class BlueToothBinder extends Binder{        //利用Binder得到Service实例        public MyService getService(){            return MyService.this;        }    }}

蓝牙设置的布局文件就是前一篇文章的布局文件稍加修改,主活动的布局文件就是Button、EditText很简单,都不贴了。

Server端:

public class MainActivity extends ActionBarActivity {    private final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");    private BluetoothAdapter bluetoothAdapter;    private final String NAME = "BlueTooth_Socket";    private AcceptThread acceptThread;    private Button btn_start, btn_searched;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//        初始版本是在onCreate里就新建线程,这样的问题是只有第一次连接成功,断开之后就无法连接了//        因为serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID)//        是写在AcceptThread()的构造方法里的,断开之后就无法监听了//        acceptThread = new AcceptThread();//        acceptThread.start();        btn_searched = (Button) findViewById(R.id.searched);        btn_searched.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent discoverableIntent = new Intent(                        BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);                discoverableIntent.putExtra(                        BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);                startActivity(discoverableIntent);//本机蓝牙的内部设置            }        });        btn_start = (Button) findViewById(R.id.start);        //点击一次开一次线程,所以每次连接断开后,要再次点击之后才能开始监听        btn_start.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                acceptThread = new AcceptThread();                acceptThread.start();                Toast.makeText(MainActivity.this, "开启监听", Toast.LENGTH_SHORT).show();            }        });    }    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            Toast.makeText(MainActivity.this, String.valueOf(msg.obj), Toast.LENGTH_SHORT).show();        }    };    private class AcceptThread extends Thread {        private BluetoothServerSocket serverSocket;        private BluetoothSocket socket;        private InputStream is;        //构造函数里进行蓝牙监听        public AcceptThread() {            try {                serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);            } catch (Exception e) {            }        }        @Override        public void run() {            try {                socket = serverSocket.accept();                //socket.connect();                Message message = new Message();                message.what = 1;                message.obj = "connect succeed";                handler.sendMessage(message);                Log.d("tag", "connected");                //如果不想在accept其他的连接,则调用BluetoothServerSocket的close()方法释放资源                // (调用该方法后,之前获得的BluetoothSocket实例并没有close。但由于RFCOMM一个时                // 刻只允许在一条channel中有一个连接,则一般在accept一个连接后,                // 便close掉BluetoothServerSocket)。这里如果不close,serverSocket被占用                //新的线程里的serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);                //无法使用                serverSocket.close();                is = socket.getInputStream();                //原本跳出循环打算用socket.isConnected(),但是因为没有用connect(),                // 返回一直是false,行不通。只能在关闭前手动发送一个消息                while (true) {                    Thread.sleep(1);//减少CPU占用率                    byte[] buffer = new byte[128];                    int count = is.read(buffer);                    Message msg = new Message();                    msg.what = 2;                    msg.obj = new String(buffer, 0, count, "UTF-8");                    //如果接受的消息为close,表示Client端已关闭连接,跳出循环                    if (String.valueOf(msg.obj).equals("close")) {                        Message m = new Message();                        m.what = 3;                        m.obj = "disconnect";                        handler.sendMessage(m);                        Log.d("tag", "break");                        break;                    }                    handler.sendMessage(msg);//sendMessage写在这里防止close消息发送给handler                }            } catch (Exception e) {            }        }    }}

操作步骤:
1、Server端点击本机可被搜索,然后点击开启监听
2、Client端点击蓝牙设置,搜索设备,点击搜索到的设备,Client端和Server端会同时Toast “connect succeed”
3、连接成功后,发送消息,Client端Toast “send succeed”,Server端Toast消息内容
4、点击停止服务,Client端Toast “Service stop”,Server端Toast “disconnect”
5、若要重新连接,重复上述步骤

注:别忘了蓝牙权限和Service注册

0 0
原创粉丝点击