2011年2月22日 星期二

Activity啟動Service - 以GPS定位為例

Service以背景服務方式提供程序的運作執行. 在其運作執行期間, 並不能直接處理使用者介面的內容, 完全以背景服務方式執行. 如果必提供資料給前景的Activity, 則必須以一個android.os.Handler物件負責處理. 或是呼叫 sendBroadcast()方法送出一個Broadcast, 而由Activity預先產生的BroadcastReceiver負責接受, 進而處理使用者介面.

啟動Service的方式通常是呼叫Context.startService()方法.

一個Activity中有兩個按鈕, 分別處理啟動GPS的啟動與停止, 而有一個文字介面呈現啟動的GPS所傳回相關的資訊. 當啟動按鈕按下之後, 將會由Activity呼叫startService()啟動一個Service的生命周期:
1. onCreate()
2. onStartCommand()
3. onDestroy()

通常會在onCreate()中進行相關的初始化的程序, 而onStartCommand()負責主要的服務程序, 當Activity呼叫了stopService(), 就會使該Service進入到onDestroy()的程序.

此時Service負責蒐集目前GPS相關資訊, 並且將資料以sendBroadcast()方法發送給Activity, Activity會預先一個BroadcastReceiver務件負責接收, 並且將資料呈現在文字介面.
首先, 先簡單處理顯示的版面配置:
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
 <Button
  android:id="@+id/service"  
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:text="Service"
     />
 <Button
  android:id="@+id/stop"  
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     android:text="Stop"
     />
 <TextView
  android:id="@+id/info"  
     android:layout_width="fill_parent" 
     android:layout_height="wrap_content" 
     />
</LinearLayout>

接著在Activity中加上一個自訂的BroadcastReceiver:
private class MyReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
   double Lat = intent.getDoubleExtra("Lat", 0.0);
   double Long = intent.getDoubleExtra("Long", 0.0);
   float Accuracy = intent.getFloatExtra("Accuracy", 0.0f);
   float Bearing = intent.getFloatExtra("Bearing", 0.0f);
   float Speed = intent.getFloatExtra("Speed", 0.0f);
   long Time = intent.getLongExtra("Time", 0);
   
   info.setText("Lat: " + Lat + "\n" +
      "Long" + Long + "\n" +
      "Accuracy" + Accuracy + "\n" +
      "Bearing" + Bearing + "\n" +
      "Speed" + Speed + "\n" +
      "Time" + Time + "\n"
      );
  }     
 }

上段程式重點如下:
  • 繼承 extends BroadcastReceiver, 並Override onReceive()方法
  • 假設將會收到一個Intent物件, 其內含地理位置資訊資料.
  • 取出資料後, 呈現在 info 的TextView物件中.
回到Activity中:
MyReceiver receiver = new MyReceiver();
IntentFilter filter = new IntentFilter("MyFilter");
registerReceiver(receiver, filter);

上段程式重點如下:

  • 建構出前段程式中自訂的 MyReceive 物件
  • 建構特定的 IntentFilter 物件, 只處理 "MyFilter" 的 Intent.
  • 註冊 Receiver, 之後只要收到 Broadcast 後, 觸發 onReceive() 方法
將按鈕及其他部份處理上去:
package tw.brad.android.test.ServiceTest;

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.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class ServiceTest extends Activity {
    private Button service,stop;
    private TextView info;
    private MyReceiver receiver;
    private IntentFilter filter;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        service = (Button)findViewById(R.id.service);
        stop = (Button)findViewById(R.id.stop);
        info = (TextView)findViewById(R.id.info);
  info.setText("Stop Service");
        
        receiver = new MyReceiver();
        filter = new IntentFilter("MyFilter");
        registerReceiver(receiver, filter);

        service.setOnClickListener(new OnClickListener(){
   @Override
   public void onClick(View v) {
    Intent intent = new Intent();
    intent.putExtra("mode", true);
    intent.setClass(ServiceTest.this, MyService.class);
    startService(intent);
   }         
        });
        stop.setOnClickListener(new OnClickListener(){
   @Override
   public void onClick(View v) {
    Intent intent = new Intent();
    intent.putExtra("mode", false);
    intent.setClass(ServiceTest.this, MyService.class);
    startService(intent);
    info.setText("Stop Service");
   }
        });
    }
    
 private class MyReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
   double Lat = intent.getDoubleExtra("Lat", 0.0);
   double Long = intent.getDoubleExtra("Long", 0.0);
   float Accuracy = intent.getFloatExtra("Accuracy", 0.0f);
   float Bearing = intent.getFloatExtra("Bearing", 0.0f);
   float Speed = intent.getFloatExtra("Speed", 0.0f);
   long Time = intent.getLongExtra("Time", 0);
   
   info.setText("Lat: " + Lat + "\n" +
      "Long" + Long + "\n" +
      "Accuracy" + Accuracy + "\n" +
      "Bearing" + Bearing + "\n" +
      "Speed" + Speed + "\n" +
      "Time" + Time + "\n"
      );
  }     
 }
}

接下來將重點放在 Service:
package tw.brad.android.test.ServiceTest;

import android.app.Service;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;

public class MyService extends Service {
 private LocationManager lm;
 private MyLocationListener mll;
 @Override
 public IBinder onBind(Intent arg0) {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public void onCreate() {
  super.onCreate();
  lm = (LocationManager)getSystemService(LOCATION_SERVICE);
  mll = new MyLocationListener();
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  boolean mode = intent.getBooleanExtra("mode", true);
  if (mode){
   lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10, 0, mll);
  }else {
   lm.removeUpdates(mll);
  }
  
  return super.onStartCommand(intent, flags, startId);
 }
 
 
 @Override
 public void onDestroy() {
  super.onDestroy();
  lm.removeUpdates(mll);
 }


 private class MyLocationListener implements LocationListener {

  @Override
  public void onLocationChanged(Location location) {
   Intent intent = new Intent("MyFilter");
   intent.putExtra("Lat", location.getLatitude());
   intent.putExtra("Long", location.getLongitude());
   intent.putExtra("Accuracy", location.getAccuracy());
   intent.putExtra("Bearing", location.getBearing());
   intent.putExtra("Speed", location.getSpeed());
   intent.putExtra("Time", location.getTime());
   sendBroadcast(intent);
  }

  @Override
  public void onProviderDisabled(String provider) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onProviderEnabled(String provider) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onStatusChanged(String provider, int status, Bundle extras) {
   // TODO Auto-generated method stub
   
  }
  
 }

}

不要忘記 AndroidManifest.xml 中處理權限及Service:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="tw.brad.android.test.ServiceTest"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".ServiceTest"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    <service android:name="MyService"></service>
</application>
    <uses-sdk android:minSdkVersion="7" />

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
</manifest> 

沒有留言:

張貼留言