android service使用详解一
Posted Ruffian-痞子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android service使用详解一相关的知识,希望对你有一定的参考价值。
2017年终总结中说要在android的坑中滚到底,并且今年要进一步提升Android基础技能,由于最近公司的项目实在很忙,导致博客更新一拖再拖,眼看再这么下去博客更新又要废掉了,赶紧抽空写一写又是一年跳槽旺季,似乎好多人又开始蠢蠢欲动看机会,这时候又得面对各种面试。
于是你在简历上写:1.精通安卓四大组件;...
面试官:”那就简单说说service,有什么作用?与thread的关系?...“你:“...”(卧槽,我真的了解service了吗?)
你口难开,第一感觉,耗时操作呗,具体点呢?说明还是不了解嘛,知道startService/stopService就是了解或者熟悉了?如果真正熟悉,一定不会说不上来,至于怎么表达应该没有什么标准答案。来回就是围绕着那些知识点表述而已。
嗯~废话少说,service会分几篇来讲,这是第一篇从最简单的使用开始说起。
- service 简单上手使用(startService/stopService)
- service 在manifest配置文件中参数说明
- service 与Activity(其它组件)通信方式
- service 两种启动方式关系说明
- service onStartCommand返回值详解
- service 与thread的关系
service 简单上手使用(startService/stopService)
service通常叫做后台服务,说明它是在后台(通常情况下)为其他组件提供服务的,没有用户交互页面,启动之后长时间存在,直到被主动停止或者系统内存不足kill掉。继承Service实现ServiceDemo
public class LocalService extends Service
public static final String TAG = "LocalService";
@Override
public void onCreate()
super.onCreate();
LogUtil.outE(TAG, "onCreate");
@Override
public int onStartCommand(Intent intent, int flags, int startId)
LogUtil.outE(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
@Override
public void onDestroy()
super.onDestroy();
LogUtil.outE(TAG, "onDestroy");
@Override
public IBinder onBind(Intent intent)
return null;
代码很简单,没有任何业务逻辑,重写生命周期,打印log方便调试。service是基本组件,跟activity一样需要在
`manifest` 中注册之后才能正常使用
<service android:name=".service.LocalService" />
这里插入说明,如果没有特殊需求,一般只需要在
`manifest` 注册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>
跟activity相比也很多相似的声明,例如:label,name,icon,主要讲解一下特殊的声明:
- android:exported :是否能被其他应用隐式调用,跟activity一样,其他应用可以通过隐式调用唤起activity/service,如果设置为 false 则无法被其他应用调用。设置为true时,根据intent-filter匹配。默认值:如果有intent-filter,默认值为true,否则为false
- android:process :是否在单独进程中运行。当设置为android:process=":remote",代表此service在单独的进程中运行,注意" **:** " 很重要,意思是在当前进程名称前附加应用包名,"remote" 和 " :remote " 不是同一个意思。"remote"表示进程名称为 `remote` ; " :remote " 表示进程名称为 `app包名:remote`
特殊备注:
1.使用process会有一个坑,设置之后会导致Application的onCreate调用多次,可以通过当前进程名称判断是否做主进程初始化的逻辑。
2."remote"就是名称前面没有 ** : ** 的情况,不允许设置单独一个单词,会安装失败,通过测试,发现使用类似`com.remote` 或者 `.remote` 都可以,就是不允许一个单词 `remote` - android:permission :权限声明
- android:enabled :是否可以被系统实例化,默认为 true。父标签 也有 enable 属性,必须两个都为默认值 true 的情况下服务才会被激活,否则不会激活
starService()/stopService
启动服务,很简单,构建Intent,调用
`startService`
Intent intent = new Intent(MainActivity.this, LocalService.class);
startService(intent);
启动服务之后,service就一直运行在主线程中,如果没有逻辑操作,什么变化都没有,也没有返回值。
通过log可以看到,start之后service就一直运行着,即使activity已经销毁了,service一样运行在后台。
> 第一次调用 startService 调用 onCreate -> onStartCommand> 没有stop情况下,多次调用 startService 不再调用 onCreate 但是会多次调用 onStartCommand。
> 例如连续调用三次 startService
> onCreate -> onStartCommand
> onStartCommand
> onStartCommand
如果想要让service停止运行,需要调用stopService,或者在service处理完逻辑操作调用stopSelf函数才能让service停止运行
Intent intent = new Intent(MainActivity.this, LocalService.class);
stopService(intent);
在activity的
`onDestroy` 中调用 stopService,查看日志,发现service停止运行,并回调了
`onDestroy`如果候面试官问了一个问题:service与activity之间怎么进行数据交互?有几种方式?
你:这个。。。service在项目中很少用,所以不是很清楚
(所以说还是你不了解,很少使用?这就是你不会的借口?)
这时候面试官提示你:bindService知道吗?
你心里窃喜:知道,知道,绑定service嘛,第二种启动srevice的方式。
面试官:那你讲讲bindService怎么使用?为什么要用bindService?使用bindService的同时startService的情况下如何停止service??
你:...请问电梯是出门左拐吗?我先回去了~~~
OK,接下来就详细讲讲service与其它组件之间的通信方式,以及bindSercice相关的知识,希望学完之后不会再出现上面所说的打脸场景
service 与Activity(其它组件)通信方式
- 通过Binder对象
其实这个方式思想还是很简单的,因为service这种带生命周期的组件,不适合通过 new 创建对象,所以通过Binder得到service的实例,然后就可以用这个实例访问service的方法和成员了
private LocalService localService;
ServiceConnection connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
LogUtil.outE(TAG, "onServiceConnected " + componentName);
localService = ((LocalService.MyBinder) iBinder).getService();
@Override
public void onServiceDisconnected(ComponentName componentName)
LogUtil.outE(TAG, "onServiceDisconnected " + componentName);
;
通过 ServiceConnection 得到 IBinder ,调用 getService() (自己写的方法)得到 service 实例。
Intent intent = new Intent(MainActivity.this, LocalService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
bindService 也很简单,再看一下service中代码,修改 onBind
@Override
public IBinder onBind(Intent intent)
return new MyBinder();
MyBinder 继承 Binder ,添加一个获取service实例的方法
public class MyBinder extends Binder
public LocalService getService()
return LocalService.this;
> bindService 除了调用方式跟 startService 有所区别。其他逻辑还是一样的,生命周期执行也是首次绑定时调用 onCreate-> onStartCommand
> 之后调用都只调用 onStartCommand
现在模拟一个后台下载文件的demo详细看看通过Binder通信的方式,先看看service实现
public class DownloadService extends Service
//最大值
public static final int MAX_PROGRESS = 100;
//是否正在下载
private boolean isRunning = false;
//进度
private int progress = 0;
//获取进度
public int getProgress()
return progress;
/**
* 模拟下载任务
*/
public void startDownload()
if (!isRunning)
new Thread(new Runnable()
@Override
public void run()
progress = 0;
isRunning = true;
while (isRunning)
if (progress < MAX_PROGRESS)
progress += 5;
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
else
isRunning = false;
).start();
@Override
public IBinder onBind(Intent intent)
return new MyBinder();
public class MyBinder extends Binder
public DownloadService getService()
return DownloadService.this;
这代码再简单不过了,应该都能看懂,启动线程,每秒更新进度,
`startDwonload` 和
`getProgress` 提供给外部调用,下载方法中做一个简单的判断,当前是否正在下载中,避免用户多次调用导致逻辑错误。
再看一下activity代码
public class MainActivity extends AppCompatActivity
private DownloadService mService;
private ProgressBar mProgressBar;
private int progress = 0;
private boolean isRunning = false;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
//绑定
Intent intent = new Intent(MainActivity.this, DownloadService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
//下载
findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
mService.startDownload();
listenProgress();
);
/**
* 监听进度,每秒钟获取调用DownloadService的getProgress()方法来获取进度,更新UI
*/
public void listenProgress()
if (!isRunning)
new Thread(new Runnable()
@Override
public void run()
progress = 0;
isRunning = true;
while (isRunning)
if (progress < DownloadService.MAX_PROGRESS)
progress = mService.getProgress();//调用service方法回去进度
mProgressBar.setProgress(progress);
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
else
isRunning = false;
).start();
ServiceConnection connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
mService = ((DownloadService.MyBinder) iBinder).getService();
@Override
public void onServiceDisconnected(ComponentName componentName)
;
@Override
protected void onDestroy()
super.onDestroy();
unbindService(connection);
activity的代码也不难,这里先不管代码风格和编程方式是否规范,为了让逻辑直观和简介,一切从简,只为更好理解。这里需要注意的一点是,更新UI是要在主线程中操作,ProgressBar 这个控件估计是内部做了处理,这里先不管了
OK,到这里,我们使用了bindService模拟了下载任务,实现了service和activity之间的数据交互。但是!!!但是不觉得这个业务使用这种主动获取数据的方式很麻烦吗?需要activity主动询问service数据。还有什么方式?
其实前辈们一直强调说基础要打扎实,只有基础扎实了才能将知识融会贯通,使用更优雅的方式解决问题。一般多线程之间的数据交互方式有哪些呢?回调?广播?...
- 回调函数
public interface OnProgressListener
void onProgress(int progress);
service业务逻辑中,进度变化时设置进度
public class DownloadService extends Service
//最大值
public static final int MAX_PROGRESS = 100;
//是否正在下载
private boolean isRunning = false;
//进度
private int progress = 0;
//获取进度
public int getProgress()
return progress;
/**
* 模拟下载任务
*/
public void startDownload()
if (!isRunning)
new Thread(new Runnable()
@Override
public void run()
progress = 0;
isRunning = true;
while (isRunning)
if (progress < MAX_PROGRESS)
progress += 5;
//进度设置
if (mListener != null)
mListener.onProgress(progress);
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
else
isRunning = false;
).start();
@Override
public IBinder onBind(Intent intent)
return new MyBinder();
public class MyBinder extends Binder
public DownloadService getService()
return DownloadService.this;
/**
* 定义回调接口
*/
public interface OnProgressListener
void onProgress(int progress);
//回调接口
private OnProgressListener mListener;
/**
* 设置监听
*
* @param listener
*/
public void setOnProgressListener(OnProgressListener listener)
this.mListener = listener;
然后在activity中修改为监听数据,不用再主动获取
public class MainActivity extends AppCompatActivity
private DownloadService mService;
private ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
//绑定
Intent intent = new Intent(MainActivity.this, DownloadService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
//下载
findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
mService.startDownload();
);
ServiceConnection connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder)
mService = ((DownloadService.MyBinder) iBinder).getService();
/**
* 设置监听
*/
mService.setOnProgressListener(new DownloadService.OnProgressListener()
@Override
public void onProgress(int progress)
mProgressBar.setProgress(progress);
);
@Override
public void onServiceDisconnected(ComponentName componentName)
;
@Override
protected void onDestroy()
super.onDestroy();
unbindService(connection);
舒服,不用定时去查询获取数据,只要设置一个监听,当service有数据更新时就会回调,activity只需要收到数据更新UI就可以了。等一下,什么是回调函数?这可能需要你去查查资料哦,如果到现在为止你还不知道什么是回调函数,,那你的路就还很长哦,赶紧去补!
可能很多小伙伴听到回调函数的时候就想到另外一种方式了:广播。也很相似的思路,service的数据发生改变的时候主动发送广播,在activity中设置广播监听,收到数据更新UI
- 广播
service代码
public class DownloadService extends Service
//最大值
public static final int MAX_PROGRESS = 100;
//是否正在下载
private boolean isRunning = false;
//进度
private int progress = 0;
//广播intent
private Intent intent = new Intent("com.ruffian.download.action");
/**
* 模拟下载任务
*/
public void startDownload()
if (!isRunning)
new Thread(new Runnable()
@Override
public void run()
progress = 0;
isRunning = true;
while (isRunning)
if (progress < MAX_PROGRESS)
progress += 5;
//发送广播
intent.putExtra("progress", progress);
sendBroadcast(intent);
try
Thread.sleep(1000);
catch (InterruptedException e)
e.printStackTrace();
else
isRunning = false;
).start();
@Override
public IBinder onBind(Intent intent)
return null;
@Override
public int onStartCommand(Intent intent, int flags, int startId)
startDownload();
return super.onStartCommand(intent, flags, startId);
activity中添加广播监听,别忘了注册和注销广播,说明一下:使用广播的情况不一定要 bindService 的方式,startService 也是可以的
public class MainActivity extends AppCompatActivity
private ProgressBar mProgressBar;
private MyReceiver myReceiver;
private Intent mIntent;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
//start
mIntent = new Intent(MainActivity.this, DownloadService.class);
//注册广播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.ruffian.download.action");
myReceiver = new MyReceiver();
registerReceiver(myReceiver, intentFilter);
//下载
findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
startService(mIntent);
);
@Override
protected void onDestroy()
super.onDestroy();
//解除绑定
stopService(mIntent);
//注销广播
unregisterReceiver(myReceiver);
/**
* 广播接收器
*/
public class MyReceiver extends BroadcastReceiver
@Override
public void onReceive(Context context, Intent intent)
//拿到进度,更新UI
int progress = intent.getIntExtra("progress", 0);
mProgressBar.setProgress(progress);
> service与activity(或者其它组件)的通信方式有以上3种,根据业务需要去选择最合适的一种,比如下载这种业务使用回调会是不错的选择;少量数据的获取也可使用bindservice得到service实例主动获取;对于多个activity都需要获取数据的业务使用广播则更方便一点。
service 两种启动方式关系说明
学会了 startService 和 bindService 。是不是又想急着去打游戏了呢?如果面试官再问你:如果使用 startService 之后再调用 bindService ,要如何停止服务呢?bind 之后再 start 的情况呢?卧槽,,,要不要问这么细?我已经做的很好了!还问! 巧了,真就有面试官会这么干,看你到底掌握了多少,深度多深~~
莫慌,给一句话: **只要调用了 start 就必须用 stop 才能停止服务。如果单一 bind 那么 unBind 就可以停止服务了**
service onStartCommand返回值详解
到现在为止,基本上把service的基础知识过了一遍,回过头来看看service的类,onCreate ,onDestory 没什么可讲的,但是这个 `onStartCommand` 好像还没仔细研究一下,是不是之前还见过 onStart 呢?卧槽,现在是不是又想问 onStart 跟 onStartCommand 的关系?
没错,我就是想问,你砍我呀。
这个比较简单,onStart 是 <= API5之前的回调,在 >API5 之后这个方法不直接回调了,而是放在 onStartCommand 中, 看一下源码
public int onStartCommand(Intent intent, int flags, int startId)
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
所以实现service时候直接重新 onStartCommand 就可以了。这里的intent 是启动组件传递过来的Intent。实际上 onStartCommand 的返回值 int 类型才是最最值得注意的,它有4种可选值
START_STICKY
START_NOT_STICKY
START_REDELIVER_INTENT
START_STICKY_COMPATIBILITY
具体说明一下:
- START_STICKY :当service因内存不足被系统kill掉后,一段时间后当内存再次空闲时,会重启service ,回调 onStartCommand 方法,START_STICKY状态下,startId 改变,重启时 intent==null
- START_NOT_STICKY : 当service因内存不足被系统kill掉后,一段时间后当内存再次空闲时,系统不再自动重启service
- START_REDELIVER_INTENT :当service因内存不足被系统kill掉后,一段时间后当内存再次空闲时,会重启service ,回调 onStartCommand 方法,START_REDELIVER_INTENT 状态下,startId 不变,重启时 intent == 后一次调用startService中的intent
- START_STICKY_COMPATIBILITY :当service因内存不足被系统kill掉后,一段时间后当内存再次空闲时,会重启service,但是只调用 oncreate 方法,没有调用 onStartCommand
由于每次启动服务(调用startService)时,onStartCommand方法都会被调用,因此我们可以通过该方法使用Intent给Service传递所需要的参数,然后在onStartCommand方法中处理的事件,最后根据需求选择不同的Flag返回值,以达到对程序更友好的控制。
service 与thread的关系
前面的例子中我们都看到了,在service中需要创建thread线程做业务逻辑,耗时操作。所以之前一直说用service来做耗时操作的说法可能是错误的喔~其实service运行在主线程(UI线程)
那么问题来了,说好的在service中做耗时操作呢?在主线程中运行,那么超过10s不是ANR了?
我觉得:这里需要把service当做一个对象来理解,他运行在主线程中,start之后就一直运行,但是所谓的耗时操作是需要创建Thread来实现,一个thread的逻辑处理完了,thread结束了,但是service还是运行着的,直到调用stop或者被系统kill掉。
所以开口就说用service做耗时操作,估计就是在网上看了一下面试答案就来了。应该是在service的工作线程中才能做耗时操作,如果你质疑我,自己去尝试,直接在 `onCreate()` 中休眠20s,必定ANR
@Override
public void onCreate()
super.onCreate();
LogUtil.outE(TAG, "onCreate");
try
Thread.sleep(20000);
catch (InterruptedException e)
e.printStackTrace();
所以现在要搞明白了,service确实运行在主线程,所谓的耗时操作实在service的工作线程中进行的。
那么service 与thread 之间有什么关系呢? 这个可以给肯定答案: 没有半毛钱关系!
thread: 是程序执行的最小单元,它是分配CPU的基本单位,android UI线程也是thread的一种,thread常用来做异步耗时的操作。
service: android的一个组件,运行在主线程中,由系统托管,并不能用来执行耗时操作,常说的Service后台任务只不过是指没有UI的组件罢了
这么说service要做耗时任务都是要创建thread咯?不是的,体统提供了IntentService,他是service的一个实现类,就是为了解决在service中做耗时操作处理而诞生的,原理还是创建thread,只不过开发者不需要关心这部分逻辑,由于篇幅太长,这部分知识放到下一篇博客中将。
看客们可以持续关注,只要你坚持不懈,我就可以一步一步带你到饿死的地步~~~总是有一些什么小程序,快应用什么的想来抢饭碗,想让安卓开发者饿死,然而呢?不存在的,只要我们够吊~~~
以上是关于android service使用详解一的主要内容,如果未能解决你的问题,请参考以下文章