Android Service和Activity基於串口藍牙模塊的雙向通信

一直以來都想利用手機來控制一些東西,比如電燈,電風扇等家電或者智能小車等.

驅動藍牙模塊可以在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也可以發送數據到藍牙模塊了;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章