1. 程式人生 > >Android Service總結(上)

Android Service總結(上)

Service是android中在後臺執行,不提供使用者介面的一個元件。

Service可以有2種形式:啟動的(Started)Service和繫結的(Bound)Service。

啟動的Service是由一個元件(比如Activity)呼叫startService建立的,在後臺獨立行的元件(即使啟動它的元件被銷燬)。它並不返回執行結果。

繫結的Service可以在執行期間和程序內的其他元件(僅限於Activity,Content Provider, Service)進行互動,繫結的Service相當於一個伺服器,呼叫它的元件相當於客戶端,在執行期間客戶端可以向Service傳送請求,繫結,獲取結果,還可以實現程序間通訊(IPC)

一.啟動的(Started)Service

1.建立和管理Service

建立一個Servcie:

1)建立一個類繼承Service

public class MyService extends Service{
    @Override
    public void onCreate() {
        Log.v("test","onCreate");
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v("test","onStartCommand");
        return START_NOT_STICKY;
    }


    @Override
    public void onDestroy() {
        Log.v("test","onDestroy");
    }


    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

2)在manifest檔案中宣告Servcie:

<manifest ... >
  ...
  <application ... >
      <service android:name="com.example.test.workspace1.app.MyService" />
      ...
  </application>
</manifest>

啟動Service:

在Activity或者其他元件中,可以通過Intent啟動指定的Service

Intent intent = new Intent(this, MyService.class);
startService(intent);
停止Servcie:

可以在Activity或者其他元件中呼叫 stopService(Intent)或者在onStartCommand中呼叫 stopSelf()

2.生命週期和回撥方法

在呼叫startService()方法後,如果service沒有建立,則執行onCreate方法,之後執行onStartCommand方法。否則直接執行onStartCommand方法。當呼叫stopService或者stopSelf後,呼叫onDestory回撥,然後銷燬Servcie。


3.返回值

onStartCommand方法返回一個int值,這個返回值告訴系統應該怎麼處理被系統關閉的Service,返回值可以是以下3個:

START_NOT_STICKY:當系統在onStartCommand返回後關閉Service後,不重新建立Service。

START_STICKY:當系統在onStartCommand返回後關閉Service,重新建立Service並且呼叫onStartCommand,但不重新傳遞上一個intent而是用null代替,除非用pendingIntent啟動Service。

START_REDELIVER_INTENT:當系統在onStartCommand返回後關閉Service,重新建立Service並且呼叫onStartCommand,重新傳遞最新的ntent,pendingIntent按照順序傳遞。這個返回值適用於後臺操作需要恢復上一次的進度的情況。

4.使用IntentService

Service是執行在主執行緒中的,如果Servcie中有阻塞的操作,超過10秒的話會造成ANR。為避免這種情況發生,可以在Service中啟用新的執行緒。或者繼承IntentService類。

IntentService完成的工作:

1.建立一個單獨的執行緒來執行 onStartCommand中的任務。

2.建立一個工作佇列,每次將一個intent 傳到onHandleIntent的實現中

3.提供一個 onStartCommand的預設實現,將intent送往工作佇列,並由onHandleIntent處理

4.當所有任務完成後,自己呼叫 stopSelf

參照IntentService的原始碼:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    protected abstract void onHandleIntent(Intent intent);
在onCreate的時候建立了子執行緒,並用它的Looper物件建立一個handler用來接收任務請求,在onStartCommand中呼叫onStart發出handler請求Message中封裝了請求的intent。在ServiceHandler這個內部類的Handler中的handleMessage裡,呼叫onHandleIntent做具體的耗時操作,任務結束後,呼叫stopSelf結束Service。

使用IntentService要簡單的多,實現自己的IntentService只需要2步:

1.重寫構造方法。

2.實現onHandleIntent方法,耗時的操作寫在onHandleIntent中。

public class HelloIntentService extends IntentService {

