美文网首页AndroidAndroid知识Android开发
IntentService的原理和实例分析

IntentService的原理和实例分析

作者: 1409d11db72f | 来源:发表于2016-08-29 14:02 被阅读763次

前言:
在工程的开发中,想必大家都会普遍用到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进行注册。

相关文章

网友评论

  • 尔乐:[ 在MainActivity类中,调用addTask()方法启动Service。在这里需要注意一点,不论点击多少次按钮,Service的onCreate()方法只会运行一次,也即程序中只存在一个MyIntentService的实例,Thread线程实例也只创建一次。而多次点击所接收的Intent数据均由Handler的消息机制进行管理。]
    ------------------
    每次handler处理完毕后,会stopself,所以如果点多次,是有可能在多个service里运行的。

本文标题:IntentService的原理和实例分析

本文链接:https://www.haomeiwen.com/subject/vruxettx.html