您的位置:首页 > 移动开发 > Android开发

Android基础之详解Service

2018-01-31 23:58 591 查看

1 介绍

  Service是android的一种机制。Service是运行在后台的代码,不能与用户交互,可以运行在自己的进程,也可以运行在其他应用程序的上下文里。需要通过某一个Activity或其他Context对象来调用。

1.1 Service的生命周期



  被启动的服务的生命周期:如果一个Service被某个Activity调用 Context.startService方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService方法多次启动,那么onCreate方法只会调用一次,onStartCommand将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此只需要一次调用stopService)。该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。

  被绑定的服务的生命周期:如果一个Service被某个Activity调用 Context.bindService方法绑定启动,不管调用bindService调用几次,onCreate方法都只会调用一次,onBind方法也只会调用一次,同时onStartCommand方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService断开连接或者之前调用bindService的Context不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。

  被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStartCommand便会调用多少次。调用unbindService将不会停止Service,需要unbindService与stopService同时调用或Service的stopSelf来停止服务。

  当服务被停止时清除服务:当一个Service被终止(①调用stopService;②调用stopSelf;③不再有绑定的连接,没有被启动)时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。

1.2 注册Service

  无论使用哪种启动方法,都需要在xml里注册你的Service:

<service
android:name=".packnameName.youServiceName"
android:enabled="true" />


1.3 什么时候使用Service

播放多媒体的时候,用户启动了其他的Activity这个时候程序要在后台继续播放。

后台service下载插件、更新包。

检测SD卡上文件的变化。

在后台记录你地理位置的改变等等。

1.4 Service分为本地服务(LocalService)和远程服务(RemoteService)

  默认情况下是在同一个主线程中。但可以通过配,android:process=”:remote” 属性让 Service 运行在不同的进程。

  本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。

  远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。

2 Service的具体问题

2.1 Service可以执行耗时操作吗

  不能,超过20s就会出现ARN。

2.2 Service的启动方法,互相的区别

(1)startService启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService;当用访问者启动之后,如果访问者不主动关闭,Service就不会关闭。

(2)bindService启动的服务:该方法启动的服务可以进行通信,停止服务使用unbindService。应用程序可以通过ServiceConnection进行数据交互。在实现Service时重写的onBind方法中,其返回的对象会传给ServiceConnection对象的onServiceConnected(ComponentName name, IBinder service)中的service参数;也就是说获取了serivce这个参数就得到了Serivce组件返回的值。

//其中只要与Service连接成功conn就会调用其onServiceConnected方法。
Context.bindService(Intent intent,ServiceConnection conn,int flag)


(3)startService同时也bindService 启动的服务:停止服务应同时使用stepService与unbindService。

2.3 Service与Activity怎么实现通信(进程内通信)

(1)通过Binder对象。同进程下Activity与Service双向通信,Activity调用bindService (Intent service, ServiceConnection conn, int flags)方法。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<application
<service android:name=".MyService" />
</application>
</manifest>


/**
* 第1步,新建一个继承自Service的类MyService;然后在AndroidManifest.xml里注册这个Service
*/
public class MyService extends Service {
private static final String TAG = "xiaoguan";
//第4.1步,实例化一个MyBinder对象
private MyBinder mBinder = new MyBinder();
private OnTestListener mListener;

@Override
public void onCreate() {
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
//第4.2步,在onBind回调方法里面返回这个mBinder对象
return mBinder;
}

//第3步,新建一个继承自Binder的类MyBinder
public class MyBinder extends Binder {
//Activity通过Binder来调用Service的方法将消息传给Service
public void testMethod(String msg) {
Log.d(TAG, "receive message from activity: "+msg);
//并回调mListener.onTest告诉Activity已收到消息
mListener.onTest("hi, activity");
}
//MyBinder 里面提供一个注册回调的方法
public void setOnTestListener(OnTestListener listener) {
mListener = listener;
}
}

//自定义一个回调接口
public interface OnTestListener {
void onTest(String str);
}
}


public class MainActivity extends Activity {
private static final String TAG = "xiaoguan";
private MyService.MyBinder mBinder;

private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//第5步,所说的在Activity里面取得Service里的binder对象
mBinder = (MyService.MyBinder) service;
//第6步,注册自定义回调
mBinder.setOnTestListener(new MyService.OnTestListener() {
@Override
public void onTest(String str) {
Log.d(TAG, "receive message from service: "+str);
}
});
}

@Override
public void onServiceDisconnected(ComponentName name) {
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, MyService.class);
//第2步,Activity里面使用bindService方式启动MyService,也就是绑定了MyService
bindService(intent,mConnection,BIND_AUTO_CREATE);

findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//点击按钮调用mBinder里面的方法,发送消息给Service
mBinder.testMethod("hi, service");
}
});
}
}


//结果打印
receive message from activity: hi, service
receive message from service: hi, activity


(2)通过广播。Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好。

2.4 Service里面可以弹出dialog或Toast

(1)Toast必须在UI主线程上才能正常显示,而在Service中是无法获得Acivity的Context的,在service中想显示出Toast只需将show的消息发送给主线程Looper就可以了。此时便可显示Toast。

Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
Toast.makeText(getApplicationContext(), "存Service is runing!",
Toast.LENGTH_SHORT).show();
}
});


(2)Dialog的用法与此类似,但是要多加个权限,在manifest中添加此权限以弹出dialog。

<uses-permission Android:name="android.permission.SYSTEM_ALERT_WINDOW" />

dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));


Builder builder = new AlertDialog.Builder(this);
builder.setMessage("是否重启服务");
builder.setNegativeButton("取消", new OnClickListense() {
@Override
public void onClick(DialogInterface dialog, int which) {
// to do
}
});
builder.setPositiveButton("确定", new OnClickListense() {
@Override
public void onClick(DialogInterface dialog, int which) {
// to do
}
});
final AlertDialog dialog = buidler.create();
//在dialog show前添加此代码,表示该dialog属于系统dialog。
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
new Thread() {
public void run() {
SystemClock.sleep(2000);
hanlder.post(new Runnable() {
@Override
public void run() {
dialog.show();
}
});
};
}.start();


参考来源于:developerzjy/ServiceDemo

2.5 为什么开启Service去创建子线程而不是Activity中

  (1)若直接在Activity中新开一条线程来做耗时操作,当该Activity退出到桌面或其他情况时将成为一个后台进程。

  (2)若在Service中新启动线程,则此时Android会依据进程中当前活跃组件重要程度,将其判断为服务进程,优先级比(1)高。

  因为服务进程的优先级比后台进程的优先级高,所以对于一个需要启动一个长时间操作的activity来说,开启service去创建子线程比Activity中创建子线程更好,尤其是对于操作将很可能超出activity的持续时间时。

  比如要上传一个图片文件,应该开启一个service来进行上传工作,这样在用户离开activity时工作仍在进行。使用service将会保证操作至少有服务进程的优先级。

3 如何保证Service在后台不被杀死,进程保活

在我的另一篇博客详细解析:Android进阶之进程优先级及提高优先级的方法(进程保活)

4 IntentService

在我的另一篇博客详细解析:Android性能优化之IntentService

5 参考链接

Android面试之Android 篇
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: