一直以來都想利用手機來控制一些東西,比如電燈,電風扇等家電或者智能小車等.
驅動藍牙模塊可以在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>
好了,廢話少說,下面從我的代碼直接開始:
主界面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也可以發送數據到藍牙模塊了;