Android基础知识 Service相关

https://blog.csdn.net/javazejian/article/details/52709857

Service的定义及作用

  • Service(服务) 是一种可以在后台执行长时间运行、没有用户界面的应用组件。

  • 作用:执行后台操作、耗时操作(需放到子线程)、进程间通信

  • Service在清单文件中的声明

      <service android:enabled=["true" | "false"]
      	android:exported=["true" | "false"]
      	android:icon="drawable resource"
      	android:isolatedProcess=["true" | "false"]
      	android:label="string resource"
      	android:name="string"
      	android:permission="string"
      	android:process="string" >
      	. . .
      </service>
    
属性含义
android:exported是否能被其他应用隐式调用,默认值是由service中有无intent-filter决定的,
如果有intent-filter,默认值为true,否则为false。
android:exported 为 false 时,无法被其他应用隐式调用(即使有intent-filter匹配)。
android:name对应Service类名
android:permission是权限声明
android:process是否需要在单独的进程中运行,
当设置为android:process=”:remote”时,代表Service在单独的进程中运行。
注意 “:”很重要,表示,在当前进程名称前面附加上当前的包名
所以“remote”和”:remote”不是同一个意思
前者的进程名称为 remote,而后者的进程名称为:App-packageName:remote
android:isolatedProcess设置 true,表示,服务会在一个特殊的进程下运行
这个进程与系统其他进程分开且没有自己的权限。
与其通信的唯一途径是通过服务的API (bind and start)。
android:enabled是否可以被系统实例化,默认为 true
因为父标签也有 enable 属性,所以必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活。

Service两种启动方式 startService、 bindService 区别及生命周期

1、startService 启动状态

调用 startService() 启动服务时,服务即处于“启动”状态
一旦启动,服务即可在后台无限期运行,即使启动服务的组件(Activity)已被销毁也不受影响。
手动调用才能停止服务,已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。

2、bindService 绑定状态

调用 bindService() 绑定到服务时,服务处于“绑定”状态
绑定服务,可以会返回一个Binder对象给客户端,用于C-S交互。
允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。
只有与另一个应用组件绑定时,绑定服务才会运行。
多个组件可以同时绑定到该服务,全部取消绑定后,该服务即会被销毁。

3、Service的一些方法
  1. onCreate()
    首次创建服务时,系统将调用一次(在调用 onStartCommand() 或onBind() 之前)。
    服务已在运行,则不会调用此方法,该方法只调用一次

  2. onBind()
    调用 bindService() 绑定服务时,onBind() 返回 一个 IBinder 接口的实现类,供客户端用来与服务进行通信
    无论是启动状态、绑定状态,此方法必须重写
    在startService启动状态的情况下直接返回 null

  3. onStartCommand(Intent intent, int flags, int startId)
    在绑定状态下,无需实现此方法
    每次 startService() 方法启动Service时都会被回调
    一旦执行此方法,服务即会启动并可在后台无限期运行。
    startService()启动的服务,需要 调用 stopSelf() 或 stopService() 来停止服务

    • 参数一:intent,启动时,启动组件传递进来的Intent

    • 参数二:flags,表示启动请求时是否有额外数据。
      flags三个可选值
      (1). 传0,表示没有额外数据
      (2) 传 Service.START_FLAG_REDELIVERY
      表示 onStartCommand() 方法,返回值为 START_REDELIVER_INTENT
      而且在上一次服务被杀死前会去调用stopSelf方法停止服务。

      START_REDELIVER_INTENT 表示:当Service因内存不足而被系统kill后,则会重建服务,
      并通过传递给服务的最后一个 Intent 调用 onStartCommand(),此时Intent时有值的

      (3) 传Service.START_FLAG_RETRY
      表示 onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()

    • 参数三:startId,当前服务的唯一ID
      与 stopSelfResult (int startId)配合使用,stopSelfResult 可以更安全地根据ID停止服务

  4. onStartCommand的返回值
    (1) START_STICKY
    当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,
    一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null

    除非有挂起的Intent,如pendingintent,
    比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务

    (2) START_NOT_STICKY
    当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service
    除非程序中再次调用startService启动此Service,
    这是最安全的选项,避免在不必要时、应用能够轻松重启所有未完成的作业时运行服务

    (3) START_REDELIVER_INTENT
    当Service因内存不足而被系统kill后,则会重建服务,并传递给服务的最后一个 Intent 调用 onStartCommand()
    任何挂起 Intent均依次传递。
    与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent
    适用于主动执行应该立即恢复的作业(例如下载文件)的服务

  5. onDestroy()
    服务销毁时的回调
    清理所有资源,如线程、注册的侦听器、接收器等,这是服务接收的最后一个调用。

