前言:
在工程的开发中,想必大家都会普遍用到Service组件。由于Service的生命周期较长,因此Service实现了两种类型的特性:
(1)在不与用户进行交互的情况下,完成应用程序指定的耗时的操作。
(2)提供了一定的功能和接口供其他应用程序调用,满足应用和应用之间的交互。
但是由于Service的运行依然是在用户线程(即主线程),因此如果做了过于耗时的操作的话,则可能会引起ANR(Android Not Responding)的情况。所以关于耗时的操作都需要放在子线程中去做。基于这种情况,Android自身也提供了关于Service的衍生类IntentService。IntentService保留了Service原有的特性,并且将耗时的操作都放在的子线程中,通过Handler的回调方式实现了数据的返回。接下来,我们通过源码和实例了解IntentService。
源码分析
1. Looper、ServiceHandler
首先,通过Android源码提供的注释我们了解到:
(1)IntentService继承Service,专门处理异步请求。
(2)客户端通过调用startService(Intent)发起请求,自然数据是通过Intent进行传递的。
(3)一旦Service启动后,对于Intent所传递的数据操作都通过工作线程(worker thread)进行处理。
(4)在完成数据的处理之后,Handler会回调其处理结果。在任务结束后会将自身服务关闭。
// 用于循环消息的Looper
private volatile Looper mServiceLooper;
// 用于处理消息的Handler
private volatile ServiceHandler mServiceHandler;
// 工作线程的名称,只用于debug
private String mName;
// 是否使用Intent重发送机制
private boolean mRedelivery;
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);
}
}
通过以上的源码我们可以看出,IntentService首先声明了Looper和Handler这两个变量,目的是想通过消息机制来管理工作线程执行完成后回调回来的数据。并且在ServiceHandler的实现中,我们也能够看到在handleMessage()的方法回调中,stopSelf()会在主线程消息接收完成后调用。
2. 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);
}
IntentService重写了onCreate()方法,除了实现了父类的方法以外,在代码中通过实现HandlerThread来建立一个独立子线程,并且通过HandlerThread的实例化来获得Looper的实例,并且通过Looper与本类中的ServiceHandler关联。
3. onStart()、onStartCommand()
@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;
}
在onStartCommand()方法中实现了消息分发。
4. onDestroy()
@Override
public void onDestroy() {
mServiceLooper.quit();
}
在onDestroy()中looper结束对消息的循环并退出。
以上流程即是IntentService的源码实现流程,其实说白了就是通过Handler、Message、Looper在Service中实现的异步线程消息处理的机制。但是由于是通过衍生Service的方式实现的,因此具有Service的生命周期特性。
代码示例
示例模拟的是从本地读取文件,然后上传到服务器的例子。
1. MyIntentService
public class MyIntentService extends IntentService {
private static final String TAG = "MyIntentService";
public MyIntentService(String name) {
super(name);
}
public MyIntentService() {
super("MyIntentService");
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: 创建Service");
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
String action = intent.getAction();
if (TextUtils.equals(Constants.ACTION_UPLOAD_IMG, action)) {
String path = intent.getStringExtra(Constants.EXTRA_IMG_PATH);
handleUpload(path);
}
}
}
/**
* 处理上传操作
*
* @param path
*/
private void handleUpload(String path) {
try {
// 模拟网络请求的上传逻辑
Thread.sleep(3000);
Intent intent = new Intent(Constants.UPLOAD_RESULT);
intent.putExtra(Constants.EXTRA_IMG_PATH, path);
LocalBroadcastManagerUtils.getInstance(getApplicationContext()).sendBroadcast(intent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 启动服务
*
* @param context
* @param path
*/
public static void startIntentService(Context context, String path) {
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(Constants.ACTION_UPLOAD_IMG);
intent.putExtra(Constants.EXTRA_IMG_PATH, path);
context.startService(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: 销毁Service");
}
}
首先,创建一个MyIntentService并继承Service类,并实现onHandleIntent()方法。在onHandleIntent()方法中,实现上传操作的逻辑。并且在完成上传逻辑的同时,由广播的方式通知页面进行数据刷新操作。(在本例中,我使用的是局部广播。替换成普通的广播也可以实现。)
2. MainActivity
public class MainActivity extends Activity implements View.OnClickListener {
private LinearLayout llContainer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initReceiver();
setContentView(R.layout.activity_main);
llContainer = (LinearLayout) findViewById(R.id.ll_container);
findViewById(R.id.btn_upload).setOnClickListener(this);
}
private void initReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Constants.UPLOAD_RESULT);
LocalBroadcastManagerUtils.getInstance(this).register(receiver, filter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_upload:
addTask();
break;
default:
break;
}
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Constants.UPLOAD_RESULT)) {
String path = intent.getStringExtra(Constants.EXTRA_IMG_PATH);
TextView tv = (TextView) llContainer.findViewWithTag(path);
tv.setText(path + " upload success!");
}
}
};
private int index = 0;
private void addTask() {
String path = Environment.getExternalStorageDirectory().getPath() + "/imgs/" + (++index) + ".png";
MyIntentService.startIntentService(this, path);
TextView tv = new TextView(this);
tv.setText(path + " is uploading ...");
tv.setTag(path);
llContainer.addView(tv);
}
@Override
protected void onDestroy() {
super.onDestroy();
LocalBroadcastManagerUtils.getInstance(this).unregister(receiver);
}
}
在MainActivity类中,调用addTask()方法启动Service。在这里需要注意一点,不论点击多少次按钮,Service的onCreate()方法只会运行一次,也即程序中只存在一个MyIntentService的实例,Thread线程实例也只创建一次。而多次点击所接收的Intent数据均由Handler的消息机制进行管理。
AndroidManifest.xml
<application
<service android:name=".MyIntentService" />
</application>
在AndroidManifest.xml文件中,不要忘记对Service进行注册。
网友评论
------------------
每次handler处理完毕后,会stopself,所以如果点多次,是有可能在多个service里运行的。