天天看點

android service,服務的正确姿勢

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().get().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。