android如何install服务,Android-Service解析

特此声明:文中有许多描述来自于网上,个人总结方便翻阅复习

1、概述

Service作为Android四大组件之一,扮演角色也非常重要,也是作为开发人员必须要掌握的一个知识点。

主要用于在后台处理一些耗时操作,或者执行某些长期运行的任务,在程序退出的时候,能够保证程序继续保持运行状态。

2、启动Service

启动service一般有两种启动方式,通过StartService启动Service和bindService启动Service

通过startService启动后,service会一直运行,除非调用了stopService()或stopSelf()方法时,才会停止并销毁。

使用场景:上传、下载

bindService启动服务的生命周期与其绑定的client(组件,如Activity)息息相关,当client销毁时,client会自动与Service解除绑定。也可以调用Context的unbindService()方法与service解除绑定。

通过bindService启动,调用者直接是典型的client-service模式。service只有一个,但绑定到service上面的client可以有一个或很多个。

**注意 **:使用bindService,Service的确是在后台运行,但是这个后台依旧是在主线程中,所以也会发生堵塞主线程的情况,所以说在我们使用Service的同时,也需要将一些耗时操作(网络通信等等)另开线程。

2.1、使用start方式启动Service

一般使用继承Service类和继承IntentService类两种方式启动。IntentService继承于Service,若Service不需要同时处理多个请求,那么使用IntentService将是最好选择。

分三步走:

1、新建一个TestService继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法,如下所示:

/**

* @Description: 继承Service一般常见重新这四个方法

* @Author: zyzhang

* @Date: 18/1/19 上午9:44

*/

public class TestService extends Service {

/**

* 调用startService()后会执行onCreate()回调.

* onCreate()只会在第一次创建service时候调用,多次执行startService()不会重复调用onCreate().

* 适合完成一些初始化工作。

*/

@Override

public void onCreate() {

super.onCreate();

}

/**

* 多次执行了Context的startService()方法,都会回调这个方法

* 如:会在此处创建一个线程用于下载数据或播放音乐等。

* @param intent

* @param flags

* @param startId

* @return

*/

@Override

public int onStartCommand(Intent intent, int flags, int startId) {

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

}

/**

* Service中的onBind()方法是抽象方法,Service类本身就是抽象类,所以onBind()方法是必须重写的,即使我们用不到。

*

* @param intent

* @return

*/

@Nullable

@Override

public IBinder onBind(Intent intent) {

return null;

}

/**

* 在销毁的时候会执行Service该方法。

*/

@Override

public void onDestroy() {

super.onDestroy();

}

}

2、AndroidManifest.xml中注册

提示:我们创建Service时与需要在Manifest中注册,同时可以在android:exported属性中设置false,以防止其他程序启动。

package="bibi.zyzhang.com.s_service">

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">

3、在主线程中启动如下:

// 启动

Intent startIntent = new Intent(this, TestService.class);

startService(startIntent);

// 结束

Intent stopIntent = new Intent(this, TestService.class);

stopService(stopIntent);

在启动service后,我们可以到程序管理界面中看到我们的TestService进程还在,直到我们点击stop才会停止。

在上面的例子中我们发现启动service后,service和activity的关系不大,如何让他们的关联起来呢,比如在Activity中可以指定让Service去执行什么任务,这时候onBind()就派上用场了,具体可以看看下面是如何实现的。

2.2、通过bindService启动Service

TestService.class

/**

* @Description: 继承Service一般常见重新这四个方法

* @Author: zyzhang

* @Date: 18/1/19 上午9:44

*/

public class TestService extends Service {

//client 可以通过Binder获取Service实例

public class MyBinder extends Binder {

public TestService getService() {

return TestService.this;

}

}

//通过binder实现调用者client与Service之间的通信

private MyBinder binder = new MyBinder();

@Override

public void onCreate() {

super.onCreate();

Log.d("zyzhang", "onCreate() executed");

}

@Nullable

@Override

public IBinder onBind(Intent intent) {

Log.d("zyzhang", "onBind() executed");

return binder;

}

@Override

public boolean onUnbind(Intent intent) {

Log.i("zyzhang", "TestService - onUnbind - from = " + intent.getStringExtra("from"));

return false;

}

/**

* 在销毁的时候会执行Service该方法。

*/

@Override

public void onDestroy() {

Log.d("zyzhang", "onDestroy() executed");

super.onDestroy();

}

}

