Service分为本地服务(LocalService)和远程服务(RemoteService):
1、本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程,因此不需要IPC,也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。
2、远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。
按使用方式可以分为以下三种:
1、startService 启动的服务:主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService;
2、bindService 启动的服务:该方法启动的服务可以进行通信。停止服务使用unbindService;
3、startService 同时也 bindService 启动的服务:停止服务应同时使用stopService与unbindService
Service 与 Thread 的区别
很多时候,你可能会问,为什么要用 Service,而不用 Thread呢,因为用 Thread 是很方便的,比起 Service 也方便多了,下面我详细的来解释一下。
1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!
既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。
Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进
行控制。
举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。
因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册
BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。
Service的生命周期
onCreate onStart onDestroy onBind
1). 被启动的服务的生命周期:如果一个Service被某个Activity 调用 Context.startService 方法启动,那么不管是否有Activity使用bindService绑定或unbindService解除绑定到该Service,该Service都在后台运行。如果一个Service被startService 方法多次启动,那么onCreate方法只会调用一次,onStart将会被调用多次(对应调用startService的次数),并且系统只会创建Service的一个实例(因此你应该知道只需要一次stopService调用)。
该Service将会一直在后台运行,而不管对应程序的Activity是否在运行,直到被调用stopService,或自身的stopSelf方法。当然如果系统资源不足,android系统也可能结束服务。
2). 被绑定的服务的生命周期:如果一个Service被某个Activity 调用 Context.bindService 方法绑定启动,不管调用 bindService 调用几次,onCreate方法都只会调用一次,同时onStart方法始终不会被调用。当连接建立之后,Service将会一直运行,除非调用Context.unbindService 断开连接或者之前调用bindService 的 Context 不存在了(如Activity被finish的时候),系统将会自动停止Service,对应onDestroy将被调用。
3). 被启动又被绑定的服务的生命周期:如果一个Service又被启动又被绑定,则该Service将会一直在后台运行。并且不管如何调用,onCreate始终只会调用一次,对应startService调用多少次,Service的onStart便会调用多少次。调用unbindService将不会停止Service,而必须调用 stopService 或 Service的 stopSelf 来停止服务。
4). 当服务被停止时清除服务:当一个Service被终止(1、调用stopService;2、调用stopSelf;3、不再有绑定的连接(没有被启动))时,onDestroy方法将会被调用,在这里你应当做一些清除工作,如停止在Service中创建并运行的线程。
特别注意:
1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);
2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;
3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;
4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。
5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。
一个本地服务的例子,注意了:通过startService和stopService启动关闭服务的,适用于服务和activity之间没有调用交互的情况。如果之间需要传递参数或者方法调用,需要使用bind和unbind方法。代码如下所示:
public class MainActivity extends AppCompatActivity {
TextView text;
Button button1;
private Intent intent;
private ServiceConnection r;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1= (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService(intent, r, Context.BIND_AUTO_CREATE);
}
});
text= (TextView) findViewById(R.id.textView3);
intent = new Intent(this, fuwu.class);
r = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {//第二个参数iBinder就是fuwu中onBind方法return的那个u实例,可以利用这个来传递数据
text.setText(((fuwu.u) iBinder).geta());
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(r);
}
}
public class fuwu extends Service {
public class u extends Binder {
public String geta() {
return "收到了";
}
}
@Override
public void onCreate() {
super.onCreate();
new Thread() {//新建了一个线程来执行计时功能
@Override
public void run() {
super.run();
int ia = 0;
for (int i = 0; i < 11; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ia++;
Log.i("xinxi", String.valueOf(ia));
}
}
}.start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("xinxi", "onBind");
return new u();
}
}
远程服务和本地服务的区别就是是否在androidmanifest.xml文件中的service中设置了android:process属性,因为startService启动的service不能传递信息给activity,所以可以直接给service添加process属性,让其运行在不同的进程,而bindService启动的service会传递信息给activity,所以如果给service添加process属性的话,需要使用到aidl,具体的代码如下所示:
public class MainActivity extends AppCompatActivity {
private android.widget.Button button;
private ServiceConnection serviceConnection;
private Button button1;
private LinearLayout activitymain;
private Button button2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button2 = (Button) findViewById(R.id.button2);
activitymain = (LinearLayout) findViewById(R.id.activity_main);
button1 = (Button) findViewById(R.id.button1);
button = (Button) findViewById(R.id.button);
serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
try {
button.setText((IAidlBinder.Stub.asInterface(iBinder)).getInfo());
Toast.makeText(MainActivity.this,(IAidlBinder.Stub.asInterface(iBinder)).getxuesheng().getDizhi()+(IAidlBinder.Stub.asInterface(iBinder)).getxuesheng().getXuehao()+(IAidlBinder.Stub.asInterface(iBinder)).getxuesheng().getName(),Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, AidlService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
});
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getfuwu();
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(MainActivity.this, bendi.class);
bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
button2.setText(((bendi.u)iBinder).geta());
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
},Context.BIND_AUTO_CREATE);
//startService(intent);//用startService启动的服务可以设置android:process属性,将其运行在不同的进程中,因为用startService启动的服务不与activity进行通信,但是如果是bindService启动的服务,不可以设置android:process,不然会报错,只有使用aidl就可以给用bindService启动的服务设置android:process属性了,因为进程之间通信要用到aidl
}
});
}
void getfuwu() {
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
/* for (ActivityManager.RunningServiceInfo serviceInfo : activityManager.getRunningServices(Integer.MAX_VALUE)) {
Log.i("xinxi", serviceInfo.process);//RunningServiceInfo是用来查看运行的服务的信息的,远程服务能在这里显示
}*/
for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : activityManager.getRunningAppProcesses()) {
Log.i("xinxi", runningAppProcessInfo.processName);//RunningAppProcessInfo是用来查看当前运行的app的进程的信息的,本地服务不创建新的进程,所以不能在这里显示,远程服务在这里能显示进程,就是通过android:process属性设置好的进程名字
}
}
}
public class AidlService extends Service {
private IAidlBinder.Stub serviceBinder = new IAidlBinder.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String getInfo() throws RemoteException {
return "I'm a Service";
}
@Override
public Student getxuesheng() throws RemoteException {
Student student=new Student(11,"laowang","shanxide");
return student;
}
};
@Override
public void onCreate() {
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return serviceBinder;
}
}
public class bendi extends Service {
public class u extends Binder {
public String geta() {
return "收到了";
}
}
@Override
public void onCreate() {
super.onCreate();
Log.i("xinxi", "onCreate");
}
/*
@Override
public int onStartCommand(Intent intent, int flags, int startId) {//注意:通过startServer启动的服务才会调用onStartCommand方法,通过bindService启动的服务调用的是onBind方法
Log.i("xinxi", "onStartCommand");
return START_STICKY;
}
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i("xinxi", "onBind");
return new u();
}
}
public class Student implements Parcelable {
private int xuehao;
private String name;
private String dizhi;
public Student(int xuehao, String name, String dizhi) {
this.xuehao = xuehao;
this.name = name;
this.dizhi = dizhi;
}
public int getXuehao() {
return xuehao;
}
public String getName() {
return name;
}
public String getDizhi() {
return dizhi;
}
public void setXuehao(int xuehao) {
this.xuehao = xuehao;
}
public void setName(String name) {
this.name = name;
}
public void setDizhi(String dizhi) {
this.dizhi = dizhi;
}
@Override
public String toString() {
return "Student{" +
"xuehao=" + xuehao +
", name='" + name + '\'' +
", dizhi='" + dizhi + '\'' +
'}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.xuehao);
dest.writeString(this.name);
dest.writeString(this.dizhi);
}
public Student() {
}
protected Student(Parcel in) {
this.xuehao = in.readInt();
this.name = in.readString();
this.dizhi = in.readString();
}
public static final Parcelable.Creator<Student> CREATOR = new Parcelable.Creator<Student>() {
@Override
public Student createFromParcel(Parcel source) {
return new Student(source);
}
@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
}
在IAidlBinder.aidl文件中,记得创建完成后,执行build>make project,这样才能在其中java文件中找到该接口
package com.example.liang.lianxi;
import com.example.liang.lianxi.Student;//注意了这里的bean文件必须得手动导入,不然系统会找不到
interface IAidlBinder {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String getInfo();
Student getxuesheng();
}
在Student.aidl文件中,注意再创建bean文件的aidl时,会出现interface name must be unique错误,这时只需要起个其他名字,等创建完成后再修改名字就行
package com.example.liang.lianxi;
parcelable Student;
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".AidlService"
android:process=":yuancheng"/>
<!--进程名为:com.example.liang.lianxi:yuancheng-->
<service android:name=".bendi"
/>
</application>
----------------------------------------------------------------------------
android开发艺术中对IntentService的介绍:
IntentService是一种特殊的Service,它继承了Service并且它是一个抽象类,抽象方法为onHandleIntent(),IntentService用于执行后台耗时的任务,当任务执行后它会自动停止,同时由于IntentService是服务的原因,这导致它的优先级比单纯的线程要高很多,所以IntentService比较适合执行一些高优先级的后台任务,不容易被系统杀死。在实现上,IntentService封装了HandlerThread和Handler。当onHandleIntent方法执行完最后一个任务后,会自动停止服务;当有多个后台任务同时存在时,这些后台任务会按照外界发起的顺序排队执行。
IntentService的用法,代码如下所示:
public class IntentFuWu extends IntentService {//记得在androidmanifest.xml中进行服务注册
public IntentFuWu() {
super("IntentFuWu");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.d("IntentFuWu", intent.getStringExtra("zhi"));
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("IntentFuWu", "服务destroy");
}
}
Intent intent = new Intent(this, IntentFuWu.class);
intent.putExtra("zhi", "我是任务1");
startService(intent);
intent.putExtra("zhi", "我是任务2");
startService(intent);
intent.putExtra("zhi", "我是任务3");
startService(intent);
/*输出结果为:
05-24 23:39:35.934 13730-13773/? D/IntentFuWu: 我是任务1
05-24 23:39:35.934 13730-13773/? D/IntentFuWu: 我是任务2
05-24 23:39:35.934 13730-13773/? D/IntentFuWu: 我是任务3
05-24 23:39:36.414 13730-13730/com.example.liang.arlvyou D/IntentFuWu: 服务destroy*/