Android Service和Activity基于串口蓝牙模块的双向通信

来源:互联网 发布:汪正扬看的编程书 编辑:程序博客网 时间:2024/04/26 21:28

一直以来都想利用手机来控制一些东西,比如电灯,电风扇等家电或者智能小车等.

驱动蓝牙模块可以在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>

 

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

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是运行在不同的进程中的,两者不能进行直接的通讯,必须通过一个"桥梁"建立起联系才行.

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简历起良好的通信关系,需要在各自发送的数据进行命令的解释,这些命令在两者之间是一致的,能够相互读懂对方发送过来的数据.

/**************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:


//接收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注册:


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,它就一直保持这与外设串口蓝牙模块的通讯.

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


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里会负责接收他


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中

//前台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也可以发送数据到蓝牙模块了;

原创粉丝点击