您的位置:首页 > 移动开发 > Android开发

Android Service和Activity基于串口蓝牙模块的双向通信【第二篇】

2016-07-29 10:07 417 查看
一直以来都想利用手机来控制一些东西,比如电灯,电风扇等家电或者智能小车等.

驱动蓝牙模块可以在Activity中直接调用,也可以在多线程下直接使用,但这样会存在一个缺陷:当你按下手机的Home或者Back键的时候.程序退出了,下次你重新启动软件的时候又需要重新建立蓝牙的链接了.

为了克服以上问题,我把蓝牙模块的调用放到Service里面使用.首先对Service说明下:(来源于http://tianrui-wang-163-com.iteye.com/blog/983099)

Service介绍

Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。

由于没有可视化界面,Service都是从其它程序组件中启动、停止和控制,这些组件包括其它的Service、Activity和Broadcast Receiver。如果你的应用程序正常且不间断的运行,而不直接依赖于用户输入,Service是你最佳的选择。

Service生命周期

服务常用生命周期回调方法如下:

onCreate() 该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。

onDestroy()该方法在服务被终止时调用。

Service对象不能自己启动,需要通过某个Activity、Service或者其他Context对象来启动。启动的方法有两种,Context.startService和Context.bindService()。两种方式的生命周期是不同的,具体如下所示。

Context.startService方式的生命周期:

启动时,startService –> onCreate() –> onStart()

停止时,stopService –> onDestroy()

Context.bindService方式的生命周期:

绑定时,bindService -> onCreate() –> onBind()

解绑定时,unbindService –>onUnbind() –> onDestory()

Service实现

定义一个Service只需要如下两步:

第一步:继承Service类

public class SMSService extends Service { } 这里可以选择要实现的方法

第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置:

<service android:name=".SMSService" ”></service>

好了,废话少说,下面从我的代码直接开始:

[java] view
plain copy

package com.lxx;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.util.UUID;

import android.app.Service;

import android.bluetooth.BluetoothAdapter;

import android.bluetooth.BluetoothDevice;

import android.bluetooth.BluetoothSocket;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.IBinder;

import android.util.Log;

public class MyService extends Service{

public boolean threadFlag = true;

MyThread myThread;

CommandReceiver cmdReceiver;//继承自BroadcastReceiver对象,用于得到Activity发送过来的命令

/**************service 命令*********/

static final int CMD_STOP_SERVICE = 0x01;

static final int CMD_SEND_DATA = 0x02;

static final int CMD_SYSTEM_EXIT =0x03;

static final int CMD_SHOW_TOAST =0x04;

private BluetoothAdapter mBluetoothAdapter = null;

private BluetoothSocket btSocket = null;

private OutputStream outStream = null;

private InputStream inStream = null;

public boolean bluetoothFlag = true;

private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

private static String address = "00:19:5D:EE:9B:8F"; // <==要连接的蓝牙设备MAC地址

@Override

public IBinder onBind(Intent intent) {

// TODO Auto-generated method stub

return null;

}

@Override

public void onCreate() {

// TODO Auto-generated method stub

super.onCreate();

}

//前台Activity调用startService时,该方法自动执行

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

// TODO Auto-generated method stub

cmdReceiver = new CommandReceiver();

IntentFilter filter = new IntentFilter();//创建IntentFilter对象

//注册一个广播,用于接收Activity传送过来的命令,控制Service的行为,如:发送数据,停止服务等

filter.addAction("android.intent.action.cmd");

//注册Broadcast Receiver

registerReceiver(cmdReceiver, filter);

doJob();//调用方法启动线程

return super.onStartCommand(intent, flags, startId);

}

@Override

public void onDestroy() {

// TODO Auto-generated method stub

super.onDestroy();

this.unregisterReceiver(cmdReceiver);//取消注册的CommandReceiver

threadFlag = false;

boolean retry = true;

while(retry){

try{

myThread.join();

retry = false;

}catch(Exception e){

e.printStackTrace();

}

}

}

public class MyThread extends Thread{

@Override

public void run() {

// TODO Auto-generated method stub

super.run();

connectDevice();//连接蓝牙设备

while(threadFlag){

int value = readByte();

if(value != -1){

DisplayToast(value + "");

}

try{

Thread.sleep(50);

}catch(Exception e){

e.printStackTrace();

}

}

}

}

public void doJob(){

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

if (mBluetoothAdapter == null) {

DisplayToast("蓝牙设备不可用,请打开蓝牙!");

bluetoothFlag = false;

return;

}

if (!mBluetoothAdapter.isEnabled()) {

DisplayToast("请打开蓝牙并重新运行程序!");

bluetoothFlag = false;

stopService();

showToast("请打开蓝牙并重新运行程序!");

return;

}

showToast("搜索到蓝牙设备!");

threadFlag = true;

myThread = new MyThread();

myThread.start();

}

public void connectDevice(){

DisplayToast("正在尝试连接蓝牙设备,请稍后····");

BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);

try {

btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);

} catch (IOException e) {

DisplayToast("套接字创建失败!");

bluetoothFlag = false;

}