bindService绑定服务的三种实现方式

扩展Binder类、使用Messenger、使用AIDL

当Service处于绑定状态时,其代表着客户端-服务器接口中的服务器。
绑定服务的生命周期通常只在为其他应用组件(如Activity)服务时处于活动状态,不会无限期在后台运行
宿主(如Activity)解除绑定后,绑定服务就会被销毁

1、扩展Binder类:

服务和客户端还必须在同一进程内,此方式不执行任何跨进程

  • 场景:不需要跨进程,服务是提供给自有应用专用的,C-S在相同进程
  • 流程:
    1)创建BindService服务端,继承自Service并在类中,创建一个实现IBinder 接口的实例对象
    2)从 onBind() 回调方法返回此 Binder 实例。
    3)在客户端中,从 onServiceConnected() 回调方法接收 Binder,并使用提供的方法调用绑定服务。
创建Service
创建实现了IBinder的实例对象
onBind 方法返回IBinder实例
客户端从 onServiceConnected 回调方法接收 Binder
客户端通过Binder与Service交互
  • 代码实现
    Server端:

    public class LocalService extends Service{
      	private final static String TAG = "wzj";
      	private int count;
      	private boolean quit;
      	private Thread thread;
      	private LocalBinder binder = new LocalBinder();
    
      	/**
      	 * 创建Binder对象,返回给客户端即Activity使用,提供数据交换的接口
      	 */
      	public class LocalBinder extends Binder {
      		// 声明一个方法,getService。(提供给客户端调用)
      		LocalService getService() {
      			// 返回当前对象LocalService,这样我们就可在客户端端调用Service的公共方法了
      			return LocalService.this;
      		}
      	}
    
      	/**
      	 * 把Binder类返回给客户端
      	 */
      	@Nullable
      	@Override
      	public IBinder onBind(Intent intent) {
      		return binder;
      	}
      	...
    }
    

Client端:

public void doBindService(String packageName) {
	Intent intent = new Intent(mContext, LocalService.class);
	intent.putExtra(PACKAGE_BUNDLE_KEY, packageName);
	/**
	 * 参数三:flags则是指定绑定时是否自动创建Service。
	 * 0 代表不自动创建、Context.BIND_AUTO_CREATE 则代表自动创建
	 */					
	mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}

public void unBindService(Context context) {
	context.unbindService(mConnection);
}

private ServiceConnection mConnection = new ServiceConnection() {
	@Override
	public void onServiceConnected(ComponentName className, IBinder service) {
		/**
		 * 绑定服务的时候被回调,在这个方法获取绑定Service的onBind()方法返回的 IBinder,
		 * 通过这个IBinder对象,实现宿主和Service的交互。
		 */
		LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
		mService = binder.getService();
	}

	@Override
	public void onServiceDisconnected(ComponentName className) {
		/**
		 * 当取消绑定的时候被回调。但正常情况下是不被调用的,
		 * 它的调用时机是当Service服务被意外中断时,(例如当服务崩溃或内存不足被终止时)这个方法才被自动调用。
		 */
		mServerMsger = null;
	}
};
2、使用Messenger

Bundle、Message、Messenger都实现了 Parcelable 接口,可以用于跨进程传递数据

  • Messenger 的跨进程方式其底层实现 就是AIDL
    使用 Messenger 为服务创建接口,客户端就可利用 Message 对象向服务发送命令
    同时客户端也可定义自有 Messenger,以便服务回传消息

  • Messenger 会在单一线程中创建包含所有请求的队列
    Messenger是以串行的方式处理客户端发来的消息,C-S交互是线程安全的。

  • 过程

  1. Service 中实现一个 Handler,接收来自客户端的每个调用的回调

  2. Handler 作为 Messenger 构造函数的参数,在创建Messenger时传入
    3) Handler # getIMessenger() 返回一个 IMessenger 对象,这是一个 Binder对象,也是Messenger 进程间通信的使用的Binder对象
    Service 的 onBind() 中,调用 IMessenger # asBinder() 返回该Binder 对象给客户端

  3. 客户端收到返回的 IBinder 后,用 Messenger 包装该 Binder,然后使用Messenger将 Message 对象发送给服务(内部用的服务端返回的Binder发送)
    发送的消息,都塞入了 Service端的 Handler 的消息队列中

  4. Service 在其 Handler 中(在 handleMessage() 方法中)接收每个 Message

创建Service
Service中创建 Hander对象
Service中创建 Messenger 对象并传入 Handler对象
onBind 方法中通过 messenger.getBinder 返回一个 Binder
客户端从 onServiceConnected 回调方法接收 Binder
客户端以Binder为参数创建一个 Messenger
该Messenger是Service中Messenger的代理 客户端可以通过它发送消息给Service
客户端创建Handler 并以该Handler为参数创建Messenger
该Messenger会传给Service Service通过它发送消息给客户端
  • 代码实现
    Server端:

    public class MessengerService extends Service {
      	static final int MSG_CONNECTED = 1;
      	private static final String TAG ="MessengerService" ;
      	
      	// 创建Messenger并传入Handler实例对象
      	final Messenger mMessenger = new Messenger(new IncomingHandler());
    
      	// 用于接收从客户端传递过来的数据
      	class IncomingHandler extends Handler {
      		@Override
      		public void handleMessage(Message msg) {
      			switch (msg.what) {
      				case MSG_CONNECTED://收到app进程发来的消息
      					msg.replyTo.send(Message.obtain(null, MSG_KILL_APP));//通知app进程自杀
      					break;
      				default:
      					super.handleMessage(msg);
      			}
      		}
      	}
    
      	/**
      	 * 当绑定Service时,该方法被调用,将通过mMessenger返回一个实现
      	 * IBinder接口的实例对象
      	 */
      	@Override
      	public IBinder onBind(Intent intent) {
      		Log.i(TAG, "Service is invoke onBind");
      		return mMessenger.getBinder();
      	}
    }
    

    Client端:

    public class ActivityMessenger extends Activity {
      	//标记 Service 是否已经绑定成功
      	boolean mBound;					
      	// 与服务端交互的Messenger
      	Messenger mService = null;
      	// 实现与服务端链接的对象
      	private ServiceConnection mConnection = new ServiceConnection() {
      		public void onServiceConnected(ComponentName className, IBinder service) {
      			/**
      			 * 通过服务端传递的IBinder对象,创建相应的Messenger
      			 * 通过该Messenger对象与服务端进行交互
      			 */
      			mService = new Messenger(service);
      			mBound = true;
      		}
    
      		public void onServiceDisconnected(ComponentName className) {
      			// This is called when the connection with the service has been
      			// unexpectedly disconnected -- that is, its process crashed.
      			mService = null;
      			mBound = false;
      		}
      	};
      	
      	private void bindService() {
      		//当前Activity绑定服务端
      		Intent intent = new Intent(getContext(), MessengerService.class);
      		bindService(intent, mConnection,Context.BIND_AUTO_CREATE);
      	}
      	
      	private void unbindServer() {
      		if (mBound) {
      			unbindService(mConnection);
      			mBound = false;
      		}
      	}
    
      	public void sendToServer() {
      		if (!mBound) return;
      		// 创建与服务交互的消息实体Message
      		Message msg = Message.obtain(null, MessengerService.MSG_CONNECTED, 0, 0);
      		//消息的replyTo指向当前进程中创建的Handler,方便接收Server进程发过来的消息
      		msg.replyTo = getClientMessenger();
      		try {
      			//发送消息
      			mService.send(msg);
      		} catch (RemoteException e) {
      			e.printStackTrace();
      		}
      	}
      	
      	/** 创建接收重启进程消息的Messenger */
      	private Messenger getClientMessenger() {
      		ClientMsgHandler handler = new ClientMsgHandler(mService);
      		Messenger clientMsger = new Messenger(handler);
      		handler.setClientMsger(clientMsger);
      		return clientMsger;
      	}
    
      	//接收server 发送过来的消息,并发送消息
      	private static class ClientMsgHandler extends Handler {
    
      		private Messenger serverMsger;
    
      		private Messenger clientMsger;
    
      		public ClientMsgHandler(Messenger serverMsger) {
      			this.serverMsger = serverMsger;
      		}
    
      		public void setClientMsger(Messenger clientMsger) {
      			this.clientMsger = clientMsger;
      		}
    
      		@Override
      		public void handleMessage(Message msg) {
      			switch (msg.what) {
      				case MessengerService.MSG_KILL_APP://收到Server端发来的消息
      					Message restartMsg = Message.obtain(null, MessengerService.MSG_RESTART_APP);
      					restartMsg.replyTo = clientMsger;
      					try {
      						if (serverMsger != null) {
      							//收到消息后,通知Server端
      							serverMsger.send(restartMsg);
      						}
      						//执行任务
      						Process.killProcess(Process.myPid());
      					} catch (Exception e) {
      						e.printStackTrace();
      					}
      					break;
      				default:
      					super.handleMessage(msg);
      			}
      		}
      	}
    }
    
3、使用AIDL

进程间通信,Service 可以同时处理大量并发请求

可以定义 aidl类型的接口,作为进程间调用的回调函数。
因为aidl 类型文件会生成对应的Stub类,该类是继承了Binder类,真正在进程间传递的是对应的Stub类。

代码

  • 1)aidl
    // IBinderManager.aidl

    //子进程请求主进程服务成功后,Server端返回给Client的Binder对象
    //返回这个管理器Binder,在子进程可以根据binderCode拿到需要的Binder
    //这种设计是为了解耦,方便不同业务下把Binder分离

    interface IBinderManager {
        IBinder queryBinder(int binderCode); //根据code获取需要的Binder
    }
    

    //IWebBinder.aidl

    //Server提供的Binder对象,可以通过 IBinderManager.queryBinder(int binderCode)获取该Binder
    interface IWebBinder {
         /**
          * methodName: 方法名   jsonParams: 方法参数    callback跨进程回调函数
          */
          void handleJsFunction(String actionName, String jsonParams, IWebBinderCallback callback);
         /**
          * type:消息类型   data:数据
          */
          void sendEventBus(int type, boolean success, String data);
    }
    

    //IWebBinderCallback.aidl

    // aidl调用后异步回调的接口;
    // Client通过IWebBinder.handleJsFunction()调用Server端方法时,把该aidl接口作为回调函数
    interface IWebBinderCallback {
        //msgType:回调给web进程的消息类型   message:回调给web进程的消息内容
        void onResult(int msgType, String message);
    }
    
  • 2)aidl 的实现类

    /**
     * Binder管理器 IBinderManager.aidl 的具体实现
     * Created by zhangjianliang on 2018/5/22
     */
    public class BinderManager extends IBinderManager.Stub {
    
      	public static final int BINDER_WEB_AIDL = 1;//h5进程请求主进程
    
      	private Context context;
    
      	public BinderManager(Context context) {
      		this.context = context;
      	}
    
      	@Override
      	public IBinder queryBinder(int binderCode) throws RemoteException {
      		IBinder binder = null;
      		switch (binderCode) {
      			case BINDER_WEB_AIDL: {
      				//WebBinderInterface是具体业务相关的Binder对象,这里就不展开代码了
      				binder = new WebBinderInterface(context);
      				break;
      			}
      			default:
      				break;
      		}
      		return binder;
      	}
      }
      
      /** IWebBinder.aidl 的实现
       * 主进程中,封装了给h5进程调用的接口
       * Created by zhangjianliang on 2018/5/22
       */
    public class WebBinderInterface extends IWebBinder.Stub {
    
      	private Context context;
    
      	public WebBinderInterface(Context context) {
      		this.context = context;
      	}
    
      	@Override   //处理web进程中h5页面穿过来的事件
      	public void handleJsFunction(String methodName, String params, IWebBinderCallback callback) throws RemoteException {
      		try {
      			H5Bridge.getInstance().callJava(methodName, params, callback);
      		} catch (Exception e) {
      			e.printStackTrace();
      		}
      	}
    
      	@Override
      	public void sendEventBus(int type, boolean success, String data) throws RemoteException {
      		EventBus.getDefault().post(new WebEvent(type, success, data));
      	}
    }
    
  • 3)Service

    /**
     * 主进程的服务端,子进程连接该服务,返回Binder,用于在子进程调用主进程的api
     * Created by zhangjianliang on 2018/5/22
     */
    public class MainRemoteService extends Service {
    
      	@Override
      	public IBinder onBind(Intent intent) {
      		return new BinderManager(this);//客户端绑定该Service时,返回的IBinder对象
      	}
    }
    
  • 4)Client端

    /**
     * 绑定Service
     * 这里放到子线程初始化,是为了避免启动服务时阻塞主线程
     */
    public synchronized void bindMainService(Context context) {
      	ThreadPoolFactory.instance().fixExecutor(new Runnable() {
      		@Override
      		public void run() {
      			mCountDownLatch = new CountDownLatch(1);//共享锁
      			Intent service = new Intent(context, MainRemoteService.class);
      			if (mConnect == null) {
      				mConnect = new ServiceConnectImpl();
      			}
      			context.bindService(service, mConnect, Context.BIND_AUTO_CREATE);
      			try {
      				mCountDownLatch.await();//阻塞当前线程(webview子进程的子线程),等待连接完成
      			} catch (InterruptedException e) {
      				e.printStackTrace();
      			}
      		}
      	});
    }
    
    private class ServiceConnectImpl implements ServiceConnection {
      	@Override
      	public void onServiceConnected(ComponentName name, IBinder service) {
      		if (service != null) {
      			mBinderManager = IBinderManager.Stub.asInterface(service);
      			try {
      				//子进程的主线程中监听binder的死亡通知
      				mBinderManager.asBinder().linkToDeath(new IBinder.DeathRecipient() {
      					@Override
      					public void binderDied() {
      						mBinderManager.asBinder().unlinkToDeath(this, 0);
      						mBinderManager = null;
      						//此处回调到了Server端的主线程中,如果这里重新连接服务就是在主线程连接服务,
      						//如果连接异常有小概率出现anr
      						//可以放到子线程重新请求连接Service
      					}
      				}, 0);
      			} catch (RemoteException e) {
      				e.printStackTrace();
      			}
      		}
      		//连接成功后,共享锁释放锁,子进程启动服务的线程唤醒继续往下执行
      		mCountDownLatch.countDown();
      	}
    
      	@Override
      	public void onServiceDisconnected(ComponentName name) {
      	}
    }
      
    /**
     * Client端通过Server返回的IBinder,调用Server的方法,并通过回调函数返回
     */
    private void callServer() {
      	IBinder binder = null;
      	WebBinderInterface mWebBinder = null;
      	try {//mBinderManager 是Server端返回的Binder对象
      		if (mBinderManager != null) {//通过 mBinderManager 获取我们业务相关的 Binder对象
      			binder = mBinderManager.queryBinder(binderCode);
      		}
      		if (binder) {
      			//mWebBinder 是Server端queryBinder(binderCode)返回的IWebBinder.Stub.Proxy 代理
      			//【这个Proxy是属于Client进程的对象】,不是Server端的 WebBinder真身
      			mWebBinder = IWebBinder.Stub.asInterface(binder);
      			if (mCallback != null) {
      				mCallback.onServiceConnected();
      			}
      		}
      		if(mWebBinder == null) {
      			return;
      		}
      		/**
      		 * handleJsFunction()是在Client执行的,因为 IWebBinder.Stub.asInterface(binder) 返回的是IWebBinder的代理
      		 * 该代理对象是属于Client端的,不是Server端的WebBinder真身
      		 * Client端的WebBinder.Stub.Proxy 对象执行完Client端的操作后,通过 IWebBinderCallback.Stub 回调到Server端
      		 * 所以 onResult 已经回到Server端执行了
      		 */
      		mWebBinder.handleJsFunction(action, params, new IWebBinderCallback.Stub() {
      			@Override
      			public void onResult(int msgType, String message) throws RemoteException {
      				if (mActivity != null && !mActivity.isFinishing()) {
      					resolveResult(msgType, message);
      				}
      			}
      		});
      	} catch (RemoteException e) {
      		e.printStackTrace();
      	}
    }
    
4、bindService 的注意点
  • 1)多个Client可以同时连接到一个Service
    只有第一个Client绑定时,系统才会调用服务的onBind()方法 来检索IBinder。
    随后系统无需再次调用onBind(),便可将同一个IBinder传递给任何其他绑定的Client。
    最后一个Client取消与服务的绑定时,系统会将服务销毁。(除非startService也启动了该服务

  • 2)在Client生命周期(如Activity的生命周期)的进入和退出时,设置绑定和取消绑定操作

  1. 只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定

  2. 希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate()绑定,在 onDestroy()取消绑定
    表示 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务
    如果服务位于其他进程内,那么当提高该进程的权重时,否则低内存时,系统可能会终止该进程

  • 3)通常情况下(注意),不要在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定
    因为每一次生命周期转换都会发生这些回调,这样反复绑定与解绑是不合理的。
    onPause执行耗时操作,会阻塞新页面的onResume

  • 4)我们应该始终捕获 DeadObjectException DeadObjectException 异常
    该异常在连接中断时引发的,表示调用的对象已死亡,也就是Service对象已销毁
    这是远程方法引发的唯一异常,DeadObjectException继承自RemoteException,因此我们也可以捕获RemoteException异常。

  • 5)调用 bindService() 绑定到服务,绑定是异步执行的。
    系统随后调用服务的 onBind() 方法,方法返回用于与服务交互的 IBinder

关于启动服务与绑定服务间的转换问题

绑定服务 -> 启动服务、启动服务 -> 绑定服务

Service可以同时是绑定状态和启动状态,系统只会为一个Service创建一个实例对象
所以不管是启动服务还是绑定服务,操作的是同一个Service实例

1、先绑定服务 -> 后启动服务

当前Service先以绑定状态运行,再以启动状态运行,那么绑定服务将【会转为启动服务运行
这时如果之前 绑定的宿主(Activity)被销毁了,也不会影响服务的运行,服务还是会一直运行下去,
需要调用停止服务、或者内存不足,才会销毁该服务

2、先启动服务 -> 后绑定服务

当前Service 先以启动状态运行,再以绑定状态运行,当前启动服务并【不会转为绑定服务】,
但是还是会与宿主绑定,只是即使宿主解除绑定后,服务依然按启动服务的生命周期在后台运行
需要调用停止服务、或者内存不足,才会销毁该服务
启动服务 优先级 > 绑定服务

前台服务、通知发送

前台服务:被认为是 用户主动意识到的一种服务,即使内存不足时,系统也不会将其终止
前台服务,必须为状态栏提供通知,除非服务停止或从前台删除,否则不能清除通知。

1、startForeground(int id, Notification notification)

把当前服务设置为前台服务,id参数表示 唯一标识通知 的整型数
提供给 startForeground() 的整型【ID 不得为0】,而 notification是一个状态栏的通知