    public HelloIntentService() {
        super("HelloIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

二.繫結的(Bound)Service

當實現一個可供繫結的Service時,需要在Service中提供Ibinder介面。有三種方式可以提供Ibinder介面,分別是繼承Binder類,使用Messenger,使用AIDL。

(一).繼承Binder類。

如果需要繫結的Service對應用是私有的,並且不存在跨程序的通訊情況,那麼應該使用這種方式。

實現方式:

1.在Service類中,建立繼承Binder的類,這個類包含返回當前Service例項的方法,或者返回其他元件可以呼叫的公共方法。

2.在 onBind()回撥中返回Binder類的例項。

3.在繫結Service的元件中,例項化ServiceConnection,並且在ServiceConnection的onServiceConnected回撥中獲得Binder的例項,用它呼叫Service中的公共方法。

例子:

Service:

public class MyService extends Service{

    private final IBinder mBinder = new LocalBinder();
    private final Random mGenerator = new Random();


    public class LocalBinder extends Binder {
        MyService getService() {
            return MyService.this;
        }
    }

    //onBind方法返回Binder的實現類
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    //供其他元件呼叫的公共方法
    public int getRandomNumber() {
        return mGenerator.nextInt(100);
    }


    @Override
    public void onCreate() {
        Log.v("test","onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v("test","onStartCommand");
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.v("test","onDestroy");
    }
}
LocalBinder 類是Binder的實現類,裡面提供了getService方法返回這個Service的例項,繫結此Service的元件可以在ServiceConnection的onServiceConnected中,通過這個方法獲取Service的例項,以呼叫其他公共方法。

Activity:

public class MainActivity extends ActionBarActivity {

    private Button showNumber;
    private MyService mService;
    boolean mBound = false; //是否已經綁定了Service

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        showNumber = (Button) findViewById(R.id.showNumber);
        showNumber.setOnClickListener(btnListener);
    }

    @Override
    protected void onStart() {
        super.onStart();
        //繫結Service
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    private ServiceConnection mConnection  = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            MyService.LocalBinder localBinder = (MyService.LocalBinder)iBinder;
            mService = localBinder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBound = false;
        }
    };

    private View.OnClickListener btnListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if (mBound) {
                int num = mService.getRandomNumber();
                Toast.makeText(MainActivity.this, "number: " + num, Toast.LENGTH_SHORT).show();
            }
        }
    };
}
佈局xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context="com.example.test.workspace1.app.MainActivity">


    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true">


        <Button
            android:id="@+id/showNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="顯示隨機數"
            android:layout_centerInParent="true"
            />

        </RelativeLayout>

</RelativeLayout>
這個Activity在onStart的時候綁定了Service,在繫結期間,通過Service的例項,不斷呼叫其getRandomNumber方法以獲得不同的隨機數。

(二)使用Messenger

如果Service需要和其他程序互動(IPC),可以使用Messenger為Service提供介面。

使用Messenger的概要:

1.在Service內部實現Handler,接受客戶端請求,這個Handler作為例項化Messenger的構造方法的引數

2.在Service的onBind()回撥中通過建立的Messenger物件返回IBinder物件的例項。

3.客戶端用Ibinder例項作為引數例項化 Messenger,Messenger用來向Service傳送訊息,Service在Handler內部的 handleMessage方法中處理請求。
例子:

應用A的Service:

public class MessengerService extends Service{

    //記錄所有繫結的客戶端數量
    private ArrayList<Messenger> mClients = new ArrayList<Messenger>();
    private int mValue = 0;

    private static final int MSG_REGISTER_CLIENT = 1;

    private static final int MSG_UNREGISTER_CLIENT = 2;

    private static final int MSG_SET_VALUE = 3;

    //這個handler接受客戶端的請求
     class IncomingHandler extends Handler {

         //處理客戶端的資料
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_REGISTER_CLIENT:
                     mClients.add(msg.replyTo);
                     break;
                 case MSG_UNREGISTER_CLIENT:
                     mClients.remove(msg.replyTo);
                     break;
                 case MSG_SET_VALUE:
                     mValue = msg.arg1;
                     for (int i = mClients.size()-1; i >= 0; i--) {
                         //向客戶端傳送客戶端傳來的最新資料
                         try {
                             mClients.get(i).send(Message.obtain(null, MSG_SET_VALUE, mValue, 0));
                         } catch (RemoteException e) {
                             mClients.remove(i);
                             e.printStackTrace();
                         }
                     }
                     break;
                 default:
                     super.handleMessage(msg);
             }
         }
     }

    //IncomingHandler作為Messenger的構造方法引數
    final Messenger mMessenger = new Messenger(new IncomingHandler());


    //onBind方法中通過建立的Messenger物件返回IBinder物件的例項
    @Override
    public IBinder onBind(Intent intent) {
        Log.v("test","onBind");
        return mMessenger.getBinder();
    }


    @Override
    public void onCreate() {
        Log.v("test","onCreate");
    }


    @Override
    public void onDestroy() {
        Log.v("test","onDestroy");
    }
}
在這個Service內部實現了IncomingHandler,接受客戶端請求,這個Handler作為例項化Messenger(mMessenger)的構造方法的引數。在handler中,當收到MSG_SET_VALUE的訊息後,將這個值傳送給所有繫結它的客戶端。
應用A的Manifest檔案:
<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name=".MainActivity"
            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=".MessengerService" >
            <intent-filter>
                <action android:name="com.test.MessengerService" />
            </intent-filter>
        </service>
    </application>

