首页 > 程序开发 > 移动开发 > Android >

Android多线程开发之IntentService的使用

2016-09-24

Android多线程开发之IntentService的使用

一、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本身就是一个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()自行销毁,消息队列也会自行退出。

相关文章
最新文章
热点推荐