Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android多線程開發之IntentService的使用

Android多線程開發之IntentService的使用

編輯:關於Android編程

一、IntentService簡介

總結下來就是:

- IntentService是Service類的子類,用來處理異步請求

- 客戶端可以通過startService(Intent)方法傳遞請求給IntentService

- IntentService單獨開啟了一個線程來處理所有的Intent請求所對應的任務,以免事務處理阻塞主線程,而且任務是按先後順序逐個進行處理的

- 當IntentService處理完所有的任務後,它會在適當的時候自動結束服務

二、IntentService使用步驟

繼承IntentService,實現構造方法和onHandleIntent()方法; 在Manifest文件中注冊自己的IntentService類; 創建任務請求並發送到IntentService進行處理; 通過IntentService向其它組件發送任務處理的結果; 在接收的組件中進行後續處理。

三、IntentService使用實例

下面是一個模擬圖片上傳的demo。

ImgUploadService.java類:

public class ImgUploadService extends IntentService {
    public static final String TAG = "ImgUploadService";

    private static final String ACTION_UPLOAD_IMG = "com.demo.service.action.UPLOAD_IMAGE";
    public static final String EXTRA_IMG_PATH = "com.demo.service.extra.IMG_PATH";

    public ImgUploadService(String name) {
        super(name);
        Log.d(TAG, "ImgUploadService[" + " ThreadName: " + name + " ]");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "onHandleIntent");
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_UPLOAD_IMG.equals(action)) {
                final String path = intent.getStringExtra(EXTRA_IMG_PATH);
                handleUploadImg(path);
            }
        }
    }

    private void handleUploadImg(String path) {
        try {
            Thread.sleep(3000); //模擬上傳耗時
            Intent intent = new Intent(IntentServiceActivity.UPLOAD_RESULT);
            intent.putExtra(EXTRA_IMG_PATH, path);
            LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void startUploadImg(Context context, String path) {
        Intent intent = new Intent(context, ImgUploadService.class);
        intent.setAction(ACTION_UPLOAD_IMG);
        intent.putExtra(EXTRA_IMG_PATH, path);
        context.startService(intent);
    }

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

IntentServiceActivity.java類:

public class IntentServiceActivity extends Activity {
    public static final String UPLOAD_RESULT = "com.demo.service.UPLOAD_RESULT";

    private LinearLayout mLlContainer;
    private TextView mBtnUpload;
    int i = 0;

    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        setContentView(R.layout.activity_main_handlerthread);
        mLlContainer = (LinearLayout) findViewById(R.id.ll_container);
        mBtnUpload = (TextView) findViewById(R.id.btn_upload);
        registerReceiver();

        mBtnUpload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addTask(); //模擬上傳
            }
        });
    }

    private void registerReceiver() {
        IntentFilter filter = new IntentFilter();
        filter.addAction(UPLOAD_RESULT);
        LocalBroadcastManager.getInstance(this).registerReceiver(mUploadImgReceiver, filter);
    }

    public void addTask() {
        String path = "圖片" + i++ + ".png";
        ImgUploadService.startUploadImg(this, path);

        TextView tv = new TextView(this);
        mLlContainer.addView(tv);
        tv.setText(path + " ....正在上傳中....");
        tv.setTag(path);
    }

    private void handleResult(String path) {
        TextView tv = (TextView) mLlContainer.findViewWithTag(path);
        tv.setText(path + "  ----上傳成功---- ");
    }

    private BroadcastReceiver mUploadImgReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(UPLOAD_RESULT)) {
                String path = intent.getStringExtra(ImgUploadService.EXTRA_IMG_PATH);
                handleResult(path);
            }
        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mUploadImgReceiver);
    }
}

activity_main.xml文件:

四、IntentService源碼解析

IntentService本身就是一個Service,它擁有Service的所有生命周期方法,但內部是通過HandlerThread實現異步執行任務的,HandlerThread是一個內部維護了一個消息隊列的線程。

既然IntentService有生命周期,那麼就從它的構造函數看起:

/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 *
 * @param name Used to name the worker thread, important only for debugging.
 */
