Service是android中四大组件之一,重要性仅次于Activity,Android文档对他的描述如下:
Service组件表示在不影响用户的情况下执行耗时操作或者提供其他应用使用的功能。
在android中所有的组件都运行在主线程,使用service的时候,还需要吧耗时的操作另开启一个线程。但是使用Service,更适合管理耗时的操作,因为service有着不同的生命周期。比如说应用中播放音乐,通常由Service控制。
启动Service
Service 可以通过两种方式启动:Context.startService()或者Context.bindService();
Context.startService()和启动activity的方法类似:
Intent i = new Intent(ServiceActivity.this, ServiceA.class);
startService(i);
使用这种方式启动Service后,Service分别会调用onCreate()和onStartCommand()方法。
停止Service的方法:
Intent i = new Intent(ServiceTestActivity.this, ServiceA.class);
stopService(i);
也可以调用Service的stopSelf()方法进行停止。
service销毁的时候会调用他的onDestory()方法,在这里进行释放资源的操作。
需要注意的是多次启动Service,onCreate()方法不会多次调用,只有在第一次启动的时候会调用(或者把Service销毁重新start),但是onStartCommand()方法会多次调用。
第二种方式:Context.bindService()
Intent i = new Intent(ServiceTestActivity.this, ServiceA.class);
bindService(i,connection,BIND_AUTO_CREATE);
解除绑定方法:
Intent i = new Intent(ServiceTestActivity.this, ServiceA.class);
unbindService(connection);
这种方式启动Service的时候,生命周期方法如下:
bindService->onCreate->onBind->unbindService->onUnbind->onDestroy
如果没有调用unbindService方法,当activity销毁的时候此Service也能销毁,但是会发生内存泄露,会报如下错误:
Activity example.ylh.com.service_demo.ServiceTestActivity has leaked ServiceConnection example.ylh.com.service_demo.ServiceTestActivity$1@513a0eb that was originally bound here
······
所以为了防止内存泄露,还是老老实实的unbind吧。
bindService启动的时候,需要传两个参数,第一个是intent,第二个是ServiceConnection,这个是什么东西呢?
我们知道Service启动之后,Service额就开始运行了,但是怎么和Activity交互呢,这个时候就需要ServiceConnection了,ServiceConnection是一个接口:
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected");
}
};
onServiceConnected方法的调用时机是在Service的onBind之后,表示service和activity绑定成功,建立关联。
onServiceDisconnected方法表示Service和activity关联失败,没有绑定,一般不会调用。
onServiceConnected方法有两个参数,第二个参数是一个IBinder,这个IBinder就是Service的onBind方法的返回值,所以我们可以通过onBind的返回值和onServiceConnected方法,让activity和Service关联起来,下边是一个典型案例:
activity的xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn1"
android:text="start"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn2"
android:text="stop"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/btn3"
android:text="action"/>
</LinearLayout>
</RelativeLayout>
activity代码:
package example.ylh.com.service_demo;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import example.ylh.com.R;
/**
* Created by yanglihai on 2017/8/17.
*/
public class ServiceTestActivity extends Activity {
public static final String TAG = ServiceTestActivity.class.getSimpleName();
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = ((ServiceA.MyBinder) service).getService();
Log.e(TAG,"onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected");
}
};
private ServiceA mService ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.service_test_activity);
findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(ServiceTestActivity.this, ServiceA.class);
bindService(i,connection,BIND_AUTO_CREATE);
}
});
findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(ServiceTestActivity.this, ServiceA.class);
unbindService(connection);
}
});
findViewById(R.id.btn3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mService.doSomething();
}
});
}
}
service的代码:
package example.ylh.com.service_demo;
import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
/**
* Created by yanglihai on 2017/8/17.
*/
public class ServiceA extends Service {
static final String TAG = ServiceA.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "oncreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
@Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
Log.e(TAG, "bindService");
return super.bindService(service, conn, flags);
}
@Override
public void unbindService(ServiceConnection conn) {
super.unbindService(conn);
Log.e(TAG, "unBindService");
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind" );
return mbinder;
}
public void doSomething(){
Log.e(TAG, "doSomething");
}
private MyBinder mbinder = new MyBinder();
public class MyBinder extends Binder{
public ServiceA getService(){
return ServiceA.this;
}
}
}
在onServiceConnected方法中,通过强转拿到binder,在通过getService()拿到Service的实例,就可以进行你想要的操作了。
前台Service
当Service启动之后,如果我们不去调用停止方法,Service会一直运行在后台,但是当系统内存不足的时候可能会杀死我们的Service,为了避免被系统杀死,可以把Service设置成前台服务,通过调用Service.startForeground()来实现。
先上代码:
package example.ylh.com.service_demo;
import android.app.Notification;
import android.app.Service;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Binder;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import example.ylh.com.R;
/**
* Created by yanglihai on 2017/8/17.
*/
public class ServiceA extends Service {
static final String TAG = ServiceA.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "oncreate");
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.btn_home_vedio);
builder.setContentTitle("视频通知");
builder.setContentText("someone send you a video");
Notification notification = builder.build();
startForeground(2,notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e(TAG, "onDestroy");
}
}
在onCreate中,创建了一个通知,用来告诉用户后台有操作在运行。startForeground()的第一个参数是一个int型的id,需要注意的是这个id不能为0,否则不能创建前台服务。
在这个Service运行期间,通知栏会有一个通知,直到Service停止或者调用StopForeground(true),通知就能消失。
补充一点,不论哪种启动方式,Service也需要在manifest文件中声明:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="example.ylh.com" >
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<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" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".service_demo.ServiceA" />
</application>
</manifest>
网友评论