[Android 基础系列]Service、IntentService和习以为常的误解

前言:

也许是低门槛原因,最初接触Android的人写了很多书、博文,创造了一个邪论:Activity就是弄界面的,Service就是弄后台的,进而将“播放音乐”这种演变为“耗时操作”,进而演绎成:“耗时的、长时间运行的都需要使用service”。只想说:MDZZ!

原意是想全文自己写,但看了一眼API文档,整理的实在是太好了,所以本文会摘录API的内容并结合重点写一点内容。

正文:

Service:

API文档中的概述如下:

A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding <service> declaration in its package's AndroidManifest.xml. Services can be started with Context.startService() andContext.bindService().
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.

给大家蹩脚的翻译一下:

Service是一个应用组件,代表着:“在不和用户交互的情况下,进行‘持续’运行的应用需求”或者“为其他应用提供功能性服务” 。每个Service类(当然是你要用的)都需要在Manifest文件中声明(利用<service>节点),Service有两种方式启动:Context.startService() 和 Context.bindService() 函数。

注意注意注意!service和其他应用组件一样,运行在宿主进程的主线程中,这意味着如果你的service需要做 频繁的CPU计算操作(例如播放MP3)或者阻塞的操作(例如访问网络),那应该创建独立的线程(就是让你规避主线程)。

然后提到了IntentService是一个规范的实现,它在子线程中处理工作(言下之意你不用手动开辟子线程)。

请注意我上面用了一个词:“持续”运行,这并不准确,但我不想使用长时间运行这个词,总会被误解为“阻塞”。

OK,再看看Service是个什么鬼,API说你所疑惑的可以通过排除法解决:

?	A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.
?	A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).

Service不是一个分隔独立的进程

Service不是线程,不代表它脱离主线程工作

把两点合一起:service是运行在启动它的应用程序进程中的,应用程序这个进程有主线程(俗称UI线程),service不脱离主线程工作,所以你不能阻塞service的主线程,否则这会带来ANR。

我们先不看生命周期(其实大家对此是比较明白的)

我们看API中提到的Local Service使用,顺便引出一个疑惑。

写一个Service:

public class LocalService extends Service {
    private NotificationManager mNM;

    // Unique Identification Number for the Notification.
    // We use it on Notification start, and to cancel it.
    private int NOTIFICATION = R.string.local_service_started;

    /**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }

    @Override
    public void onCreate() {
        mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);

        // Display a notification about us starting.  We put an icon in the status bar.
        showNotification();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("LocalService", "Received start id " + startId + ": " + intent);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        // Cancel the persistent notification.
        mNM.cancel(NOTIFICATION);

        // Tell the user we stopped.
        Toast.makeText(this, R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();

    /**
     * Show a notification while this service is running.
     */
    private void showNotification() {
        // In this sample, we'll use the same text for the ticker and the expanded notification
        CharSequence text = getText(R.string.local_service_started);

        // Set the icon, scrolling text and timestamp
        Notification notification = new Notification(R.drawable.stat_sample, text,
                System.currentTimeMillis());

        // The PendingIntent to launch our activity if the user selects this notification
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                new Intent(this, LocalServiceActivities.Controller.class), 0);

        // Set the info for the views that show in the notification panel.
        notification.setLatestEventInfo(this, getText(R.string.local_service_label),
                       text, contentIntent);

        // Send the notification.
        mNM.notify(NOTIFICATION, notification);
    }
}

省略声明

演示绑定/启动

private LocalService mBoundService;

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        // This is called when the connection with the service has been
        // established, giving us the service object we can use to
        // interact with the service.  Because we have bound to a explicit
        // service that we know is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        mBoundService = ((LocalService.LocalBinder)service).getService();

        // Tell the user about this for our demo.
        Toast.makeText(Binding.this, R.string.local_service_connected,
                Toast.LENGTH_SHORT).show();
    }

    public void onServiceDisconnected(ComponentName className) {
        // This is called when the connection with the service has been
        // unexpectedly disconnected -- that is, its process crashed.
        // Because it is running in our same process, we should never
        // see this happen.
        mBoundService = null;
        Toast.makeText(Binding.this, R.string.local_service_disconnected,
                Toast.LENGTH_SHORT).show();
    }
};

