现在的位置: 首页 > 综合 > 正文

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

2013年12月09日 ⁄ 综合 ⁄ 共 11014字 ⁄ 字号 评论关闭

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

驱动蓝牙模块可以在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也可以发送数据到蓝牙模块了;

本例子的源码:

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

抱歉!评论已关闭.