MainActivity.class

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private Button startService;

private Button stopService;

private TestService service = null;

private boolean isBind = false;

private ServiceConnection conn = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

isBind = true;

TestService.MyBinder myBinder = (TestService.MyBinder) iBinder;

service = myBinder.getService();

Log.i("zyzhang", "onServiceConnected");

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

isBind = false;

Log.i("zyzhang", "onServiceDisconnected");

}

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

startService = (Button) findViewById(R.id.start_service);

stopService = (Button) findViewById(R.id.stop_service);

startService.setOnClickListener(this);

stopService.setOnClickListener(this);

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.start_service:

Intent intent = new Intent(this, TestService.class);

intent.putExtra("from", "MainActivity");

Log.i("zyzhang", "----------------------------------------------------------------------");

Log.i("zyzhang", "MainActivity 执行 bindService");

bindService(intent, conn, BIND_AUTO_CREATE);

break;

case R.id.stop_service:

if (isBind) {

Log.i("zyzhang",

"----------------------------------------------------------------------");

Log.i("zyzhang", "MainActivity 执行 unbindService");

unbindService(conn);

}

break;

default:

break;

}

}

@Override

public void onDestroy() {

super.onDestroy();

Log.i("zyzhang", "MainActivity - onDestroy");

}

}

3、Service和Thread的关系

两者自然是没有关系的,首先Service是运行在主线程里的,而Thread是开启一个子线程。

所以在Service里编写了非常耗时的代码,程序必定会出现ANR的。需要的话可以在Service中再创建一个子线程。

那为什么不直接在Activity中创建子线程呢?那是因为Activity很难对Thread进行控制,当Activity被销毁后,没有任何办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

4、生命周期

d5a657571a56

image.png

其中左图显示了使用 startService() 所创建的服务的生命周期,右图显示了使用 bindService() 所创建的服务的生命周期。

5、如何保证Service不被杀死?

onStartCommand方式中,返回START_STICKY

如果返回START_NOT_STICKY,表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service。举个例子,某个Service需要定时从服务器获取最新数据:通过一个定时器每隔指定的N分钟让定时器启动Service去获取服务端的最新数据。当执行到Service的onStartCommand时,在该方法内再规划一个N分钟后的定时器用于再次启动该Service并开辟一个新的线程去执行网络操作。假设Service在从服务器获取最新数据的过程中被Android系统强制杀掉,Service不会再重新创建,这也没关系,因为再过N分钟定时器就会再次启动该Service并重新获取数据。

如果返回START_STICKY,表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,但是onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY,比如一个用来播放背景音乐功能的Service就适合返回该值。

如果返回START_REDELIVER_INTENT,表示Service运行的进程被Android系统强制杀掉之后,与返回START_STICKY的情况类似,Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。只要返回START_REDELIVER_INTENT,那么onStartCommand重的intent一定不是null。如果我们的Service需要依赖具体的Intent才能运行(需要从Intent中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的Service就适合返回START_REDELIVER_INTENT。

2.提高Service的优先级

在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = "1000"这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时适用于广播。

3.提升Service进程的优先级

当系统进程空间紧张时,会依照优先级自动进行进程的回收。

Android将进程分为6个等级,按照优先级由高到低依次为:

前台进程foreground_app

可视进程visible_app

次要服务进程secondary_server

后台进程hiddena_app

内容供应节点content_provider

空进程empty_app

可以使用startForeground将service放到前台状态,这样低内存时,被杀死的概率会低一些。

4.在onDestroy方法里重启Service

当service走到onDestroy()时,发送一个自定义广播,当收到广播时,重新启动service。

5.系统广播监听Service状态

6.将APK安装到/system/app,变身为系统级应用

6、远程Service的用法

我们知道Service其实是运行在主线程里的,如果直接在Service中处理一些耗时的逻辑,就会导致程序ANR,如下:

public class MyService extends Service {

@Override

public void onCreate() {

super.onCreate();

Log.d(TAG, "onCreate() executed");

try {

Thread.sleep(60000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

// 不管是startService还是bindService程序就会阻塞住并无法进行任何其它操作,过一段时间后就会弹出ANR的提示框

那么远程Service的用法可以解决这个问题,将MyService转换成远程Service只需要在注册的时候将它的android:process属性指定成:remote就可以了如下:

android:name="xxx.MyService"

android:process=":remote" >

此时的MyService已经在另外一个进程当中运行了,所以不会影响改进程的主线程。

使用远程Service的弊端:

首先将MyService的onCreate()方法中让线程睡眠的代码去除掉,然后重新运行程序,并点击一下Bind Service按钮,你会发现程序崩溃了!为什么点击Start Service按钮程序就不会崩溃,而点击Bind Service按钮就会崩溃呢?这是由于在Bind Service按钮的点击事件里面我们会让MainActivity和MyService建立关联,但是目前MyService已经是一个远程Service了,Activity和Service运行在两个不同的进程当中,这时就不能再使用传统的建立关联的方式,程序也就崩溃了。

如何才能让Activity与一个远程Service建立关联呢?这就要使用AIDL来进行跨进程通信了(IPC)实现如下:

新建MyAIDLService.aidl文件:

package bibi.zyzhang.com.s_service;

interface MyAIDLService {

int plus(int a,int b);

String toUpperCase(String str);

}

TestService.java代码如下:

/**

* @Description: 继承Service一般常见重新这四个方法

* @Author: zyzhang

* @Date: 18/1/19 上午9:44

*/

public class TestService extends Service {

@Override

public void onCreate() {

super.onCreate();

Log.d("zyzhang", "onCreate() executed");

}

@Nullable

@Override

public IBinder onBind(Intent intent) {

Log.d("zyzhang", "onBind() executed");

return mBinder;

}

/*这里先是对MyAIDLService.Stub进行了实现,重写里了

*toUpperCase()和plus()这两个方法。**/

MyAIDLService.Stub mBinder = new MyAIDLService.Stub() {

@Override

public int plus(int a, int b) throws RemoteException {

return a + b;

}

@Override

public String toUpperCase(String str) throws RemoteException {

if (str != null) {

return str.toUpperCase();

}

return null;

}

};

@Override

public boolean onUnbind(Intent intent) {

Log.i("zyzhang", "TestService - onUnbind - from = " + intent.getStringExtra("from"));

return false;

}

/**

* 在销毁的时候会执行Service该方法。

*/

@Override

public void onDestroy() {

Log.d("zyzhang", "onDestroy() executed");

super.onDestroy();

}

}

AndroidManifase.xml代码如下:

android:name="bibi.zyzhang.com.s_service.TestService"

android:process=":remote">

MainActivity.java中点击onBind按钮的代码看最终的打印效果,关键代码如下:

private MyAIDLService myAIDLService;

private ServiceConnection conn = new ServiceConnection() {

@Override

public void onServiceConnected(ComponentName componentName, IBinder iBinder) {

isBind = true;

myAIDLService = MyAIDLService.Stub.asInterface(iBinder);

try {

int result = myAIDLService.plus(3, 5);

String upperStr = myAIDLService.toUpperCase("hello world");

Log.d("zyzhang", "result is " + result);

Log.d("zyzhang", "upperStr is " + upperStr);

} catch (RemoteException e) {

e.printStackTrace();

}

Log.i("zyzhang", "onServiceConnected");

}

@Override

public void onServiceDisconnected(ComponentName componentName) {

isBind = false;

Log.i("zyzhang", "onServiceDisconnected");

}

};

// D/zyzhang: result is 8

// D/zyzhang: upperStr is HELLO WORLD

上面代码中,想要让Activity与Service之间建立关联,需要调用bindService()方法,并将Intent作为参数传递进去,在Intent里指定好要绑定的Service,示例代码如下:

Intent bindIntent = new Intent(this, TestService.class);

bindService(bindIntent, conn, BIND_AUTO_CREATE);

这里在构建Intent的时候是使用MyService.class来指定要绑定哪一个Service的,但是在另一个应用程序中去绑定Service的时候并没有TestService这个类,这时就必须使用到隐式Intent了。现在修改AndroidManifest.xml中的代码,给TestService加上一个action,如下所示:

android:name="bibi.zyzhang.com.s_service.TestService"

android:process=":remote">

对应MainActivity.java中如下就可以了:

Intent intent = new Intent("bibi.zyzhang.com.s_service.MyAIDLService");

bindService(intent, connection, BIND_AUTO_CREATE);

到这改告一段落了,谢谢分享。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值