void doBindService() {
    // Establish a connection with the service.  We use an explicit
    // class name because we want a specific service implementation that
    // we know will be running in our own process (and thus won't be
    // supporting component replacement by other applications).
    bindService(new Intent(Binding.this,
            LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
    mIsBound = true;
}

void doUnbindService() {
    if (mIsBound) {
        // Detach our existing connection.
        unbindService(mConnection);
        mIsBound = false;
    }
}

@Override
protected void onDestroy() {
    super.onDestroy();
    doUnbindService();
}

我们看到了使用bindService 绑定并启动service,并在Activity销毁时解除绑定。所以有人疑惑了:我并不想我的service这时候被结束掉,但不解除绑定是不规范的、甚至担心是否会影响到Activity的销毁、回收。

OK,我们必须再回忆一下Service的介绍,没有一句话说它是为Activity提供服务的,它本身是一个服务没错,它也只是一个服务,而不是为Activity提供服务的服务。

前面提到了两种“启动”service的方法 bindService和startService,严格的说:startService是启动,而且是Context中定义的方法,从来没有说是Activity中定义的;bindService是绑定service,绑定时如果没有启动会帮助你启动,但本质是绑定。

存在这样的差异:

启动:我只是把你弄活了,然后没有半毛钱关系。

绑定:你要跟着我混!我不要你了你就可以走了。

Context类中对unbindService的描述是:

Disconnect from an application service. You will no longer receive calls as the service is restarted, and the service is now allowed to stop at any time.

断开应用服务连接,你不会再收到service重启的消息(回调),这个service也可以在任意时间被系统终止了。

这里我们做一个简单的测试,让两个Activity绑定同一个Service,看解除绑定对service的影响:

package com.example.testservice;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class MainActivity extends Activity {

	private final static String TAG = "MainActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		Log.d(TAG, "onCreate");
		doBindService();

		findViewById(R.id.button).setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				startActivity(new Intent(MainActivity.this,SecondActivity.class));
			}
		});
	}

	private LocalService mBoundService;

	private ServiceConnection mConnection = new ServiceConnection() {
	    public void onServiceConnected(ComponentName className, IBinder service) {
	        mBoundService = ((LocalService.LocalBinder)service).getService();

	        // Tell the user about this for our demo.
	        Log.d(TAG, "service bind");
	    }

	    public void onServiceDisconnected(ComponentName className) {
	        // This is called when the connection with the service has been
	        // unexpectedly disconnected -- that is, its process crashed.
	        // Because it is running in our same process, we should never
	        // see this happen.
	        mBoundService = null;
	        Log.d(TAG, "service unbind");
	    }
	};

	void doBindService() {
	    // Establish a connection with the service.  We use an explicit
	    // class name because we want a specific service implementation that
	    // we know will be running in our own process (and thus won't be
	    // supporting component replacement by other applications).
		Log.d(TAG, "do service bind");
	    bindService(new Intent(this,
	            LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
	    mIsBound = true;
	}

	private boolean mIsBound  = false;

	void doUnbindService() {
	    if (mIsBound) {
	        // Detach our existing connection.
	    	Log.d(TAG, "do service unbind");
	        unbindService(mConnection);
	        mIsBound = false;
	    } else {
	    	Log.d(TAG, "no necessary to unbind,it is not binding");
	    }
	}

	@Override
	protected void onDestroy() {
		Log.d(TAG, "onDestroy");
	    super.onDestroy();
	    doUnbindService();
	}
}
package com.example.testservice;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

public class SecondActivity extends Activity {

	private final static String TAG = "SecondActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_second);
		doBindService();
	}

	private LocalService mBoundService;

	private ServiceConnection mConnection = new ServiceConnection() {
	    public void onServiceConnected(ComponentName className, IBinder service) {
	        mBoundService = ((LocalService.LocalBinder)service).getService();

	        // Tell the user about this for our demo.
	        Log.d(TAG, "service bind");
	    }

	    public void onServiceDisconnected(ComponentName className) {
	        // This is called when the connection with the service has been
	        // unexpectedly disconnected -- that is, its process crashed.
	        // Because it is running in our same process, we should never
	        // see this happen.
	        mBoundService = null;
	        Log.d(TAG, "service unbind");
	    }
	};

	void doBindService() {
	    // Establish a connection with the service.  We use an explicit
	    // class name because we want a specific service implementation that
	    // we know will be running in our own process (and thus won't be
	    // supporting component replacement by other applications).
		Log.d(TAG, "do service bind");
	    bindService(new Intent(this,
	            LocalService.class), mConnection, Context.BIND_AUTO_CREATE);
	    mIsBound = true;
	}

	private boolean mIsBound  = false;

	void doUnbindService() {
	    if (mIsBound) {
	        // Detach our existing connection.
	    	Log.d(TAG, "do service unbind");
	        unbindService(mConnection);
	        mIsBound = false;
	    } else {
	    	Log.d(TAG, "no necessary to unbind,it is not binding");
	    }
	}

	@Override
	protected void onDestroy() {
		Log.d(TAG, "onDestroy");
	    super.onDestroy();
	    doUnbindService();
	}
}
package com.example.testservice;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