應用B的Activity:
public class MainActivity extends ActionBarActivity {

    private Messenger mService = null;
    private boolean mBound = false;
    private TextView mCallbackText;

    private static final int MSG_REGISTER_CLIENT = 1;
    private static final int MSG_UNREGISTER_CLIENT = 2;
    private static final int MSG_SET_VALUE = 3;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bindBtn = (Button) findViewById(R.id.bind);
        bindBtn.setOnClickListener(mBindListener);
        Button unbindBtn = (Button) findViewById(R.id.unbind);
        unbindBtn.setOnClickListener(mUnbindListener);
        mCallbackText = (TextView)findViewById(R.id.text);

    }

    private View.OnClickListener mBindListener = new View.OnClickListener() {
        public void onClick(View v) {
            doBindService();
        }
    };

    private View.OnClickListener mUnbindListener = new View.OnClickListener() {
        public void onClick(View v) {
            doUnbindService();
        }
    };

    private void doBindService() {
        Intent intent = new Intent("com.test.MessengerService");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        mBound = true;
        mCallbackText.setText("繫結.");
    }

    private void doUnbindService() {
        if (mBound) {
            if (mService != null) {
                try {
                    //向Service傳送解除繫結的訊息
                    Message msg = Message.obtain(null, MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            unbindService(mConnection);
            mBound = false;
            mCallbackText.setText("解除繫結.");

//            msg = Message.obtain(null, MSG_SET_VALUE, this.hashCode(), 0);
//            mService.send(msg);
        }
    }


    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = new Messenger(iBinder);
            Toast.makeText(MainActivity.this, "與Service建立連線", Toast.LENGTH_SHORT).show();
            try {
                Message msg = Message.obtain(null, MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);

                //向Service傳送一串字元
                msg = Message.obtain(null, MSG_SET_VALUE, this.hashCode(), 0);
                mService.send(msg);

            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mService = null;
            Toast.makeText(MainActivity.this, "與Service斷開連線", Toast.LENGTH_SHORT).show();
        }
    };

    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SET_VALUE:
                    mCallbackText.setText("service傳遞的數字: " + msg.arg1);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    //與Service中初始化的方法一樣
    final Messenger mMessenger = new Messenger(new IncomingHandler());
}
MainActivity在繫結Service後,在onServiceConnected回撥中通過new Messenger(iBinder);得到Service的Messenger物件,通過這個Messenger物件傳送訊息,Service的handler可以接收到請求並處理,同時在MainActivity中有個全域性變數mMessenger,它初始化的方法和Service中是一樣的。在onServiceConnected中將它賦值給Message物件的replyTo屬性,通過Service的Messenger物件傳送給Service的handler,這樣Service中可以通過Activity傳送過去的Messenger物件,向Activity傳送請求,由Activity中的handler處理。

以上的Service和Activity在2個不同的APP中,它們完成的互動是:Activity將一段hashCode傳送給繫結的Service,Service記錄所有繫結自己的客戶端,並且一旦收到最新的hashCode,就將他們傳送給所有繫結的客戶端,所有繫結的客戶端中的Activity收到返回的hashCode後顯示在TextView上。這樣就通過Messenger物件,完成了2個不同程序收發訊息的互動。

執行效果:


相比Messenger,AIDL適用於讓多個APP同時訪問你的Service進行通訊的情況,一般的應用用不到AIDL,下篇單獨寫。

繫結Service的生命週期:


當Service在客戶端呼叫bindService時是不會回撥onStartCommand方法的,而是呼叫onBind,呼叫unbindService後,onUnbind方法回撥,接下來是onDestroy。

當一個Service綁定了多個客戶端時,只有所有的客戶端呼叫unbindService後,Service才會銷燬。

Service有2個回撥方法 onRebind和 onUnbind,當onUnbind返回true時,客戶端呼叫bindService時,Service會執行onRebind方法而不是onBind。