public IntentService(String name) {
    super();
    mName = name;
}

構造方法很簡單,用於創建一個IntentService對象,參數namne用於定義工作線程的名字,僅用於調試作用。接下來看下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對象,並傳入工作線程的名字
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    // 開啟後台工作線程
    thread.start();

    // 獲取後台工作線程的Looper對象
    mServiceLooper = thread.getLooper();
    // 創建一個ServiceHandler對象,用來處理異步消息。
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

在onCreate()方法裡,首先利用HandlerThread類創建了一個循環的工作線程,接著獲取工作線程中的Looper對象並將其作為參數創建了一個叫ServiceHandler的類,該類是IntentService的內部類,該類繼承了Handler,我們看它的定義:

public abstract class IntentService extends Service {
    //volatile關鍵字保證變量每次在使用的時候,都從主存中取。而不是從各個線程的“工作內存”中讀取
    private volatile Looper mServiceLooper;//
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            //一收到消息就回調到onHandleIntent()中進行處理
            onHandleIntent((Intent)msg.obj);
            //處理完消息就調用stopSelf()方法,並傳入消息的索引值
            stopSelf(msg.arg1);
        }
    }
}

從源碼中可以看到,mServiceLooper和mServiceHandler都加了volatile關鍵字修飾,這是為了保證變量在每次使用的時候都從主內存中讀取,而不是從各個線程的“工作內存”中讀取,也就是說保證內存的可見性。

接著看onHandleIntent()方法,該方法是在ServiceHandler收到消息後回調的,也即onHandleIntent()方法是在HandlerThread工作線程中執行的,它是一個抽象方法,留給調用者去實現耗時任務的。

/**
* This method is invoked on the worker thread with a request to process.
 * Only one Intent is processed at a time, but the processing happens on a
 * worker thread that runs independently from other application logic.
 * So, if this code takes a long time, it will hold up other requests to
 * the same IntentService, but it will not hold up anything else.
 * When all requests have been handled, the IntentService stops itself,
 * so you should not call {@link #stopSelf}.
 *
 * @param intent The value passed to {@link
 *               android.content.Context#startService(Intent)}.
 */
protected abstract void onHandleIntent(Intent intent);

此外,因為任務是通過ServiceHandler發送給異步線程的消息隊列來處理的,而消息隊列裡的消息是依次取出並執行的,所以從這裡可以了解到任務必定是串行執行的。而執行完一個消息後,調用了一個帶有startId參數的stopSelf()方法:

** * Old version of {@link #stopSelfResult} that doesn't return a result. * * @see #stopSelfResult */
public final void stopSelf(int startId) {
    if (mActivityManager == null) {
        return;
    }
    try {
        mActivityManager.stopServiceToken(
                new ComponentName(this, mClassName), mToken, startId);
    } catch (RemoteException ex) {
    }
}

該方法實際上是通過調用ActivityManager的stopServiceToken()方法來停止當前服務的,不過服務不會馬上停止,而是等待繼續完成剩下的任務後才自動結束,因為方法參數裡攜帶了一個startId,它可以看做是一個請求的唯一標識,只有當所有的請求都結束,我們的Service才自行銷毀,而startId是從onStartCommand()裡被調用的,我們看下它的源碼:

/**
 * 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;
}

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

對於Service,每次調用startServcie方法啟動一個服務,都會調用一次onStartCommand()方法,每次也會帶一個startId過去,如果執行多個任務,那麼就會存在多個startId,然後在onStart()方法中通過mServiceHandler獲得一個消息對象msg,然後將startId作為該消息的消息碼,將異步任務請求intent作為消息內容封裝成一個消息msg發送到mServiceHandler中進行異步處理,最後回調到onHandleIntent()中,子類通過實現onHandleIntent()即可進行異步耗時任務的執行。

這裡注意,我們不需要自己去重寫onStartCommand()和onStart()方法,但onHandleIntent()必須重寫。

當所有的startId都處理完後,IntentService會調用onDestroy()自行銷毀,消息隊列也會自行退出。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved