Android IntentService

特點(diǎn)

  • 它本質(zhì)是一種特殊的Service,繼承自Service并且本身就是一個(gè)抽象類
  • 它可以用于在后臺執(zhí)行耗時(shí)的異步任務(wù)鸥印,當(dāng)任務(wù)完成后會自動停止
  • 它擁有較高的優(yōu)先級书闸,不易被系統(tǒng)殺死(繼承自Service的緣故)翩概,因此比較適合執(zhí)行一些高優(yōu)先級的異步任務(wù)
  • 它內(nèi)部通過HandlerThread和Handler實(shí)現(xiàn)異步操作
  • 創(chuàng)建IntentService時(shí),只需實(shí)現(xiàn)onHandleIntent和構(gòu)造方法,onHandleIntent為異步方法臭增,可以執(zhí)行耗時(shí)操作

使用實(shí)例

public  class MyIntentService extends IntentService {
    public static final String DOWNLOAD_URL="download_url";
    public static final String INDEX_FLAG="index_flag";
    public static UpdateUI updateUI;


    public static void setUpdateUI(UpdateUI updateUIInterface){
        updateUI=updateUIInterface;
    }

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

    /**
     * 實(shí)現(xiàn)異步任務(wù)的方法
     * @param intent Activity傳遞過來的Intent,數(shù)據(jù)封裝在intent中
     */
    @Override
    protected void onHandleIntent(Intent intent) {

        //在子線程中進(jìn)行網(wǎng)絡(luò)請求
        Bitmap bitmap=downloadUrlBitmap(intent.getStringExtra(DOWNLOAD_URL));
        Message msg1 = new Message();
        msg1.what = intent.getIntExtra(INDEX_FLAG,0);
        msg1.obj =bitmap;
        //通知主線程去更新UI
        if(updateUI!=null){
            updateUI.updateUI(msg1);
        }
        //mUIHandler.sendMessageDelayed(msg1,1000);

        LogUtils.e("onHandleIntent");
    }
    //----------------------重寫一下方法僅為測試------------------------------------------
    @Override
    public void onCreate() {
        LogUtils.e("onCreate");
        super.onCreate();
    }

    @Override
    public void onStart(Intent intent, int startId) {
        super.onStart(intent, startId);
        LogUtils.e("onStart");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtils.e("onStartCommand");
        return super.onStartCommand(intent, flags, startId);

    }

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

    @Override
    public IBinder onBind(Intent intent) {
        LogUtils.e("onBind");
        return super.onBind(intent);
    }


    public interface UpdateUI{
        void updateUI(Message message);
    }


    private Bitmap downloadUrlBitmap(String urlString) {
        HttpURLConnection urlConnection = null;
        BufferedInputStream in = null;
        Bitmap bitmap=null;
        try {
            final URL url = new URL(urlString);
            urlConnection = (HttpURLConnection) url.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
            bitmap= BitmapFactory.decodeStream(in);
        } catch (final IOException e) {
            e.printStackTrace();
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (final IOException e) {
                e.printStackTrace();
            }
        }
        return bitmap;
    }

}

通過代碼可以看出横辆,我們繼承了IntentService撇他,這里有兩個(gè)方法是必須實(shí)現(xiàn)的,一個(gè)是構(gòu)造方法狈蚤,必須傳遞一個(gè)線程名稱的字符串逆粹,另外一個(gè)就是進(jìn)行異步處理的方法onHandleIntent(Intent intent) 方法,其參數(shù)intent可以附帶從activity傳遞過來的數(shù)據(jù)炫惩。這里我們的案例主要利用onHandleIntent實(shí)現(xiàn)異步下載圖片僻弹,然后通過回調(diào)監(jiān)聽的方法把下載完的bitmap放在message中回調(diào)給Activity(當(dāng)然也可以使用廣播完成),最后通過Handler去更新UI他嚷。下面再來看看Acitvity的代碼:

