Android Service 淺析

為什么用Service

回顧Activity

說(shuō)起Service之前先來(lái)看看四個(gè)卵胞兄弟痊臭,因?yàn)?code>ContentProvider和BroadcastReceiverService沒(méi)有太大關(guān)聯(lián)倚搬,所以重點(diǎn)看一下卵胞兄弟Activity即可丧蘸,老規(guī)矩,還是read the fucking source code開(kāi)始斗搞,代碼從startActivity開(kāi)始。

public class MainActivity extends ActionBarActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        startActivity(new Intent(this, TabActivity.class));
    }
}

上面這處代碼再熟悉不過(guò)了慷妙,我們跟下去看看究竟僻焚。

@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

繼續(xù)跟下去會(huì)進(jìn)入到接口IActivityManager

public interface IActivityManager extends IInterface {
    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags,
            ProfilerInfo profilerInfo, Bundle options) throws RemoteException;
}

進(jìn)入到實(shí)現(xiàn)類ActivityManagerNative

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
        String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    data.writeString(callingPackage);
    intent.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeStrongBinder(resultTo);
    data.writeString(resultWho);
    data.writeInt(requestCode);
    data.writeInt(startFlags);
    if (profilerInfo != null) {
        data.writeInt(1);
        profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        data.writeInt(0);
    }
    if (options != null) {
        data.writeInt(1);
        options.writeToParcel(data, 0);
    } else {
        data.writeInt(0);
    }
    mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
    reply.readException();
    int result = reply.readInt();
    reply.recycle();
    data.recycle();
    return result;
}

這個(gè)方法簡(jiǎn)單分析一下,L27之前都是數(shù)據(jù)在跨進(jìn)程之前需要進(jìn)行打包處理景殷,然后L27調(diào)用transact方法溅呢,進(jìn)入NDK方法澡屡,最后從replay中取得返回結(jié)果。

重回Service

上面跟了這么多代碼咐旧,最重要的一點(diǎn)就是Activity組件的跳轉(zhuǎn)是完全解耦的驶鹉,說(shuō)的更直接一點(diǎn)是只需要Context上下文即可完成跳轉(zhuǎn),意即無(wú)論再任何場(chǎng)景下铣墨,只要有Context上下文室埋,想怎么跳轉(zhuǎn)就怎么跳轉(zhuǎn),這么好的特性伊约,是不是在剩下的四個(gè)卵胞兄弟中也有這樣的特性姚淆?帶著這個(gè)疑問(wèn)我們繼續(xù)跟一下startService

public class MainActivity extends ActionBarActivity
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        startService(new Intent(this, TabService.class));
    }
}

跟下去,進(jìn)入ContextWrapper

@Override
public ComponentName startService(Intent service) {
    return mBase.startService(service);
}

進(jìn)入實(shí)現(xiàn)類ContextImpl

@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, mUser);
}

第一行就是個(gè)warning屡律,第二行繼續(xù)跟下去進(jìn)入ActivityManagerProxy代理類

public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, int userId) throws RemoteException
{
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    data.writeInterfaceToken(IActivityManager.descriptor);
    data.writeStrongBinder(caller != null ? caller.asBinder() : null);
    service.writeToParcel(data, 0);
    data.writeString(resolvedType);
    data.writeInt(userId);
    mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
    reply.readException();
    ComponentName res = ComponentName.readFromParcel(reply);
    data.recycle();
    reply.recycle();
    return res;
}

這個(gè)方法整體上是不是似曾相識(shí)腌逢,里面的具體流程就不作贅述了。
通過(guò)上面的源碼對(duì)比超埋,發(fā)現(xiàn)Service和Activity如此的想象搏讶,那既然Service和Activity這么像,為啥還多此一舉多弄出來(lái)一個(gè)Service干什么霍殴?原因其實(shí)很簡(jiǎn)單媒惕,因?yàn)榻怦睿钥梢钥鐖?chǎng)景来庭,然后就有一堆理由需要使用Service了妒蔚,比如:

一些應(yīng)用程序,始終需要與服務(wù)器之間始終保持著心跳連接月弛,就可以使用Service來(lái)實(shí)現(xiàn)肴盏。即使Activity被銷毀,或者程序被關(guān)閉尊搬,只要進(jìn)程還在叁鉴,Service就可以繼續(xù)運(yùn)行。而且重要的是這個(gè)Service可以被任何可以獲取Context的地方控制

接下來(lái)我們說(shuō)一下如何控制Service佛寿,由前面可知幌墓,只要獲取Context就可以對(duì)Service為所欲為了,以下為實(shí)例代碼

public void onClick(View v) {
    switch (v.getId()) {
    case R.id.start_service:
        Intent startIntent = new Intent(this, MyService.class);
        startService(startIntent);
        break;
    case R.id.stop_service:
        Log.d("MyService", "click Stop Service button");
        Intent stopIntent = new Intent(this, MyService.class);
        stopService(stopIntent);
        break;
    case R.id.bind_service:
        Intent bindIntent = new Intent(this, MyService.class);
        bindService(bindIntent, connection, BIND_AUTO_CREATE);
        break;
    case R.id.unbind_service:
        Log.d("MyService", "click Unbind Service button");
        unbindService(connection);
        break;
    default:
        break;
    }
}

這里需要注意一點(diǎn)冀泻,控制總是成對(duì)出現(xiàn)常侣,比如:
第一種情況

startService
stopService

第二種情況

bindService
unbindService

第三種情況

startService
bindService
unbindService
stopService

Service和Thread以及Process區(qū)別

不少Android初學(xué)者都可能會(huì)有這樣的疑惑,Service和Thread到底有什么關(guān)系呢弹渔?什么時(shí)候應(yīng)該用Service胳施,什么時(shí)候又應(yīng)該用Thread?答案可能會(huì)有點(diǎn)讓你吃驚肢专,因?yàn)镾ervice和Thread之間沒(méi)有任何關(guān)系舞肆!
之所以有不少人會(huì)把它們聯(lián)系起來(lái)焦辅,主要就是因?yàn)镾ervice的后臺(tái)概念。Thread我們大家都知道椿胯,是用于開(kāi)啟一個(gè)子線程筷登,在這里去執(zhí)行一些耗時(shí)操作就不會(huì)阻塞主線程的運(yùn)行。而Service我們最初理解的時(shí)候哩盲,總會(huì)覺(jué)得它是用來(lái)處理一些后臺(tái)任務(wù)的前方,一些比較耗時(shí)的操作也可以放在這里運(yùn)行,這就會(huì)讓人產(chǎn)生混淆了廉油。但是惠险,如果我告訴你Service其實(shí)是運(yùn)行在主線程里的,你還會(huì)覺(jué)得它和Thread有什么關(guān)系嗎抒线?

這里不再做實(shí)驗(yàn)去驗(yàn)證了班巩,如果你感興趣的話可以自己去驗(yàn)證。所以說(shuō)白了十兢,Service既不是線程更不是進(jìn)程趣竣,他只是Android提供給我方便使用的組件而已,千萬(wàn)別聯(lián)想多了旱物。

本地Service

Service根據(jù)功能不同,可以分為本地Service和遠(yuǎn)程Service卫袒,當(dāng)然還有前臺(tái)Service和后臺(tái)Service宵呛,后臺(tái)Service見(jiàn)怪不怪,那么前臺(tái)Service是什么鬼夕凝?這里僅僅舉個(gè)例子宝穗,不做深入,比如墨跡天氣码秉,默認(rèn)顯示在通知欄里面逮矛,那么如何做到的,你可以Google一下列子很多转砖,我們回歸本地和遠(yuǎn)程Service须鼎,有些人可能會(huì)聯(lián)想很多,其實(shí)這個(gè)本地和遠(yuǎn)程是相對(duì)進(jìn)程來(lái)說(shuō)的府蔗,在同一個(gè)進(jìn)程里就是本地Service晋控,不在同一個(gè)進(jìn)程里就是遠(yuǎn)程Service。我們知道不同進(jìn)程間通信和相同進(jìn)程間通信有著很大區(qū)別姓赤,最直白的表象就是復(fù)雜程度不同赡译,我們先看本地Service如何通信。
[引用代碼]

public class MyService extends Service {

    public static final String TAG = "MyService";

    private MyBinder mBinder = new MyBinder();

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate() executed");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand() executed");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy() executed");
    }

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

    class MyBinder extends Binder {

        public void startDownload() {
            Log.d("TAG", "startDownload() executed");
            // 執(zhí)行具體的下載任務(wù)
        }
    }
}

注意這里MyBinder類繼承自Binder類不铆,在其中添加了一個(gè)startDownload()公有方法用于在后臺(tái)執(zhí)行下載任務(wù)蝌焚。然后這個(gè)類的實(shí)例mBinder在重載方法onBind中作為返回值返回裹唆,這個(gè)實(shí)例就是Activity和Service之間聯(lián)系最為緊密的橋梁。

public class MainActivity extends Activity implements OnClickListener {
    private Button bindService;
    private Button unbindService;

    private MyService.MyBinder myBinder;
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myBinder = (MyService.MyBinder) service;
            myBinder.startDownload();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService = (Button) findViewById(R.id.bind_service);
        unbindService = (Button) findViewById(R.id.unbind_service);
        bindService.setOnClickListener(this);
        unbindService.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.bind_service:
            Intent bindIntent = new Intent(this, MyService.class);
            bindService(bindIntent, connection, BIND_AUTO_CREATE);
            break;
        case R.id.unbind_service:
            unbindService(connection);
            break;
        default:
            break;
        }
    }
}

[此處引用]

可以看到只洒,這里我們首先創(chuàng)建了一個(gè)ServiceConnection的匿名類许帐,在里面重寫(xiě)了onServiceConnected()方法和onServiceDisconnected()方法,這兩個(gè)方法分別會(huì)在Activity與Service建立關(guān)聯(lián)和解除關(guān)聯(lián)的時(shí)候調(diào)用红碑。在onServiceConnected()方法中舞吭,我們又通過(guò)向下轉(zhuǎn)型得到了MyBinder的實(shí)例,有了這個(gè)實(shí)例析珊,Activity和Service之間的關(guān)系就變得非常緊密了羡鸥。

現(xiàn)在我們可以在Activity中根據(jù)具體的場(chǎng)景來(lái)調(diào)用MyBinder中的任何public方法,即實(shí)現(xiàn)了Activity指揮Service干什么Service就去干什么的功能忠寻。

當(dāng)然惧浴,現(xiàn)在Activity和Service其實(shí)還沒(méi)關(guān)聯(lián)起來(lái)了呢,這個(gè)功能是在Bind Service按鈕的點(diǎn)擊事件里完成的奕剃≈月茫可以看到,這里我們?nèi)匀皇菢?gòu)建出了一個(gè)Intent對(duì)象纵朋,然后調(diào)用bindService()方法將Activity和Service進(jìn)行綁定柿顶。bindService()方法接收三個(gè)參數(shù),第一個(gè)參數(shù)就是剛剛構(gòu)建出的Intent對(duì)象操软,第二個(gè)參數(shù)是前面創(chuàng)建出的ServiceConnection的實(shí)例嘁锯,第三個(gè)參數(shù)是一個(gè)標(biāo)志位,這里傳入BIND_AUTO_CREATE表示在Activity和Service建立關(guān)聯(lián)后自動(dòng)創(chuàng)建Service聂薪,這會(huì)使得MyService中的onCreate()方法得到執(zhí)行家乘,但onStartCommand()方法不會(huì)執(zhí)行。

然后如何我們想解除Activity和Service之間的關(guān)聯(lián)怎么辦呢藏澳?調(diào)用一下unbindService()方法就可以了仁锯,這也是Unbind Service按鈕的點(diǎn)擊事件里實(shí)現(xiàn)的邏輯。

遠(yuǎn)程Service

首先我們比較關(guān)系如何實(shí)現(xiàn)遠(yuǎn)程Service

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.servicetest"
    android:versionCode="1"
    android:versionName="1.0" >

    ......
    
    <service
        android:name="com.example.servicetest.MyService"
        android:process=":remote" >
    </service>

</manifest>

通過(guò)上面的配置文件翔悠,我們知道應(yīng)該是Service和APP從屬于不同的進(jìn)程业崖,然后比較復(fù)雜的就是進(jìn)程間通信了。

那么如何才能讓Activity與一個(gè)遠(yuǎn)程Service建立關(guān)聯(lián)呢凉驻?這就要使用AIDL來(lái)進(jìn)行跨進(jìn)程通信了(IPC)腻要。
AIDL(Android Interface Definition Language)是Android接口定義語(yǔ)言的意思,它可以用于讓某個(gè)Service與多個(gè)應(yīng)用程序組件之間進(jìn)行跨進(jìn)程通信涝登,從而可以實(shí)現(xiàn)多個(gè)應(yīng)用程序共享同一個(gè)Service的功能雄家。

接下來(lái)不準(zhǔn)備詳細(xì)些如何實(shí)現(xiàn)進(jìn)程間通信了,因?yàn)锳ndroid SDK里面有很多例子可以參考胀滚,另外如果對(duì)AIDL一點(diǎn)也不熟悉趟济,可以通過(guò)Android Service完全解析乱投,關(guān)于服務(wù)你所需知道的一切(下)這篇文章了解一下.
另外還有一篇博客寫(xiě)的不錯(cuò)Android 中的 Service 全面總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顷编,一起剝皮案震驚了整個(gè)濱河市戚炫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌媳纬,老刑警劉巖双肤,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異钮惠,居然都是意外死亡茅糜,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)素挽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蔑赘,“玉大人,你說(shuō)我怎么就攤上這事预明∷跞” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵撰糠,是天一觀的道長(zhǎng)酥馍。 經(jīng)常有香客問(wèn)我,道長(zhǎng)阅酪,這世上最難降的妖魔是什么物喷? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮遮斥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扇丛。我一直安慰自己术吗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布帆精。 她就那樣靜靜地躺著较屿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卓练。 梳的紋絲不亂的頭發(fā)上隘蝎,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音襟企,去河邊找鬼嘱么。 笑死,一個(gè)胖子當(dāng)著我的面吹牛顽悼,可吹牛的內(nèi)容都是我干的曼振。 我是一名探鬼主播几迄,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼冰评!你這毒婦竟也來(lái)了映胁?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤甲雅,失蹤者是張志新(化名)和其女友劉穎解孙,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體抛人,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡弛姜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了函匕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娱据。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盅惜,靈堂內(nèi)的尸體忽然破棺而出中剩,到底是詐尸還是另有隱情,我是刑警寧澤抒寂,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布结啼,位于F島的核電站,受9級(jí)特大地震影響屈芜,放射性物質(zhì)發(fā)生泄漏郊愧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一井佑、第九天 我趴在偏房一處隱蔽的房頂上張望属铁。 院中可真熱鬧,春花似錦躬翁、人聲如沸焦蘑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)例嘱。三九已至,卻和暖如春宁舰,著一層夾襖步出監(jiān)牢的瞬間拼卵,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工蛮艰, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腋腮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像低葫,于是被迫代替她去往敵國(guó)和親详羡。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

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