android service

Service 是一個可以在后臺執(zhí)行長時間運行操作而不使用用戶界面的應(yīng)用組件筋栋。服務(wù)可由其他應(yīng)用組件啟動炊汤,而且即使用戶切換到其他應(yīng)用,服務(wù)仍將在后臺繼續(xù)運行弊攘。 此外抢腐,組件可以綁定到服務(wù)并與之進行交互,甚至是執(zhí)行進程間通信 (IPC)襟交。 例如迈倍,服務(wù)可以處理網(wǎng)絡(luò)事務(wù)、播放音樂捣域,執(zhí)行文件 I/O 或與內(nèi)容提供程序交互啼染,而所有這一切均可在后臺進行宴合。

服務(wù)在其托管進程的主線程中運行,它既不創(chuàng)建自己的線程迹鹅,也不在單獨的進程中運行(除非另行指定)。

應(yīng)使用服務(wù)還是線程?

服務(wù)是一種即使用戶未與應(yīng)用交互弟蚀,但它仍可以在后臺運行的組件蚤霞。 因此,應(yīng)僅在必要時才創(chuàng)建服務(wù)义钉。如果您確實要使用服務(wù)昧绣,則默認情況下,它仍會在應(yīng)用的主線程中運行捶闸,因此滞乙,如果服務(wù)執(zhí)行的是密集型或阻止性操作(例如 MP3 播放或聯(lián)網(wǎng)),則仍應(yīng)在服務(wù)內(nèi)創(chuàng)建新線程鉴嗤。

如需在主線程外部執(zhí)行工作,不過只是在用戶正在與應(yīng)用交互時才有此需要序调,則應(yīng)創(chuàng)建新線程而非服務(wù)醉锅。 例如,如果只是想在 Activity 運行的同時播放一些音樂发绢,則可在 onCreate() 中創(chuàng)建線程硬耍,在 onStart() 中啟動線程,然后在 onStop() 中停止線程边酒。還可以考慮使用 AsyncTask 或 HandlerThread经柴,而非傳統(tǒng)的 Thread 類。

Service的種類

  • 按運行類型分:
類別 區(qū)別 應(yīng)用
前臺服務(wù) 會在通知欄顯示onGoing的 Notification 當服務(wù)被終止的時候墩朦,通知一欄的 Notification 也會消失坯认,這樣對于用戶有一定的通知作用。常見的如音樂播放服務(wù)氓涣。
后臺服務(wù) 默認的服務(wù)即為后臺服務(wù)牛哺,即不會在通知一欄顯示 onGoing的 Notification。 當服務(wù)被終止的時候劳吠,用戶是看不到效果的引润。某些不需要運行或終止提示的服務(wù),如天氣更新痒玩,日期同步淳附,郵件同步等议慰。
  • 按使用方式分:
類別 區(qū)別
startService啟動的服務(wù) 主要用于啟動一個服務(wù)執(zhí)行后臺任務(wù),不進行通信奴曙。停止服務(wù)使用stopService别凹。
bindService啟動的服務(wù) 方法啟動的服務(wù)要進行通信。停止服務(wù)使用unbindService缆毁。
同時使用startService番川、bindService 啟動的服務(wù) 停止服務(wù)應(yīng)同時使用stopService與unbindService。

使用service

  • 使用startService啟動服務(wù)
    1.定義一個類繼承service
public class MyService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            //TODO do something useful
            return Service.START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
          //TODO for communication return IBinder implementation
      return null;
    }
}

onstartCommad方法返回一個int類型用來定義服務(wù)在被android系統(tǒng)終止之后的重啟方式脊框。最常用的三種常量返回值



2.在Manifest.xml文件中配置該Service

<service
        android:name="MyService"
        android:icon="@drawable/icon"
        android:label="@string/service_name"
        >
</service>

3.使用Context的startService(Intent)方法啟動該Service

Intent intent = new Intent(this, MyService.class);
startService(intent);

4.不再使用時颁督,調(diào)用stopService(Intent)方法停止該服務(wù)
.
使用這種方式創(chuàng)建啟動服務(wù)的生命周期如下圖:


注意:
1.如果服務(wù)已經(jīng)開啟,不會重復(fù)的執(zhí)行onCreate()浇雹,而是會調(diào)用onStartCommand()沉御。
2.服務(wù)停止的時候調(diào)用 onDestory()。服務(wù)只會被停止一次昭灵。

  • 使用bindService方式啟動服務(wù)
    如果您的服務(wù)僅供本地應(yīng)用使用吠裆,不需要跨進程工作,則可以實現(xiàn)自有 Binder 類烂完,讓您的客戶端通過該類直接訪問服務(wù)中的公共方法试疙。

注:此方法只有在客戶端和服務(wù)位于同一應(yīng)用和進程內(nèi)這一最常見的情況下方才有效。 例如抠蚣,對于需要將 Activity 綁定到在后臺播放音樂的自有服務(wù)的音樂應(yīng)用祝旷,此方法非常有效。

具體的創(chuàng)建服務(wù)的方法:

① 在服務(wù)中嘶窄,創(chuàng)建一個可滿足下列任一要求的 Binder 實例:
 - 包含客戶端可調(diào)用的公共方法
 - 返回當前 Service實例怀跛,其中包含客戶端可調(diào)用的公共方法
 - 返回由服務(wù)承載的其他類的實例,其中包含客戶端可調(diào)用的公共方法

② 從 onBind()回調(diào)方法返回此 Binder實例柄冲。

③ 在客戶端中吻谋,從 [onServiceConnected()](https://developer.android.com/reference/android/content/ServiceConnection.html#onServiceConnected(android.content.ComponentName, android.os.IBinder))回調(diào)方法接收 Binder,并使用提供的方法調(diào)用綁定服務(wù)现横。

例如漓拾,以下這個服務(wù)可讓客戶端通過 Binder 實現(xiàn)訪問服務(wù)中的方法:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();

    //Binder中包含返回該實例的方法
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService 
            //so clients can call public methods
            return LocalService.this;
        }
    }
    // onBind()回調(diào)方法返回此 Binder實例。
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder 為客戶端提供 getService() 方法戒祠,以檢索 LocalService 的當前實例晦攒。這樣,客戶端便可調(diào)用服務(wù)中的公共方法得哆。 例如脯颜,客戶端可調(diào)用服務(wù)中的 getRandomNumber()。

點擊按鈕時贩据,以下這個 Activity 會綁定到 LocalService 并調(diào)用 getRandomNumber():

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService栋操,建議在onstart方法中進行綁定
        Intent intent = new Intent(this, LocalService.class);
        //利用bindService(Intent, ServiceConnection, int)方法啟動該Service
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service闸餐,建議在onstop方法中進行解綁
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, 
            //then this request should occur in a separate thread 
            //to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder 
            //and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

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

特點:bind的方式開啟服務(wù),綁定服務(wù)矾芙,調(diào)用者掛了舍沙,服務(wù)也會跟著掛掉。綁定者可以調(diào)用服務(wù)里面的方法剔宪。

使用這種方式創(chuàng)建啟動服務(wù)的生命周期如下圖:


Service 元素的常見屬性

屬性 描述
android:name 服務(wù)類名
android:label 服務(wù)的名字拂铡,如果此項不設(shè)置,那么默認顯示的服務(wù)名則為類名
android:icon 服務(wù)的圖標
android:permission 申明此服務(wù)的權(quán)限葱绒,這意味著只有提供了該權(quán)限的應(yīng)用才能控制或連接此服務(wù)
android:process 表示該服務(wù)是否運行在另外一個進程感帅,如果設(shè)置了此項,那么將會在包名后面加上這段字符串表示另一進程的名字
android:enabled 如果此項設(shè)置為 true地淀,那么 Service 將會默認被系統(tǒng)啟動失球,不設(shè)置默認此項為 false
android:exported 表示該服務(wù)是否能夠被其他應(yīng)用程序所控制或連接,不設(shè)置默認此項為 false

IntentService

IntentService是繼承于Service并處理異步請求的一個類帮毁,在IntentService內(nèi)有一個工作線程來處理耗時操作实苞,啟動IntentService的方式和啟動傳統(tǒng)Service一樣,同時烈疚,當任務(wù)執(zhí)行完后黔牵,IntentService會自動停止,而不需要我們?nèi)ナ謩涌刂啤?br> 另外爷肝,可以啟動IntentService多次荧止,而每一個耗時操作會以工作隊列的方式在IntentService的onHandleIntent回調(diào)方法中執(zhí)行,并且阶剑,每次只會執(zhí)行一個工作線程,執(zhí)行完第一個再執(zhí)行第二個危号,以此類推牧愁。
那么,用IntentService有什么好處呢外莲?首先猪半,我們省去了在Service中手動開線程的麻煩,第二偷线,當操作完成時磨确,我們不用手動停止Service

下面來寫一個Demo來模擬耗時操作在IntentService中的運行過程


自定義intentService

public class MyIntentService extends IntentService {

    public static final String REUSLT = "reuslt";

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

