Service
service前言
service是什么?
1.四大组件之一
2.没有与任何活动页面牵连
3.可以在后台进行跨度时间长的工作
4.在创建他的进程进行,开启耗费时间的工作的时候在分线程
可以干什么?
1.访问网络,文件读写,播放音乐,大数据的数据库操作
特点:
1.不用于页面交互
2.应用退出之后他不会退出
3.默认情况下,在ui线程创建
4.可以开启分线程来应对网络堵塞
理解:
1.与活动区别:
1.是否与页面交互: 服务:不 活动:交互
2.应用退出之后的状态:服务:活着 活动:销毁
3.应用再次进入的状态:服务:不变 活动:重建
2.与分线程区别
1.时间耗费:服务:时间耗费跨度大。 线程:所耗时间长
2.应用退出之后的状态:服务:存在 线程:存在
3.应用再次进入的状态:服务:不变 线程:不是前一个了
4.所属线程:服务:UI 线程:workthread
PS:分线程属性isBackground:默认为false,当true时候:会与UI同生共死
多线程编程
线程基本用法:3种
1.new Thread(new Runnable(){run{ }})
2.new Thread(new Runnable(){ })
3.new Thread(new Runnable(){ })
//a.
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
//b.
//service service = new service();
new service().start();
public class service extends Thread{
@Override
public void run() {
super.run();
}
}
//c.
service2 service2 = new service2();
new Thread(service2).start();*/
public class service2 implements Runnable{
@Override
public void run() {
}
子线程中更新UI:
1.Handler:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
tv_service.setText("nice to meet you");
break;
default:
break;
}
}
};
tv_service = findViewById(R.id.tv_service);
btn_service = findViewById(R.id.btn_service);
btn_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
});
2.AsyncTask:
new Asy().execute();
class Asy extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {//ui
super.onPreExecute();
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... voids) {//worker
while (true){
int down = 10;
publishProgress(10);
if(down >= 100)
break;
}
return true;
}
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
progressDialog.dismiss();
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressDialog.setMessage(values[0]+"%");
}
}
3.异步消息处理机制
1.Message:就是上面handler使用方法
2.AysncTask:上面那种
服务基本用法
定义一个Service:
1.startService
Intent i = new Intent(MainActivity.this, MyService.class);//启动
startService(i);
2.stopservie:
Intent i = new Intent(MainActivity.this, MyService.class);//stop
stopService(i);
3.启动停止:
3.1.启动bindservice:
Intent i = new Intent(MainActivity.this, MyService.class);
//bind
bindService(i,conn,BIND_AUTO_CREATE);
3.2.停止unbindservice:
Intent i = new Intent(MainActivity.this, MyService.class);
//unbindService
unbindService(conn);
4.活动和服务通信:通过service中的OnBind
conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService.DownBinder downBinder = (MyService.DownBinder) service;
downBinder.start();
downBinder.getprogress();
}
@Override
public void onServiceDisconnected(ComponentName name) {
//
}
};
btn_bind_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(MainActivity.this, MyService.class);
//bind
bindService(i,conn,BIND_AUTO_CREATE);
}
});
private DownBinder mbinder;
public IBinder onBind(Intent intent) {
return mbinder;
}
class DownBinder extends Binder{
public void start(){
Log.d("DownBinder", "start:DownBinder ");
}
public int getprogress(){
Log.d("DownBinder", "start:stop ");
return 0;
}
}
5.前台服务:startForeground
Intent i = new Intent(MyService.this,MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(MyService.this,REQUEST_CODE,i,FLAG);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("title")
.setContentText("text")
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round))
.setContentIntent(pi)
.build();
startForeground(ID,notification);
6.IntentService:main里 目的:自动提供开启分线程和自动解绑的
public class MyIntentService extends IntentService{
//原理:把ibinder包装成了一个msg,oncreate里面自己创建了looper,handler,HandlerThread之类,handlermsg中onHandleIntent,所以onHandleIntent是在分线程工作。
privatefinalclassServiceHandlerextendsHandler{
publicServiceHandler(Looperlooper){
super(looper);
}
@Override
publicvoidhandleMessage(Messagemsg){
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {//这个是
//耗时
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
服务生命周期
生命周期:
1.Call to startService - >oncreate->onstartCommand->serviceRunning->onDestory
2.Call to bindService - >oncreate->onBind->serviceRunning->onUnbind->onDestory
3.开启startservice+bindService时候怎么完全关:(stopself/stopservice) + unbindservice
startservice:为了长时间开启后台服务,bindService:为了通信
同时开启的生命周期:
手动Startservice->oncreate->onstartcommand->手动bindService->onBind->手动unBindService->onUnbind->手动stopService->onDestory
服务实例–下载
main中
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int WRITE_REQUEST = 1;
private Button btn_start;
private Button btn_pause;
private Button btn_cancel;
//private DownloadService downloadService;
private DownloadService.DownloadBinder downloadBinder ;//为了调用service里面的方法,onbind传输数据
private ServiceConnection conn = new ServiceConnection() {//bindservice要传的参数conn,在这从写匿名内部类
@Override
public void onServiceConnected(ComponentName name, IBinder service) {//活动与服务成功绑定时
downloadBinder = (DownloadService.DownloadBinder) service;//service向下转型,得到downloadBinder实例
//绑定成功后在这样得到实例,使活动和服务更加关系紧密
//没绑定成功如果使用一般的new可能会报错吧
}
@Override
public void onServiceDisconnected(ComponentName name) {//活动与服务没有成功绑定时
//
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化
init();
btn_start.setOnClickListener(this);
btn_pause.setOnClickListener(this);
btn_cancel.setOnClickListener(this);
//创建服务
Intent i = new Intent(this,DownloadService.class);
startService(i);//启动服务,为了一直在后台运行
bindService(i,conn,BIND_AUTO_CREATE);//绑定服务,为了通信 记住!!!第一次都忘写了
//权限申请
//先判断是否有这个权限
if(ContextCompat.checkSelfPermission(MainActivity.this
, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
//没有的话,申请一下权限看他同意吗,结果直接跳转到方法onRequestPermissionsResult
ActivityCompat.requestPermissions(MainActivity.this
, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
,WRITE_REQUEST);
}
}
//权限申请结束后自动调用的方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode){
case WRITE_REQUEST:
if(grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED ){
Toast.makeText(this,"拒绝了",Toast.LENGTH_SHORT).show();
//finish();
}else {
Toast.makeText(this,"接受了",Toast.LENGTH_SHORT).show();
}
default:
break;
}
}
private void init() {
btn_start = findViewById(R.id.btn_start);
btn_pause = findViewById(R.id.btn_pause);
btn_cancel = findViewById(R.id.btn_cancel);
}
//活动销毁时记得关绑定
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(conn);
}
@Override
public void onClick(View v) {
if(downloadBinder== null)//..
return ;
switch (v.getId()){
case R.id.btn_start://自己创建一个下载地址,后台服务下载,调用服务的startDownload方法
String url = "https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";
downloadBinder.startDownload(url);
break;
case R.id.btn_pause://后台服务停止
downloadBinder.pauseDownload();
break;
case R.id.btn_cancel://后台服务取消
downloadBinder.cancelDownload();
break;
default:
break;
}
}
}
DownloadService中
public class DownloadService extends Service {//控制下载进程,前台通知是否展现,新建通知
public NotificationManager getNotificationManager(){//为了得到NotificationManager,进行通知的notify
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
//NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
private DownloadInterface downloadlistener = new DownloadInterface() {//重写接口方法,这个接口是为了更好的反应下载的结果
@Override
public void OnProgress(int progress) {//更新通知,下载的进度
/*Intent i = new Intent(DownloadService.this,MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(DownloadService.this,0,i,0);
Notification notification = new NotificationCompat
.Builder(DownloadService.this)
.setContentTitle("title")
.setContentIntent(pi)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.setWhen(System.currentTimeMillis())
.build();*/
getNotificationManager().notify(DOWNLOAD,getNotification("downloading",progress));
}
//控制下载进程,前台通知是否展现,新建通知
@Override
public void OnSuccess() {//下载结果是成功:
downloadTask = null;//线程关闭
stopForeground(true);//停止前台服务
getNotificationManager().notify(1,getNotification("onsuccess",-1));//更新通知
}
@Override
public void OnFailed() {
downloadTask = null;//线程关闭
stopForeground(true);//停止前台服务
getNotificationManager().notify(1,getNotification("onfailed",-1));//更新通知
}
@Override
public void OnCanceled() {
downloadTask = null;//线程关闭
stopForeground(true);//停止前台服务
getNotificationManager().notify(1,getNotification("oncanceled",-1));//更新通知
}
@Override
public void OnPaused() {
downloadTask = null;//线程关闭
//不用停止前台,可能之后会重启下载任务之类的
getNotificationManager().notify(1,getNotification("oncanceled",-1));//更新通知
}
};
private DownloadTask downloadTask ;//在AsyncTask中进行耗时任务
private DownloadBinder mbinder = new DownloadBinder();//自己创建一个类exendsbinder,之后使得活动能调用这个类的方法
private int DOWNLOAD = 1;
private String lasturl = null;//下载地址
@Nullable
@Override
public IBinder onBind(Intent intent) {//通信
return mbinder;
}
public class DownloadBinder extends Binder {//自己创建的类,为了返给onbind方法
public void startDownload(String url) {//他的点击前的下载状态:暂停/没开始下载。点完btn_start之后调用这个,参数是下载的地址
if(downloadTask == null){//暂停/没开始下载
lasturl = url;
downloadTask = new DownloadTask(downloadlistener);//创建异步任务
downloadTask.execute(lasturl);//执行
startForeground(DOWNLOAD,getNotification("startDownload",0));//开启前台服务
}
}
public void pauseDownload() {//他的点击前的下载状态:开始。点完btn_pause之后调用这个
if(downloadTask != null){//下载当前状态不为null,暂停才有意义
/*downloadTask = new DownloadTask(downloadlistener);
downloadTask*/
downloadTask.pauseDownload();//这时候已经经历过上面的startDownload了,所以可以直接调用异步任务的pauseDownload方法
}
}
public void cancelDownload() {//他的点击前的下载状态:开始/暂停/没开始下载/下载完的
if(downloadTask == null){//现在下载状态是 没开始下载/暂停/下载完的 ,则要删除文件
if(lasturl != null){//下载地址为空说明不用删除啥,现在不为空,进行下面的
//得到下载的名字和地址
String filename = lasturl.substring(lasturl.lastIndexOf("/"));//名字
String path = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.getPath();//地址:在SD卡中,可通过这个语句找到
File file = new File(path+filename);//创建文件用这个名字
if(file.exists()){//存在就删除,不存在就说明 当前状态是 未开始 就被取消了
file.delete();
}
getNotificationManager().cancel(DOWNLOAD);//这个方法是 通知被取消
stopForeground(true);//移除前台服务
}
}else{//说明当前下载状态是downloadTask != null正开始之后在下载中
downloadTask.cancelDownload();
// stopForeground();
}
}
}
private Notification getNotification(String title,int progress){//通知,为了简化代码
Intent i = new Intent(DownloadService.this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(DownloadService.this
,0,i,0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle(title);//每个调用的地方可能title不同,所以作为参数传入
builder.setContentIntent(pi);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
builder.setWhen(System.currentTimeMillis());
if(progress > 0){//进度
builder.setContentText(progress+"%");
builder.setProgress(100,progress,false);//(最大,现在进度,是否模糊化)
}
return builder.build();
}
}
接口中:
public interface DownloadInterface {
//接口的作用是:规范化代码
//这几个方法是对结果的回应
void OnProgress(int progress);//显示当前进度
void OnSuccess();//结果是成功
void OnFailed();
void OnCanceled();
void OnPaused();
}
DownloadTask中:
public class DownloadTask extends AsyncTask<String,Integer,Integer> {//异步任务,处理耗时操作
private DownloadInterface listener;//为了接收服务传入的监听器
// private DownloadService downloadService;
private int lastprogress;//最新进度
public static final int TYPE_SUCEESS = 1;
public static final int TYPE_FAILED = 2;
public static final int TYPE_CANCELED = 3;
public static final int TYPE_PAUSED = 4;
public DownloadTask(DownloadInterface listener) {//为了传入监听器做的有参
this.listener = listener;
}
private boolean isPause = false;//先定义为假
public void pauseDownload() {//当异步任务在服务中调用了这个方法时,说明main活动按了btn_pause
isPause = true;//在doInBackground下载任务中就可以暂停了,暂时就不用下载了
}
private boolean isCancel = false;//先定义为假
public void cancelDownload() {//当异步任务在服务中调用了这个方法时,说明main活动按了btn_cancel
isCancel = true;//在doInBackground下载任务中就可以取消了,就不用下载了
}
@Override
protected Integer doInBackground(String... strings) {
InputStream is = null;//电脑<----别的 所以用is
RandomAccessFile randomAccessFile = null;//这个是保存下载的文件
File file = null;//这个是为了判断的文件
try {
String downurl = strings[0];//下载地址
String downurlname = downurl.substring(downurl.lastIndexOf("/"));//名字
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();//
file = new File(path + downurlname);
long correntlength = 0;//当前文件长度
if(file.exists()){
//判断之前是否下载过一点,获取当前长度
correntlength = file.length();
}//没有文件的话,correntlength就还是0
long successlength = 0;//成功的文件长度,后面在赋正确的值用OkHttpClient
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.url(downurl)
.build();
Response response = okHttpClient.newCall(request).execute();
if(response != null && response.isSuccessful()){
successlength = response.body().contentLength();//得到成功的文件长度
response.close();//关流
}
//现在成功的文件长度不为0了
//对长度进行判断
if(correntlength == successlength){//下载完了
return TYPE_SUCEESS;
}else if(successlength == 0){//没获取的到正确的下载地址,下载的东西没获取到
return TYPE_FAILED;
}
//正常下载中 的情况
OkHttpClient okHttpClient1 = new OkHttpClient();
Request request1 = new Request.Builder()
.url(downurl)
.addHeader("RANGE","bytes="+correntlength+"-")//断点处下载
.build();
Response response1 = okHttpClient1.newCall(request1).execute();
if(response1 != null){//说明获取到了正确的response,可以正常下载了
is = response1.body().byteStream();
randomAccessFile = new RandomAccessFile(file,"rw");
randomAccessFile.seek(correntlength);//找到当前位置下载
//下载
long total = 0;//这次下载的总长度(不包括correntlength)。每次读取的长度是不一样的
int len = 0;
byte[] arr = new byte[1024];
while ((len = is.read(arr)) != -1){
if(isPause){//点了btn_pause
return TYPE_PAUSED;
}else if(isCancel){//点了btn_cancel
return TYPE_CANCELED;
}else{//啥也没点正常运行
total += len;
randomAccessFile.write(arr,0,len);//写入
int progress = (int) ((total + correntlength) *100 / successlength);
// listener.OnProgress(progress);
publishProgress(progress);//发布进度,这个会自动调用onProgressUpdate方法
}
}
//下载完了后
response1.body().close();//关流
return TYPE_SUCEESS;
}
} catch (IOException e) {
e.printStackTrace();
}finally{//最后
try {
if(is != null){//关流
is.close();
}
if(randomAccessFile != null){//关流
randomAccessFile.close();
}
if(isCancel && file != null){//点击btn_cancel的,删除文件
file.delete();
}
}catch (IOException e) {
e.printStackTrace();
}
}
return TYPE_FAILED ;
}
@Override
protected void onPostExecute(Integer integer) {//对doInBackground的return的处理
super.onPostExecute(integer);
switch (integer){//创造新通知,接口通知方法在service里面
case TYPE_SUCEESS:
listener.OnSuccess();
break;
case TYPE_FAILED:
listener.OnFailed();
break;
case TYPE_CANCELED:
listener.OnCanceled();
//break;
case TYPE_PAUSED:
listener.OnPaused();//
break;
default:
break;
}
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
int progress = values[0];//values[0]中存在传过来的参数publishProgress(progress);
if(progress > lastprogress){//!!!
//更新通知,接口方法在service里面
//为什么在service中是因为,他要对异步任务进行null,在本类的重写的话不能进行这个操作
listener.OnProgress(progress);//发布任务,服务的OnProgress那边会更新通知
lastprogress = progress;//最新进度更新为 传来的最新进度
}
}
}
面试
1.1 注册Service需要注意什么
Service还是运行在主线程当中的,所以如果需要执行一些复杂的逻辑操作,最好在服务的内部手动创建子线程(AysncTask)进行处理,否则会出现UI线程被阻塞的问题
1.2 Service与Activity怎么实现通信:类extends Binder 这个类的对象给onbind
第一种方式:通过MyBinder方式调用Service方法:serviceconnection那向下转型变成的是MyBinder类型
第二种方式:通过接口Iservice调用Service方法 多了个接口:serviceconnection那向下转型变成的是Iservice类型
1.3 介绍源码中Binder机制(了解)(不太懂)
面试问到这个,其实就是让你说一下binder是干什么的,Service Manager是如何成为一个守护进程的
Binder是干什么的
Binder – 一种进程间通信(IPC)机制, 基于OpenBinder
1.Client、Server和Service Manager实现在用户空间中,Binder驱动程序实现在内核空间中
2.Binder驱动程序和Service Manager在Android平台中已经实现,开发者只需要在用户空间实现自己的Client和Server
3.Binder驱动程序提供设备文件/dev/binder与用户空间交互,Client、Server和Service Manager通过open和ioctl文件操作函数与Binder驱动程序进行通信
4.Client和Server之间的进程间通信通过Binder驱动程序间接实现
5.Service Manager是一个守护进程,用来管理Server,并向Client提供查询Server接口的能力
1.Service Manager是如何成为一个守护进程的?即Service Manager是如何告知Binder驱动程序它是Binder机制的上下文管理者。
Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能
既然Service Manager组件是用来管理Server并且向Client提供查询Server远程接口的功能,那么,Service Manager就必然要和Server以及Client进行通信了。我们知道,Service Manger、Client和Server三者分别是运行在独立的进程当中,这样它们之间的通信也属于进程间通信了,而且也是采用Binder机制进行进程间通信,因此,Service Manager在充当Binder机制的守护进程的角色的同时,也在充当Server的角色,然而,它是一种特殊的Server,下面我们将会看到它的特殊之处
Service Manager在用户空间的源代码位于frameworks/base/cmds/servicemanager目录下,主要是由binder.h、binder.c和service_manager.c三个文件组成。Service Manager的入口位于service_manager.c文件中的main函数:
int main(int argc, char **argv)
{
struct binder_state *bs;
void svcmgr = BINDER_SERVICE_MANAGER;
bs = binder_open(1281024);
if (binder_become_context_manager(bs)) {
LOGE(“cannot become context manager (%s)\n”, strerror(errno));
return -1;
}
svcmgr_handle = svcmgr;
binder_loop(bs, svcmgr_handler);
return 0;
}
main函数主要有三个功能:一是打开Binder设备文件;二是告诉Binder驱动程序自己是Binder上下文管理者,即我们前面所说的守护进程;三是进入一个无穷循环,充当Server的角色,等待Client的请求
2.Server和Client是如何获得Service Manager接口的?即defaultServiceManager接口是如何实现的。
ServiceManager 作为守护进程,Service Manager的职责当然就是为Server和Client服务了。那么,Server和Client如何获得Service Manager接口,进而享受它提供的服务呢?
Service Manager在Binder机制中既充当守护进程的角色,同时它也充当着Server角色,然而它又与一般的Server不一样。对于普通的Server来说,Client如果想要获得Server的远程接口,那么必须通过Service Manager远程接口提供的getService接口来获得,这本身就是一个使用Binder机制来进行进程间通信的过程。而对于Service Manager这个Server来说,Client如果想要获得Service Manager远程接口,却不必通过进程间通信机制来获得,因为Service Manager远程接口是一个特殊的Binder引用,它的引用句柄一定是0。
经过一系列的调用…
回到defaultServiceManager函数中,最终结果为:
gDefaultServiceManager = new BpServiceManager(new BpBinder(0));
这样,Service Manager远程接口就创建完成了,它本质上是一个BpServiceManager,包含了一个句柄值为0的Binder引用。
在Android系统的Binder机制中,Server和Client拿到这个Service Manager远程接口之后怎么用呢?
对Server来说,就是调用IServiceManager::addService这个接口来和Binder驱动程序交互了,即调用BpServiceManager::addService 。而BpServiceManager::addService又会调用通过其基类BpRefBase的成员函数remote获得原先创建的BpBinder实例,接着调用BpBinder::transact成员函数。在BpBinder::transact函数中,又会调用IPCThreadState::transact成员函数,这里就是最终与Binder驱动程序交互的地方了。回忆一下前面的类图,IPCThreadState有一个PorcessState类型的成中变量mProcess,而mProcess有一个成员变量mDriverFD,它是设备文件/dev/binder的打开文件描述符,因此,IPCThreadState就相当于间接在拥有了设备文件/dev/binder的打开文件描述符,于是,便可以与Binder驱动程序交互了。
对Client来说,就是调用IServiceManager::getService这个接口来和Binder驱动程序交互了。具体过程上述Server使用Service Manager的方法是一样的,这里就不再累述了。
1.4 IntentService与Service的区别(intentservice的优点)
IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题
会创建独立的worker线程来处理所有的Intent请求;
会创建独立的worker线程来处理onHandleIntent()方法实现的代码,无需处理多线程问题;
所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;
为Service的onBind()提供默认实现,返回null;
为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;
IntentService不会阻塞UI线程,而普通Serveice会导致ANR异常
Intentservice若未执行完成上一次的任务,将不会新开一个线程,是等待之前的任务完成后,再执行新的任务,等任务完成后再次调用stopSelf()
1.5 Service 是否在 main thread 中执行, service 里面是否 能执行耗时的操作?
默认情况,如果没有显示的指 service 所运行的进程, Service 和 activity 是运 行在当前 app 所在进程的 main thread(UI 主线程)里面。
service 里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 )
特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行
1.6 Service的生命周期
Service 有绑定模式和非绑定模式,以及这两种模式的混合使用方式。不同 的使用方法生命周期方法也不同。
非绑定模式:当第一次调用 startService 的时候执行的方法依次为 onCreate()、onStartCommand(),当 Service 关闭的时候调用 onDestory 方 法。
绑定模式:第一次 bindService()的时候,执行的方法为 onCreate()、 onBind()解除绑定的时候会执行 onUnbind()、onDestory()。
上面的两种生命周期是在相对单纯的模式下的情形。我们在开发的过程中还 必须注意 Service 实例只会有一个,也就是说如果当前要启动的 Service 已经存在了那么就不会再次创建该 Service 当然也不会调用 onCreate()方法。
一个 Service 可以被多个客户进行绑定,只有所有的绑定对象都执行了unBind()方法后该 Service才会销毁,不过如果有一个客户执行了onStart方法,那么这个时候如果所有的 bind 客户都执行了 unBind()该Service也不会销毁。
1.7 Activity、Intent、Service 是什么关系(了解)
他们都是 Android 开发中使用频率最高的类。其中 Activity 和 Service 都是 Android 四大组件之一。他俩都是 Context 类的子类 ContextWrapper 的子类, 因此他俩可以算是兄弟关系吧。不过兄弟俩各有各自的本领,Activity 负责用户 界面的显示和交互,Service 负责后台任务的处理。Activity 和 Service 之间可 以通过 Intent 传递数据,因此可以把 Intent 看作是通信使者。
1.8 Service 和 Activity 在同一个线程吗?
对于同一 app 来说默认情况下是在同一个线程中的,main Thread (UI Thread)。
1.9 在 service 的生命周期方法 onstartConmand()可不可以执行网络操作?如何在 service 中执行网络操作?(在service如何执行耗时操作)
可以直接在 Service 中执行网络操作,在 onStartCommand()方法中可以执行网络操作
如果需要在服务中进行耗时操作,可以选择IntentService, IntentService是Service的子类,用来处理异步请求。
IntentService在onCreate()方法中通过HandlerThread单独开启一个线程来处理Intent请求对象所对应的任务,这样可以避免事务处理阻塞主线程。
onHandleIntent()函数针对Intent的不同进行不同的事务处理就可以,执行完一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service; 否则ServiceHandler会取得下一个Intent请求
传入该函数来处理其所对应的任务。
2.0 如何提高service的优先级? (理解,说出两三条就很nB了)
参考文章
1、在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时实用于广播。
2、在onStartCommand里面调用 startForeground()方法把Service提升为前台进程级别,然后再onDestroy里面要记得调用stopForeground ()方法。
3、onStartCommand方法,手动返回START_STICKY。
4、 在onDestroy方法里发广播重启service。
service +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。(第三方应用或是在setting里-应用-强制停止时,APP进程就直接被干掉了,onDestroy方法都进不来,所以无法保证会执行)
5、监听系统广播判断Service状态。
通过系统的一些广播,比如:手机重启、界面唤醒、应用状态改变等等监听并捕获到,然后判断我们的Service是否还存活。
6、Application加上Persistent(持久的)属性。
2.1 Service 的 onStartCommand 方法有几种返回值?各代表什么意思?
有4种返回值,不同值代表的意思如下:
START_STICKY:如果 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的intent对象随后系统会尝试重新创建 service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到 service,那么参数 Intent 将为 null。
START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。
START_REDELIVER_INTENT:重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。
START_STICKY_COMPATIBILITY: START_STICKY 的兼容版本,但不保证服务被 kill 后一定能重启。
2.2 Service 的 onRebind(Intent)重新绑定方法在什么情况下会执行?
如果在 onUnbind()方法返回 true 的情况下会执行,否则不执行。
2.3 Activity 调用 Service 中的方法都有哪些方式?
Binder:
通过 Binder 接口的形式实现,当 Activity 绑定 Service 成功的时候 Activity 会在 ServiceConnection 的类 的 onServiceConnected()回调方法中获取到 Service 的 onBind()方法 return 过来的 Binder 的子类,然后通过对象调用方法。
Aidl:
aidl 比较适合当客户端和服务端不在同一个应用下的场景。
Messegener:
它引用了一个Handler对象,以便others能够向它发送消息(使用mMessenger.send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。一个Messeger不能同时双向发送,两个就就能双向发送了
Service里面可以弹Toast么?
面试经验:
可以弹Toast还能弹出对话框,把Service看成Activity就行,所有的界面都是展示到Window窗体上面的。
答案解析:
可以的。弹吐司有个条件就是得有一个Context上下文,而Service本身就是Context的子类,因此在Service里面弹Toast是完全可以的。比如我们在Service中完成下载任务后可以弹一个Toast通知用户。
你一般在什么情况下会使用Service?
经验总结:
Service其实就是背地搞事情,又不想让别人知道,你想知道一件事情不需要直接去问,你可以通过侧面了解。这就是Service设计的初衷
Service为什么被设计出来?
根据Service的定义,我们可以知道需要长期在后台进行的工作我们需要将其放在Service中去做。说得再通熟易懂一点,就是不能放在Activity中来执行的工作就必须得放到Service中去做。如:音乐播放、下载、上传大文件、定时关闭应用等功能。这些功能如果放到Activity中做的话,那Activity退出被销毁了的话,那这些功能也就停止了,这显然是不符合我们的设计要求的,所以要将他们放在Service中去执行。
Activity怎么和service绑定,怎么在activity中启动自己对应的service?
答案解析:
Activity通过bindService(Intent service, ServiceConnection conn, int flags)跟Service进行绑定。
绑定成功以后,Service会将代理对象通过回调的形式传递给ServiceConnection,这样我们就获取Service提供的代理对象
在Activity中可以通过startService和bindService方法启动Service。
注意: 如果想获取Service中提供的代理对象,那么必须通过bindService方法,进行绑定服务。
使用场景比如:音乐播放器,第三方支付等。
如果仅仅只是为了开启一个后台任务,那么可以使用startService方法。