【Android】之【Service】

一、代码示例

如下代码监听开机广播,启动一个Service

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.demo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>


        <receiver android:name="com.android.demo.broadcast.BootBroadcastReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <service
            android:name="com.android.demo.service.AppService"
            android:process=":remote" >
        </service>
    </application>

</manifest>

AppService

package com.android.demo.service;

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

import com.android.demo.util.ShellUtils;

import java.util.ArrayList;
import java.util.List;

public class AppService extends Service {

    private final static String TAG = "AppService";

    private boolean isServiceStarted = false;

    private Intent startIntent = null;

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "AppService onCreate");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "AppService onStartCommand");

        //onStart(intent, startId);
        //return super.onStartCommand(intent, flags, startId);

        //START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。
        //随后系统会尝试重新创建service,由于服务状态为开始状态,
        //所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。
        //如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null

        return START_STICKY;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        // TODO Auto-generated method stub
        startIntent = intent;

        Log.e(TAG, "AppService, onStart");

        if (!isServiceStarted) {
            isServiceStarted = true;
        }
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        Log.e(TAG, "AppService, onDestroy");

        isServiceStarted = false;

        //在销毁的时候,重新开启
        if (startIntent != null) {
            startService(startIntent);
        }
    }
}

BootBroadcastReceiver

package com.android.demo.broadcast;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import com.android.demo.service.AppService;

public class BootBroadcastReceiver extends BroadcastReceiver {

	private static final String TAG = "BootBroadcastReceiver";
	
	private static Context mContext;
	
	@Override
	public void onReceive(Context context, Intent intent) {

		mContext = context;

		String action = intent.getAction();

		Log.e(TAG, "BootBroadcastReceiver action  = " + action);

		if (action == null) {
			return;
		}
		
		if (action.equals("android.intent.action.BOOT_COMPLETED")) {
			 Log.i(TAG, "BOOT_COMPLETED");
			 
	            Intent i = new Intent(context, AppService.class);
	            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	            context.startService(i);
		}
	}
	
}

二、 Service简介

Service是Android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后台运行,并且可以和其他组件进行交互。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的。
Service的启动有两种方式:context.startService() 和 context.bindService()

三、 Service启动流程

context.startService() 启动流程:
context.startService() -> onCreate() -> onStart() -> Service running -> context.stopService() -> onDestroy() -> Service stop

onstart()方法是在android2.0一下的版本中使用。而在android2.0以上则使用onstartCommand()方法。

如果Service还没有运行,则android先调用onCreate(),然后调用onStart();
如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。
如果stopService的时候会直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行,该Service的调用者在启动起来后可以通过stopService关闭Service。
所以调用startService的生命周期为:onCreate --> onStart (可多次调用) --> onDestroy

context.bindService()启动流程:
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop

onBind()将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service的实例、运行状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。
所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可多次绑定) --> onUnbind --> onDestory。
在Service每一次的开启关闭过程中,只有onStart可被多次调用(通过多次startService调用),其他onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。

四、 Service生命周期

Service的生命周期并不像Activity那么复杂,它只继承了onCreate()、onStart()、onDestroy()三个方法
当我们第一次启动Service时,先后调用了onCreate()、onStart()这两个方法;当停止Service时,则执行onDestroy()方法。
这里需要注意的是,如果Service已经启动了,当我们再次启动Service时,不会在执行onCreate()方法,而是直接执行onStart()方法。
在这里插入图片描述
它可以通过Service.stopSelf()方法或者Service.stopSelfResult()方法来停止自己,只要调用一次stopService()方法便可以停止服务,无论调用了多少次的启动服务方法。

五、 关于怎样让服务不被杀死

① 添加android:persistent=“true”

添加android:persistent=“true"到AndroidManifest.xml,
Google文档描述如下:Whether or not the application should remain running at all times-true” if it should, and "false"if not. The default value is “false”. Applications should not normally set this flag; persistence mode is intended only for certain system applications.可见这个属性不能随便用,到目前为止,我所发现使用该属性的应用只有Phone,而且使用是要求权限的,所以这个属性对第三方应用来说意义不是很大;常驻内存属性对第三方app无效

② 重写service的onStartCommand方法

设置onStartCommand()的返回值 。

@Override   public int onStartCommand(Intent intent, int flags, int startId) {   
     return START_STICKY;   
}  

简单介绍下这个方法,在Android开发的过程中,每次调用startService(Intent)的时候,都会调用该Service对象的onStartCommand(Intent,int,int)方法,然后在onStartCommand方法中做一些处理。然后我们注意到这个函数有一个int的返回值,这篇文章就是简单地讲讲int返回值的作用。
从Android官方文档中,我们知道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后一定能重启。

③ 在onDestory方法中重启Service服务

一般来说,这样做是可以的。但是如果这样----》设置–>下载–>强制停止。则不会执行ondestory方法,或者通过别人应用,如360直接kill掉我的应用时,也是不会调用Service的ondestory方法的。onDestroy方法里重启serviceservice +broadcast 方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service;

<receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
  <intent-filter>        
  <action android:name="android.intent.action.BOOT_COMPLETED" />  
  <action android:name="android.intent.action.USER_PRESENT" />   
  <action android:name="com.dbjtech.waiqin.destroy" />//这个就是自定义的action    
  </intent-filter> 
</receiver>

在onDestroy时:

@Override  
public void onDestroy() {  
  stopForeground(true);  
  Intent intent = new Intent("com.dbjtech.waiqin.destroy");  
  sendBroadcast(intent);  
  super.onDestroy();  
}

在BootReceiver里

public class BootReceiver extends BroadcastReceiver {  

  @Override  
  public void onReceive(Context context, Intent intent) {  
      if (intent.getAction().equals("com.dbjtech.waiqin.destroy")) {  
          //在这里写重新启动service的相关操作  
          startUploadService(context);  
      }  
  }  
}

也可以直接在onDestroy()里startService

@Override  
public void onDestroy() {  

   Intent sevice = new Intent(this, MainService.class);  
   this.startService(sevice);  
   super.onDestroy();  
}

【结论】当在setting里-应用-强制停止时,APP进程可能就直接被干掉了,onDestroy方法都进不来,所以还是无法保证.

④ 修改AndroidManifest.xml

<manifest  xmlns:android="http://schemas.android.com/apk/res/android"  android:sharedUserId="android.uid.system">      
<application android:icon="@drawable/icon"
          android:label="@string/app_name" android:allowClearUserData="false"
          android:process="system" 
          android:killAfterRestore="false">

如果在加入了此部分代码,表示该程序运行在system进程组中,system进程组是没有权限访问sd卡的,而且service是不会自动重启的。

⑤ 提高service的优先级别

不管你service的优先级别有多高,用户都是可以手动杀死的.
提升service优先级
在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。

<service  
  android:name="com.dbjtech.acbxt.waiqin.UploadService"  
  android:enabled="true" >  
  <intent-filter android:priority="1000" >  
      <action android:name="com.dbjtech.myservice" />  
  </intent-filter>  
</service>

【结论】目前看来,priority这个属性貌似只适用于broadcast,对于Service来说可能无效

⑥ 提升service进程优先级

Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:
1.前台进程( FOREGROUND_APP)
2.可视进程(VISIBLE_APP )
3.次要服务进程(SECONDARY_SERVER )
4.后台进程 (HIDDEN_APP)
5.内容供应节点(CONTENT_PROVIDER)
6.空进程(EMPTY_APP)
当service运行在低内存的环境时,将会kill掉一些存在的进程。因此进程的优先级将会很重要,可以使用startForeground 将service放到前台状态。这样在低内存时被kill的几率会低一些。
在onStartCommand方法内添加如下代码:

Notification notification = new Notification(R.drawable.ic_launcher,  
getString(R.string.app_name), System.currentTimeMillis());  
PendingIntent pendingintent = PendingIntent.getActivity(this, 0,  
new Intent(this, AppMain.class), 0);  
notification.setLatestEventInfo(this, "uploadservice", "请保持程序在后台运行",  
pendingintent);  
startForeground(0x111, notification);

注意在onDestroy里还需要stopForeground(true),运行时在下拉列表会看到自己的APP

⑦ 可以监听Intent.ACTION_TIME_TIC系统时钟广播

启动、网络切换、耳机插入、锁屏,亮屏等系统广播,系统每隔一段时间发送这个广播,当service被杀死的时候,隔一段时间通过广播启动,动态注册android.intent.action.TIME_TICK监听
判断service是否启动

public boolean isServiceRunning(String serviceName) 
{ 
    ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);   
    for (RunningServiceInfo service :manager.getRunningServices(Integer.MAX_VALUE))   
    {   
        if(serviceName.equals(service.service.getClassName())) 
        {   
            return true;   
        }   
    } 
    return false; 
} 

接受到广播后判断是否启动该service 若没有启动就启动它

if(intent.getAction().equals(Intent.ACTION_TIME_TICK)) 
{         
 if (!isServiceRunning(name)){   
    Intent mIntent = new Intent(context, MyService.class);   
    context.startService(mIntent);   
 }   
} 

⑧ 增加系统白名单,防止重要应用低内存时被误杀

联系厂商,加入白名单,有童鞋验证把demo工程的包名改成手机QQ的,编译运行在华为的机子上,发现进程杀不死,退到后台oom_adj的值同样不发生变化,而恢复原来的包名就不行了,可见很多大型软件,尤其聊天类的都会和厂商合作,将相关应用加入白名单。

参考

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Crazy程序猿2020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值