/**
 * @ClassName: LocalService
 * @Description: TODO
 * @date 2016年4月27日 下午3:43:57
 *
 * @author leobert.lan
 * @version 1.0
 */
public class LocalService extends Service {

	private final static String TAG = "LocalService";

    /**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }

    @Override
    public void onCreate() {
    	Log.i(TAG, "service onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "Received start id " + startId + ": " + intent);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

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

    @Override
    public IBinder onBind(Intent intent) {
    	Log.i(TAG, "service onBind");
        return mBinder;
    }

    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new LocalBinder();

}

我截取了一下logcat,但是eclipse的背景。。。

我们看到,不是解除绑定就关闭service的,是service不再被绑定的时候,它会被干掉。

正如API提供的demo中注释所说的一样:

// This is called when the connection with the service has been
        // established, giving us the service object we can use to
        // interact with the service.  Because we have bound to a explicit
        // service that we know is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.

bindService可以获得service对象,并且和它进行交互(说白了就是想调用他的方法)。

我们还有另外一个有逼格的东西:IntentService。

IntentService:

文档这样描述它:

IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

This "work queue processor" pattern is commonly used to offload tasks from an application's main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

All requests are handled on a single worker thread -- they may take as long as necessary (and will not block the application's main loop), but only one request will be processed at a time.

IntentService是service的子类,设计用来处理异步请求,通过startService函数的调用来发送请求,吧啦吧啦一堆。

要使用它的话需要实现onHandlerIntent方法,这个方法的描述:

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 stopSelf().

Intent是有序到达、处理的,先来的先处理,耗时的话后面的等着,不会阻塞主线程之类的,干完活自己干死自己,所以你不要调用stopSelf().

然而我们经常觉得没啥卵用。

多线程:

这里话分两头,前面我们知道service不是thread,耗时操作应该规避主线程的,所以要开辟子线程做,IntentService做了一定的封装,帮助开辟了子线程。

在Android使用多线程要知道一件事情:Dalvik毕竟能提供的资源有限,意味着回收很重要,开辟线程会提高资源使用程度,但也有被系统干死回收的可能,依托Activity开辟子线程(初衷一定是为了耗时的code、阻塞的code)有Activity面临关闭而子线程操作没有完成的可能,例如日记本应用。依托Service的生命力顽强一点。

不过这个没法演示。

后记:

网上的文章有好有坏,大家还是要多看API文档靠谱,国人的转载风格大家都懂的。我写东西一般本着负责的心,没法保证都是对的,如果有错误的内容还请大家指出。

时间: 2024-11-10 00:53:00

[Android 基础系列]Service、IntentService和习以为常的误解的相关文章

Android基础系列合集

四大组件 1.Service Android四大组件-Service Android-远程Service Service 动态更新 UI 2.Activity Android四大组件-Activity Android-Activity启动流程 Android - ANR小结 3.Content Provider Android四大组件-Content Provider android - SQLite 数据库 4.Broadcast Receiver Android四大组件-Broadcast

android 基础一 &lt;Service&gt;

1. 创建一个service: public class MyService extends Service {} 2.在清单中申明service<application> <service        android:name=".MyService"        android:enabled="true"        android:exported="true">    </service><

【Android基础】-Service组件使用详解

Service是Android四大组件之一,它与Activity的区别是:它一直在后台运行,没有前台界面.一旦Service被启动起来后,他就跟Activity一样,完全具有自己的生命周期. 一.创建Service,定义一个继承Service的子类 Service中定义了一系列生命周期方法,如下: IBinder onBind(Intent intent):该方法是Service必须实现的方法.该方法返回一个IBinder对象,应用程序可以通过该对象与Service组件通信. void onCr

Android基础(五) Service全解析----看不见的Activity

一.服务的介绍: 作为Android四大组件之中的一个,Service(服务)也常常运用于我们的日常使用中,它与Activity的差别在于:Service一直在后台执行.没实用户界面.所以绝不会到前台来.但Service被启动起来之后.它就和Activity一样.全然具有自己的生命周期. 在关于程序中是选择用Activity还是Service的一个选择标准就是:假设某个程序组件须要执行时向用户呈现某种用户界面.或者该程序须要与用户交互,就须要使用Activity,否则就该考虑使用Service.

[Android基础系列]设计模式(一)

前言 这篇文章可以说是java基础的范畴,为了下一篇Android开发中的常用设计模式做一下铺垫,也顺便反思一下. 正文 设计模式分类 分类方式是多样的,这里我们按照功能类型进行分类: 创建型 工厂方法 Factory Method 抽象工厂 Abstract Factory 建造者 Builder 原型 Prototype 单例 Singleton 结构型 适配器 Adapter Class/Object 桥接 Bridge 组合 Composite 装饰 Decorator 外观 Facad

[Android基础系列]重新审视ContentProvider

前言 上一篇文章梳理了一下Service,这一篇文章梳理一下同为四大组件之一的ContentProvider,也试着使用在线MarkDown编辑器.正常开发中,我们使用ContentProvider较少,大家也不会太过于关注.国内能看到的几篇文章也是比较老旧了,demo中也有一些错误,所以写一篇"像样"的,凑凑数. 正文 概述ContentProvider 依旧去API文档中先拿到官方的概述,并做一点蹩脚的翻译和注解: Content providers are one of the

[Android基础系列]设计模式(二)

前言 上一篇设计模式一 中,我们只整理了创建型的设计模式,本篇主要看一下结构型的. 正文 结构型包含以下类型: 适配器 Adapter Class/Object 桥接 Bridge 组合 Composite 装饰 Decorator 外观 Facade 享元 Flyweight 代理 Proxy 适配器 个人意见:最适合在工作完成了一部分,但是两套系统接口规范不完全相适的情况下,使用适配器"协调"一下,在从零开始的情况下,尽可能避免接口不适配的情况. demo: 目标 package

Android四大组件——Service后台服务、前台服务、IntentService、跨进程服务、无障碍服务、系统服务

Service后台服务.前台服务.IntentService.跨进程服务.无障碍服务.系统服务 本篇文章包括以下内容: 前言 Service的简介 后台服务 不可交互的后台服务 可交互的后台服务 混合性交互的后台服务 前台服务 IntentService AIDL跨进程服务 AccessibilityService无障碍服务 系统服务 部分源码下载 前言 作为四大组件之一的Service类,是面试和笔试的必备关卡,我把我所学到的东西总结了一遍,相信你看了之后你会对Service娓娓道来,在以后遇

Android基础工具类重构系列一Toast

前言: 一直在考虑写一下Android实际项目中的一些总结,翻看CSDN博客,上一篇已经是一年多曾经. 本系列定位Android基础工具类重构.旨在记录实际项目中经经常使用到的一些工具类,比方Toast.Dialog.动画类,ImageLoader类等等.正在梳理,但发现梳理完再写预计黄花菜都凉了.所以改变策略,边写边梳理. 首先要写的就是这个Toast. 一.说明 作为Android系统提供的基类,Toast是最简单的提示消息类.特点悬浮.跨界面(Activity)特定时间内自己主动销毁. 二