public class IntentServiceActivity extends Activity implements MyIntentService.UpdateUI{
    /**
     * 圖片地址集合
     */
    private String url[] = {
            "http://img.blog.csdn.net/20160903083245762",
            "http://img.blog.csdn.net/20160903083252184",
            "http://img.blog.csdn.net/20160903083257871",
            "http://img.blog.csdn.net/20160903083257871",
            "http://img.blog.csdn.net/20160903083311972",
            "http://img.blog.csdn.net/20160903083319668",
            "http://img.blog.csdn.net/20160903083326871"
    };

    private static ImageView imageView;
    private static final Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            imageView.setImageBitmap((Bitmap) msg.obj);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_intent_service);
        imageView = (ImageView) findViewById(R.id.image);

        Intent intent = new Intent(this,MyIntentService.class);
        for (int i=0;i<7;i++) {//循環(huán)啟動任務(wù)
            intent.putExtra(MyIntentService.DOWNLOAD_URL,url[i]);
            intent.putExtra(MyIntentService.INDEX_FLAG,i);
            startService(intent);
        }
        MyIntentService.setUpdateUI(this);
    }

    //必須通過Handler去更新蹋绽,該方法為異步方法,不可更新UI
    @Override
    public void updateUI(Message message) {
        mUIHandler.sendMessageDelayed(message,message.what * 1000);
    }
}

代碼比較簡單筋蓖,通過for循環(huán)多次去啟動IntentService卸耘,然后去下載圖片,注意即使我們多次啟動IntentService粘咖,但I(xiàn)ntentService的實(shí)例只有一個(gè)蚣抗,這跟傳統(tǒng)的Service是一樣的,最終IntentService會去調(diào)用onHandleIntent執(zhí)行異步任務(wù)瓮下。這里可能我們還會擔(dān)心for循環(huán)去啟動任務(wù)翰铡,而實(shí)例又只有一個(gè),那么任務(wù)會不會被覆蓋掉呢讽坏?其實(shí)是不會的锭魔,因?yàn)镮ntentService真正執(zhí)行異步任務(wù)的是HandlerThread+Handler,每次啟動都會把下載圖片的任務(wù)添加到依附的消息隊(duì)列中路呜,最后由HandlerThread+Handler去執(zhí)行.

源碼解析

IntentService的onCreate方法:

@Override
public void onCreate() {
   // TODO: It would be nice to have an option to hold a partial wakelock
   // during processing, and to have a static startService(Context, Intent)
   // method that would launch the service & hand off a wakelock.

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

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

當(dāng)?shù)谝粏覫ntentService時(shí)迷捧,它的onCreate方法將會被調(diào)用,其內(nèi)部會去創(chuàng)建一個(gè)HandlerThread并啟動它胀葱,接著創(chuàng)建一個(gè)ServiceHandler(繼承Handler)漠秋,傳入HandlerThread的Looper對象,這樣ServiceHandler就變成可以處理異步線程的執(zhí)行類了(因?yàn)長ooper對象與HandlerThread綁定抵屿,而HandlerThread又是一個(gè)異步線程庆锦,我們把HandlerThread持有的Looper對象傳遞給Handler后,ServiceHandler內(nèi)部就持有異步線程的Looper晌该,自然就可以執(zhí)行異步任務(wù)了)肥荔,那么IntentService是怎么啟動異步任務(wù)的呢绿渣?其實(shí)IntentService啟動后還會去調(diào)用onStartCommand方法,而onStartCommand方法又會去調(diào)用onStart方法燕耿,我們看看它們的源碼:

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

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

從源碼我們可以看出中符,在onStart方法中,IntentService通過mServiceHandler的sendMessage方法發(fā)送了一個(gè)消息誉帅,這個(gè)消息將會發(fā)送到HandlerThread中進(jìn)行處理(因?yàn)镠andlerThread持有Looper對象淀散,所以其實(shí)是Looper從消息隊(duì)列中取出消息進(jìn)行處理,然后調(diào)用mServiceHandler的handleMessage方法)蚜锨,我們看看ServiceHandler的源碼:

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);
   }
}

