android service,服務的正確姿勢

Dusan_杜小菜發表於2016-05-11

service,android四大元件之一,脫離UI在後臺執行。特點和具體如何使用無需多言吧。

一,service是個什麼鬼

Service(服務)是一個沒有使用者介面的在後臺執行執行耗時操作的應用元件。其他應用元件能夠啟動Service,並且當使用者切換到另外的應用場景,Service將持續在後臺執行。另外,一個元件能夠繫結到一個service與之互動(IPC機制),例如,一個service可能會處理網路操作,播放音樂,操作檔案I/O或者與內容提供者(content provider)互動,所有這些活動都是在後臺進行。

Started Service生命週期及程式相關

1.onCreate(Client首次startService(..)) >> onStartCommand >> onStartCommand - optional … >> onDestroy(Client呼叫stopService(..))

注:onStartCommand(..)可以多次被呼叫,onDestroy()與onCreate()想匹配,當使用者強制kill掉程式時,onDestroy()是不會執行的。

2.對於同一型別的Service,Service例項一次永遠只存在一個,而不管Client是否是相同的元件,也不管Client是否處於相同的程式中。

3.Service通過startService(..)啟動Service後,此時Service的生命週期與Client本身的什麼週期是沒有任何關係的,只有Client呼叫stopService(..)或Service本身呼叫stopSelf(..)才能停止此Service。當然,當使用者強制kill掉Service程式或系統因記憶體不足也可能kill掉此Service。

4.Client A 通過startService(..)啟動Service後,可以在其他Client(如Client B、Client C)通過呼叫stopService(..)結束此Service。

5.Client呼叫stopService(..)時,如果當前Service沒有啟動,也不會出現任何報錯或問題,也就是說,stopService(..)無需做當前Service是否有效的判斷。

6.startService(Intent serviceIntent),其中的intent既可以是顯式Intent,也可以是隱式Intent,當Client與Service同處於一個App時,一般推薦使用顯示Intent。當處於不同App時,只能使用隱式Intent。

在AndroidManifest.xml檔案中註冊service。

 <!--apk升級,自動備份,監測網路狀態-->
        <service android:name="com.x x.service.OairService">
            <intent-filter>
                <action android:name="com.x x.oairService.action"/>
            </intent-filter>
        </service>

service實現類,繼承Service。

/**
 * DemoService 生命週期方法
 * Created by duqian on 16/5/11.
 */
public class DemoService extends Service {
    private Context context;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }
}

二,service一些常用方法

開啟和停止service,要注意相容android 5.0以上版本,服務啟動要設定你應用的包名,否則會報錯,閃退。

/**
 * service 工具類
 * Created by duqian on 15/12/28.
 */
public class ServiceUtil {
    private static final String TAG =ServiceUtil.class.getSimpleName();
    //開啟服務:
    public static void startService(Context context,Class clazz) {
        boolean serviceRunning = SystemUtils.isServiceRunning(context, clazz);
        if (serviceRunning){
            forceStopService(context,clazz);//先停止再開啟:重啟服務,防止服務失效。
        }
        Intent service = new Intent(context, clazz);
        //Intent service = new Intent();
        //service.setAction("com.oair.oairService.action");
        service.setPackage(context.getPackageName());//這裡你需要設定你應用的包名,5.0新要求
        context.startService(service);//多次呼叫startService並不會啟動多個service 而是會多次呼叫onStart
    }

    /**
     * 停止service
     * @param context
     * @param clazz
     */
    public static void stopService(Context context,Class clazz) {
        boolean serviceRunning = SystemUtils.isServiceRunning(context, clazz);
        if (serviceRunning){
            forceStopService(context, clazz);
        }
    }

    private static void forceStopService(Context context, Class clazz) {
        Intent intent = new Intent(context, clazz);
        intent.setPackage(context.getPackageName());//這裡你需要設定你應用的包名,5.0新要求
        context.stopService(intent);
        //LogUtils.debug(TAG,clazz.getSimpleName()+" is stopped");
    }

    /**
     * service中檢測本應用是否為前臺介面
     * @param context
     * @return
     */
    public static boolean checkAppRunning(Context context) {
        boolean isRunning = false;
        ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
        ComponentName cn = activityManager.getRunningTasks(1).get(0).topActivity;
        String topPackageName = cn.getPackageName();

        String myPackageName = context.getPackageName();
        if (topPackageName != null && topPackageName.equals(myPackageName)) {
            //在這裡停止含有定時執行的服務,在停止之前需要先取消該定時器
            isRunning = true;
            //LogUtils.debug(TAG,"當前棧頂應用:" + topPackageName + ",myPackageName=" + myPackageName);
        }else{
            isRunning = false;
        }
        return isRunning;
    }
}

三,service相關常識

startService 啟動的服務,主要用於啟動一個服務執行後臺任務,不進行通訊。停止服務使用stopService。
bindService 啟動的服務,要進行通訊。停止服務使用unbindService。
startService 同時也 bindService 啟動的服務,停止服務應同時使用stopService與unbindService。
android中打造不死的service比較麻煩,因為非前臺程式會被系統kill。

重點關注下onStartCommand(Intent intent, int flags, int startId)方法。

其中引數flags預設情況下是0,對應的常量名為START_STICKY_COMPATIBILITY。startId是一個唯一的整型,用於表示此次Client執行startService(…)的請求請求標識,在多次startService(…)的情況下,呈現0,1,2….遞增。另外,此函式具有一個int型的返回值,具體的可選值及含義如下:

START_NOT_STICKY:當Service因為記憶體不足而被系統kill後,接下來未來的某個時間內,即使系統記憶體足夠可用,系統也不會嘗試重新建立此Service。除非程式中Client明確再次呼叫startService(…)啟動此Service。

START_STICKY:當Service因為記憶體不足而被系統kill後,接下來未來的某個時間內,當系統記憶體足夠可用的情況下,系統將會嘗試重新建立此Service,一旦建立成功後將回撥onStartCommand(…)方法,但其中的Intent將是null,pendingintent除外。

START_REDELIVER_INTENT:與START_STICKY唯一不同的是,回撥onStartCommand(…)方法時,其中的Intent將是非空,將是最後一次呼叫startService(…)中的intent。

歡迎交流,杜工,Dusan,Q291902259。

相關文章