    @Override
    protected void onHandleIntent(Intent intent) {
        String msg = intent.getStringExtra(MainActivity.COM_KEVINWANGY_TESTINTENTSERVICE_MSG);
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        msg = "get result \"" + msg + "_result\" at "
                + android.text.format.DateFormat.format("dd/MM/yy h:mm:ss aa", System.currentTimeMillis());
        Log.i("onHandleIntent", msg);

        Intent resultIntent = new Intent();
        resultIntent.setAction(MainActivity.ResultReceiver.RESULT_ACTION);
        resultIntent.putExtra(REUSLT, msg);
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this); //Service本身就是Context
        localBroadcastManager.sendBroadcast(resultIntent);
    }
}

定義MainActiviy , 其中包含一個BroadCastReceiver

public class MainActivity extends AppCompatActivity {

    public static final String COM_KEVINWANGY_TESTINTENTSERVICE_MSG = "com.kevinwangy.testintentservice.msg";
    private Button mButton;
    private TextView mStart_text;
    private TextView mFinish_text;
    private EditText mEditText;
    private String mStart_msg;
    private ResultReceiver mResultReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final StringBuilder stringBuilder = new StringBuilder();

        mStart_text = (TextView)findViewById(R.id.start_text);
        mFinish_text = (TextView)findViewById(R.id.finish_text);

        mEditText = (EditText)findViewById(R.id.input_edit);

        mButton = (Button)findViewById(R.id.input_btn);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mStart_msg = mEditText.getText().toString();
                stringBuilder.append("send message \"" + mStart_msg + "\" at "
                        + android.text.format.DateFormat.format("dd/MM/yy h:mm:ss aa", System.currentTimeMillis()) + "\n");
                mStart_text.setText(stringBuilder.toString());

                Intent intent = new Intent(MainActivity.this, MyIntentService.class);
                intent.putExtra(COM_KEVINWANGY_TESTINTENTSERVICE_MSG, mStart_msg);
                startService(intent);
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
    }

    @Override
    protected void onStop() {
        super.onStop();
    }

    public class ResultReceiver extends BroadcastReceiver {
    }
}

ResultReceiver 的定義如下

    public class ResultReceiver extends BroadcastReceiver {
        public static final String RESULT_ACTION = "com.kevinwang.testintentservice.resultReceiver";

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null && TextUtils.equals(intent.getAction(), RESULT_ACTION)) {
                String result = intent.getStringExtra(MyIntentService.REUSLT);
                Log.i("onReceive", result);
                Log.i("onReceive", "context is " + context.toString());
                mFinish_text.setText(result);
            }
        }
    }

在onstart中注冊BroadCastReceiver

    protected void onStart() {
        super.onStart();
        IntentFilter filter = new IntentFilter(ResultReceiver.RESULT_ACTION);
        mResultReceiver = new ResultReceiver();
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localBroadcastManager.registerReceiver(mResultReceiver, filter);
    }

在onstop中解除注冊

    protected void onStop() {
        super.onStop();
        LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localBroadcastManager.unregisterReceiver(mResultReceiver);
    }

在androidManifest中聲明

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name=".MyIntentService"/>

關(guān)于service、intentService声邦、thread和AsyncTask的對比

參考文檔及博客服務(wù)乏奥,綁定服務(wù)Service那點事兒

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末亥曹,一起剝皮案震驚了整個濱河市邓了,隨后出現(xiàn)的幾起案子恨诱,更是在濱河造成了極大的恐慌,老刑警劉巖骗炉,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件照宝,死亡現(xiàn)場離奇詭異,居然都是意外死亡句葵,警方通過查閱死者的電腦和手機厕鹃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來乍丈,“玉大人剂碴,你說我怎么就攤上這事∈模” “怎么了汗茄?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铭若。 經(jīng)常有香客問我洪碳,道長,這世上最難降的妖魔是什么叼屠? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任瞳腌,我火速辦了婚禮,結(jié)果婚禮上镜雨,老公的妹妹穿的比我還像新娘嫂侍。我一直安慰自己,他們只是感情好荚坞,可當我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布挑宠。 她就那樣靜靜地躺著,像睡著了一般颓影。 火紅的嫁衣襯著肌膚如雪各淀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天诡挂,我揣著相機與錄音碎浇,去河邊找鬼。 笑死璃俗,一個胖子當著我的面吹牛奴璃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播城豁,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼苟穆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鞭缭,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤剖膳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后岭辣,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吱晒,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年沦童,在試婚紗的時候發(fā)現(xiàn)自己被綠了仑濒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡偷遗,死狀恐怖墩瞳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情氏豌,我是刑警寧澤喉酌,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站泵喘,受9級特大地震影響泪电,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜纪铺,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一相速、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鲜锚,春花似錦突诬、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骏令,卻和暖如春蔬捷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伏社。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留塔淤,地道東北人摘昌。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像高蜂,于是被迫代替她去往敵國和親聪黎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容