這里其實(shí)也說明onHandleIntent確實(shí)是一個(gè)異步處理方法(ServiceHandler本身就是一個(gè)異步處理的handler類)档插,在onHandleIntent方法執(zhí)行結(jié)束后,IntentService會通過 stopSelf(int startId)方法來嘗試停止服務(wù)亚再。這里采用stopSelf(int startId)而不是stopSelf()來停止服務(wù)郭膛,是因?yàn)閟topSelf()會立即停止服務(wù),而stopSelf(int startId)會等待所有消息都處理完后才終止服務(wù)氛悬。最后看看onHandleIntent方法的聲明:

protected abstract void onHandleIntent(Intent intent);

到此我們就知道了IntentService的onHandleIntent方法是一個(gè)抽象方法则剃,所以我們在創(chuàng)建IntentService時(shí)必須實(shí)現(xiàn)該方法,通過上面一系列的分析可知如捅,onHandleIntent方法也是一個(gè)異步方法棍现。這里要注意的是如果后臺任務(wù)只有一個(gè)的話,onHandleIntent執(zhí)行完镜遣,服務(wù)就會銷毀己肮,但如果后臺任務(wù)有多個(gè)的話,onHandleIntent執(zhí)行完最后一個(gè)任務(wù)時(shí)悲关,服務(wù)才銷毀谎僻。最后我們要知道每次執(zhí)行一個(gè)后臺任務(wù)就必須啟動一次IntentService,而IntentService內(nèi)部則是通過消息的方式發(fā)送給HandlerThread的坚洽,然后由Handler中的Looper來處理消息戈稿,而Looper是按順序從消息隊(duì)列中取任務(wù)的,也就是說IntentService的后臺任務(wù)時(shí)順序執(zhí)行的讶舰,當(dāng)有多個(gè)后臺任務(wù)同時(shí)存在時(shí),這些后臺任務(wù)會按外部調(diào)用的順序排隊(duì)執(zhí)行需了,我們前面的使用案例也很好說明了這點(diǎn)跳昼。

參考

http://blog.csdn.net/javazejian/article/details/52426425

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肋乍,隨后出現(xiàn)的幾起案子鹅颊,更是在濱河造成了極大的恐慌,老刑警劉巖墓造,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堪伍,死亡現(xiàn)場離奇詭異锚烦,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)帝雇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門涮俄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人尸闸,你說我怎么就攤上這事彻亲。” “怎么了吮廉?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵苞尝,是天一觀的道長。 經(jīng)常有香客問我宦芦,道長宙址,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任调卑,我火速辦了婚禮抡砂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘令野。我一直安慰自己舀患,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布气破。 她就那樣靜靜地躺著聊浅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪现使。 梳的紋絲不亂的頭發(fā)上低匙,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機(jī)與錄音碳锈,去河邊找鬼顽冶。 笑死,一個(gè)胖子當(dāng)著我的面吹牛售碳,可吹牛的內(nèi)容都是我干的强重。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼贸人,長吁一口氣:“原來是場噩夢啊……” “哼间景!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起艺智,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤倘要,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后十拣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體封拧,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡志鹃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泽西。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曹铃。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖尝苇,靈堂內(nèi)的尸體忽然破棺而出铛只,到底是詐尸還是另有隱情,我是刑警寧澤糠溜,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布淳玩,位于F島的核電站,受9級特大地震影響非竿,放射性物質(zhì)發(fā)生泄漏蜕着。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一红柱、第九天 我趴在偏房一處隱蔽的房頂上張望承匣。 院中可真熱鬧,春花似錦锤悄、人聲如沸韧骗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽袍暴。三九已至,卻和暖如春隶症,著一層夾襖步出監(jiān)牢的瞬間政模,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工蚂会, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淋样,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓胁住,卻偏偏與公主長得像趁猴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子彪见,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

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