2、stopForeground(boolean removeNotification)

用来从前台删除服务,参数布尔值,表示是否也删除状态栏通知,true为删除
该方法并不会停止服务,只是不再是前台服务
如果在服务正在前台运行时将其停止,则通知也会被删除。

服务Service、线程Thread的区别

1、两者概念的迥异
  • 1)Thread 是程序执行的最小单元,它是分配CPU的基本单位,
    当然 Thread 还可以用于执行一些耗时异步的操作。
    android系统中UI线程也是线程的一种

  • 2)Service是Android的一种无界面组件,运行在主线程上,它是由系统进程托管
    Service 后台任务是指没有UI的组件

2、两者的执行任务迥异
  • 1)在android系统中,线程一般指的是工作线程(即后台线程),而主线程主要执行UI绘制和事件分发
    如果主线程执行耗时操作会导致ANR

  • 2)Service 则是android系统中的组件,一般运行于主线程中
    因此在Service中是不可以执行耗时操作的,否则系统会报ANR异常。

  • 称Service为后台服务,原因是它本身没有UI,用户无法感知,
    如果需要让 Service执行耗时任务,可在 Service中开启单独线程去执行。

3、两者使用场景
  • 1)耗时操作,都应该使用工作线程(Thread),才能保证UI线程不被占用而影响用户体验。

  • 2)需要长时间的在后台运行,且不需要交互,使用服务
    比如播放音乐,通过 Service+Notification 方式在后台执行同时在通知栏显示着。

4、两者的最佳使用方式
  • Thread、Service 结合着使用,
    比如下载文件,一般会通过 Service + Notification + Thread异步下载
    比如 应用程序会维持一个Service来从网络中获取推送服务

  • IntentService:Thread 与 Service 结合执行后台耗时任务
    优点:使用方便、代码简洁,不需要我们创建Service实例、同时也创建线程
    IntentService 是单个 worker thread,任务需要排队,因此不适合大多数的多任务场景

5、两者的真正关系

两者没有半毛钱关系。

Android 5.0以上 隐式启动问题、解决方案

1、显示启动
Intent intent = new Intent(this,XXXService.class);
startService(intent);
2、隐式启动

需要设置一个Action,把Action的名字设置成Service的全路径名字,在这种情况下 android:exported 默认为true

Intent serviceIntent = new Intent(); 
serviceIntent.setAction("com.android.ForegroundService");
startService(serviceIntent);
3、隐式启动存在意义

不同应用时,只能用隐式启动。

4、Android 5.0以上的隐式启动问题
  • 问题:Android 5.0 之后,禁止了隐式声明Intent来启动Service,使用隐式启动Service,会抛没有指明 Intent 的错误

  • 解决:

  1. 设置 Action、packageName

    Intent serviceIntent = new Intent(); 
    serviceIntent.setAction("com.android.ForegroundService");
    serviceIntent.setPackage(getPackageName());//设置应用的包名
    startService(serviceIntent);
    
  2. 将隐式启动转换为显示启动 PackageManager.queryIntentServices()

    public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
     	//找出所有匹配 intent 的 Service
     	PackageManager pm = context.getPackageManager();
     	List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
     	//确保只有一个匹配的 Service
     	if (resolveInfo == null || resolveInfo.size() != 1) {
     		return null;
     	}
     	//通过 ComponentName 创建Service 组件信息
     	ResolveInfo serviceInfo = resolveInfo.get(0);
     	String packageName = serviceInfo.serviceInfo.packageName;
     	String className = serviceInfo.serviceInfo.name;
     	ComponentName component = new ComponentName(packageName, className);
     	
     	//创建一个新的 intent   Use the old one for extras and such reuse
     	Intent explicitIntent = new Intent(implicitIntent);
     	//把Service 组件信息设置到 intent中
     	explicitIntent.setComponent(component);
     	return explicitIntent;
    }
    
     //调用
     Intent intent = new Intent();//辅助Intent
     intent.setAction("com.android.ForegroundService");
    
     //显示的启动服务
     Intent serviceIntent = new Intent(getExplicitIntent(this,intent));
     startService(serviceIntent);
    