DisplayToast("成功连接蓝牙设备!");

mBluetoothAdapter.cancelDiscovery();

try {

btSocket.connect();

DisplayToast("连接成功建立,可以开始操控了!");

showToast("连接成功建立,可以开始操控了!");

bluetoothFlag = true;

} catch (IOException e) {

try {

btSocket.close();

bluetoothFlag = false;

} catch (IOException e2) {

DisplayToast("连接没有建立,无法关闭套接字!");

}

}

if(bluetoothFlag){

try {

inStream = btSocket.getInputStream();

} catch (IOException e) {

e.printStackTrace();

} //绑定读接口

try {

outStream = btSocket.getOutputStream();

} catch (IOException e) {

e.printStackTrace();

} //绑定写接口

}

}

public void sendCmd(byte cmd, int value)//串口发送数据

{

if(!bluetoothFlag){

return;

}

byte[] msgBuffer = new byte[5];

msgBuffer[0] = cmd;

msgBuffer[1] = (byte)(value >> 0 & 0xff);

msgBuffer[2] = (byte)(value >> 8 & 0xff);

msgBuffer[3] = (byte)(value >> 16 & 0xff);

msgBuffer[4] = (byte)(value >> 24 & 0xff);

try {

outStream.write(msgBuffer, 0, 5);

outStream.flush();

} catch (IOException e) {

e.printStackTrace();

}

}

public int readByte(){//return -1 if no data

int ret = -1;

if(!bluetoothFlag){

return ret;

}

try {

ret = inStream.read();

} catch (IOException e) {

e.printStackTrace();

}

return ret;

}

public void stopService(){//停止服务

threadFlag = false;//停止线程

stopSelf();//停止服务

}

public void showToast(String str){//显示提示信息

Intent intent = new Intent();

intent.putExtra("cmd", CMD_SHOW_TOAST);

intent.putExtra("str", str);

intent.setAction("android.intent.action.lxx");

sendBroadcast(intent);

}

public void DisplayToast(String str)

{

Log.d("Season",str);

}

//接收Activity传送过来的命令

private class CommandReceiver extends BroadcastReceiver{

@Override

public void onReceive(Context context, Intent intent) {

if(intent.getAction().equals("android.intent.action.cmd")){

int cmd = intent.getIntExtra("cmd", -1);//获取Extra信息

if(cmd == CMD_STOP_SERVICE){

stopService();

}

if(cmd == CMD_SEND_DATA)

{

byte command = intent.getByteExtra("command", (byte) 0);

int value = intent.getIntExtra("value", 0);

sendCmd(command,value);

}

}

}

}

}

主界面Activity只有一个按钮,就是通过Broadcast来想Service发送数据,Service收到数据进行命令等的解析,然后调用相应的函数,相当于直接调用了Service中的函数.因为Activity和Service是运行在不同的进程中的,两者不能进行直接的通讯,必须通过一个"桥梁"建立起联系才行.

[java] view
plain copy

package com.lxx;

import android.app.Activity;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.content.IntentFilter;

import android.os.Bundle;

import android.os.IBinder;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.TextView;

import android.widget.Toast;

public class BroadcastActivity extends Activity {

/** Called when the activity is first created. */

TextView myTextView;

Button sendButton;

MyReceiver receiver;

IBinder serviceBinder;

MyService mService;

Intent intent;

int value = 0;

/**************service 命令*********/

static final int CMD_STOP_SERVICE = 0x01;

static final int CMD_SEND_DATA = 0x02;

static final int CMD_SYSTEM_EXIT =0x03;

static final int CMD_SHOW_TOAST =0x04;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

myTextView = (TextView)findViewById(R.id.myTextView);

myTextView.setText("Season");

sendButton = (Button)findViewById(R.id.sendButton);

sendButton.setOnClickListener(new SendButtonClickListener());

intent = new Intent(BroadcastActivity.this,MyService.class);

startService(intent);

}

public class SendButtonClickListener implements OnClickListener{

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

byte command = 45;

int value = 0x12345;

sendCmd(command,value);

}

}

@Override

protected void onDestroy() {

// TODO Auto-generated method stub

super.onDestroy();

if(receiver!=null){

BroadcastActivity.this.unregisterReceiver(receiver);

}

}

@Override

protected void onResume() {

// TODO Auto-generated method stub

super.onResume();

receiver = new MyReceiver();

IntentFilter filter=new IntentFilter();

filter.addAction("android.intent.action.lxx");

BroadcastActivity.this.registerReceiver(receiver,filter);

}

public void showToast(String str){//显示提示信息

Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();

}