如何保证服务不被杀死

1、因内存资源不足而杀死Service

  • 将 onStartCommand() 方法的返回值,设为 START_STICKYSTART_REDELIVER_INTENT
    表示服务在内存资源紧张时被杀死后,在内存资源足够时再恢复

    /**
     * 返回 START_STICKY 或 START_REDELIVER_INTENT
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }
    
  • 将 Service设置为前台服务,这样就有比较高的优先级,在内存资源紧张时也不会被杀掉。

2、用户通过 settings -> Apps -> Running -> Stop 方式杀死Service
  • 这个过程会执行 Service 的生命周期,onDestory 方法会被调用,可以在 onDestory() 中发送广播重新启动
    这样杀死服务后会立即启动。这种方案是行得通的

  • 可开启两个服务,相互监听,相互启动
    服务A监听B的广播来启动B,服务B监听A的广播来启动A

    public class ServiceKilledByAppStop extends Service {
    
      private BroadcastReceiver mReceiver;
      private IntentFilter mIF;
    
      @Nullable
      @Override
      public IBinder onBind(Intent intent) {
      	return null;
      }
    
      @Override
      public void onCreate() {
      	super.onCreate();
      	mReceiver = new BroadcastReceiver() {
      		@Override
      		public void onReceive(Context context, Intent intent) {
      			Intent a = new Intent(ServiceKilledByAppStop.this, ServiceKilledByAppStop.class);
      			startService(a);
      		}
      	};
      	mIF = new IntentFilter();
      	//自定义action
      	mIF.addAction("com.restart.service");
      	//注册广播接者
      	registerReceiver(mReceiver, mIF);
      }
    
      @Override
      public void onDestroy() {
      	super.onDestroy();
      	Intent intent = new Intent();
      	intent.setAction("com.restart.service");
      	//发送广播
      	sendBroadcast(intent);
      	unregisterReceiver(mReceiver);
      }
    }
    

HandlerThread

1、HandlerThread 是一个线程类,它继承了Thread
2、HandlerThread 有自己的内部 Looper对象,可以进行looper循环;
3、通过获取 HandlerThread 的 looper对象,传递给Handler对象,可以在handleMessage方法中执行异步任务。
4、创建 HandlerThread后,必须先调用HandlerThread.start()方法
Thread会先调用run方法,创建Looper对象。
//创建异步 HandlerThread
HandlerThread handlerThread = new HandlerThread("thread_name");
//必须先开启线程,内部会创建属于该线程的Loop对象
handlerThread.start();
//子线程Handler
Handler childHandler = new Handler(handlerThread.getLooper()) {
	@Overide
	public void handleMessage(Message msg) {
		//在 HandlerThread 线程中执行
	}
};

IntentService的使用及原理

  • 是一种特殊的 Service, 继承自Service,是一个抽象类

  • 可以用于在后台【执行耗时的异步任务,当任务完成后会自动停止

  • 拥有【较高的优先级】,不易被系统杀死(继承自Service的缘故),适合执行一些高优先级的异步任务

  • 内部通过 HandlerThreadHandler 实现异步操作

  • 创建 IntentService时,只需【实现onHandleIntent】和构造方法,onHandleIntent为异步方法,可以执行耗时操作

推荐阅读:

《Android开发艺术探索》 第九章 9.3节Service的工作过程
关于Android Service真正的完全详解,你需要知道的一切
Android 多线程之IntentService 完全详解
Android 多线程之HandlerThread 完全详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值