public class MyReceiver extends BroadcastReceiver{

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

if(intent.getAction().equals("android.intent.action.lxx")){

Bundle bundle = intent.getExtras();

int cmd = bundle.getInt("cmd");

if(cmd == CMD_SHOW_TOAST){

String str = bundle.getString("str");

showToast(str);

}

else if(cmd == CMD_SYSTEM_EXIT){

System.exit(0);

}

}

}

}

public void sendCmd(byte command, int value){

Intent intent = new Intent();//创建Intent对象

intent.setAction("android.intent.action.cmd");

intent.putExtra("cmd", CMD_SEND_DATA);

intent.putExtra("command", command);

intent.putExtra("value", value);

sendBroadcast(intent);//发送广播

}

}

以下主要对代码部分进行详细的说明:

1.为了方便Activity和Service简历起良好的通信关系,需要在各自发送的数据进行命令的解释,这些命令在两者之间是一致的,能够相互读懂对方发送过来的数据.

[java] view
plain copy

/**************service 命令*********/

static final int CMD_STOP_SERVICE = 0x01;//停止服务

static final int CMD_SEND_DATA = 0x02;//发送数据

static final int CMD_SYSTEM_EXIT =0x03;//退出程序

static final int CMD_SHOW_TOAST =0x04;//界面上显示toast

2.Service传送数据到Activity:

要接收Broadcast首先的有个Receiver:

[java] view
plain copy

//接收Activity传送过来的命令

private class CommandReceiver extends BroadcastReceiver{

@Override

public void onReceive(Context context, Intent intent) {

if(intent.getAction().equals("android.intent.action.cmd")){

int cmd = intent.getIntExtra("cmd", -1);//获取Extra信息

if(cmd == CMD_STOP_SERVICE){

stopService();

}

if(cmd == CMD_SEND_DATA)

{

byte command = intent.getByteExtra("command", (byte) 0);

int value = intent.getIntExtra("value", 0);

sendCmd(command,value);

}

}

}

}

为了能够接收到数据,首先得把这个Receiver注册:

[java] view
plain copy

public class MyReceiver extends BroadcastReceiver{

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

if(intent.getAction().equals("android.intent.action.lxx")){

Bundle bundle = intent.getExtras();

int cmd = bundle.getInt("cmd");

if(cmd == CMD_SHOW_TOAST){

String str = bundle.getString("str");

showToast(str);

}

else if(cmd == CMD_SYSTEM_EXIT){

System.exit(0);

}

}

}

}

其中doJob是启动一个线程,定时的去读取蓝牙串口的数据,然后根据里面的数据解析就可以实现不同的操作了,这个反过来就是外设直接控制手机了.因为这个线程是一直在后台运行着的,只要不结束Service,它就一直保持这与外设串口蓝牙模块的通讯.

[java] view
plain copy

public class MyThread extends Thread{

@Override

public void run() {

// TODO Auto-generated method stub

super.run();

connectDevice();//连接蓝牙设备

while(threadFlag){

int value = readByte();//从蓝牙模块读取一个字节的数据,解释命令用

if(value != -1){

DisplayToast(value + "");

}

try{

Thread.sleep(50);

}catch(Exception e){

e.printStackTrace();

}

}

}

}

3.Activity传送数据到Service:

Activity上有一个按钮,点击一下就可以发送数据到蓝牙串口模块,工作原理是这样的:界面上实现按钮的点击事件

[java] view
plain copy

public class SendButtonClickListener implements OnClickListener{

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

byte command = 45;

int value = 0x12345;

sendCmd(command,value);

}

}

这里的sendCmd是关键,里面通过建立起一个广播消息,Service里会负责接收他

[java] view
plain copy

public void sendCmd(byte command, int value){

Intent intent = new Intent();//创建Intent对象

intent.setAction("android.intent.action.cmd");

intent.putExtra("cmd", CMD_SEND_DATA);

intent.putExtra("command", command);

intent.putExtra("value", value);

sendBroadcast(intent);//发送广播

}

Service中

[java] view
plain copy

//前台Activity调用startService时,该方法自动执行

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

// TODO Auto-generated method stub

cmdReceiver = new CommandReceiver();

IntentFilter filter = new IntentFilter();//创建IntentFilter对象

//注册一个广播,用于接收Activity传送过来的命令,控制Service的行为,如:发送数据,停止服务等

filter.addAction("android.intent.action.cmd");

//注册Broadcast Receiver

registerReceiver(cmdReceiver, filter);

doJob();//调用方法启动线程

return super.onStartCommand(intent, flags, startId);

}

通过以上步骤就可以建立起蓝牙模块发送数据到Activity,Activity也可以发送数据到蓝牙模块了;

本例子的源码:

http://download.csdn.net/source/3557026

转自:http://blog.csdn.net/cen616899547/article/details/6728040
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: