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

[Android四大组件] Service详解,你不知道的都在这里

2017-03-21 12:47 603 查看

Service 服务

什么是服务

官方的解释: Service 就是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件.

通俗易懂: 也就是厨房里默默工作的厨师们 .

服务的生命周期:



服务基本用法

首先新建一个DemoService继承Service, 并重写父类的onCreate()、onStartCommand()和 onDestroy()方法 , 代码如下 :

public class DemoService extends Service{

public static final String TAG = "DemoService";

// 首次创建服务, 此方法执行一次性设置程序, 若服务已在运行, 则不会调用此方法
@Override
public void onCreate(){
super.onCreate();
Log.d(TAG,"service onCreate()");
}

// 当其他组件调用startService()启动服务时, 此方法会被调用, 需要调用stopSelf()或stopService()来停止服务
@Override
public int onStartCommand(Intent intent, int flags,int startId){
Log.d(TAG,"service onStartCommand()");
return super.onStartCommand(intent,flags,startId);
}

// 服务不再使用就会被销毁
@Override
public void onDestroy(){
Log.d(TAG,"service onDestroy()");
super.onDestroy();
}

// 当其他组件调用bindService()来绑定服务, 此方法被调用, 所以此方法必须返回IBinder提供一个接口,返回null则不允许被绑定.
@Override
public IBinder onBind(Intent intent){
return null;

4000
}

}


然后我们到AndroidManifest.xml去注册这个服务, 代码如下 :

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

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >

……

<service android:name="com.example.servicedemo.DemoService" >
</service>
</application>
</manifest>


==进阶==, 为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。

此外,还可以通过添加 android:exported 属性并将其设置为 “false”,确保服务仅适用于您的应用。这可以有效阻止其他应用启动您的服务,即便在使用显式 Intent 时也如此。

然后我们再到Activity中加入两个Button, 一个是启动服务, 一个是停止服务,代码如下:

public class MainActivity extends Activity implements View.OnClickListener {

private Button mStartService;
private Button mStopService;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mStartService = (Button) findViewById(R.id.service_start);
mStopService = (Button) findViewById(R.id.service_stop);
mStartService.setOnClickListener(this);
mStopService.setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
Intent startIntent = new Intent(this, DemoService.class);
startService(startIntent);
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this, DemoService.class);
stopService(stopIntent);
break;
default:
break;
}
}
}


当我们运行这个app的时候, 点击开启服务的时候, logcat输出了”service onCreate()” 和 “service onStartCommand()”.

当我们再次点击开启服务的时候, 只输出” service onStartCommand()”, 原因就在上面DemoService的注释里, 你们去找吧, 哈哈;

但是好想我们的Service无脑地在后台运行是不是有点浪费内存啊,接下来讲讲如何使用Service吧。

Service 的通信

扩展Binder类

虽然我们可以在onCreate() 和 onStartCommand()方法去执行一些操作 ,但是这样子好像服务就成了 留守儿童了, 没人跟它聊天,告诉它要做什么了.

不知道大家发现了DemoService上有一个onBind方法我们好像没有用到, 这就是联系的关键方法.

首先, 我们先创建一个类继承Binder, 然后添加一个操作的方法 , 如下:

class DemoBinder extends Binder{

public void compute(){
Log.d(TAG,"Binder compute() ");
}
}


然后在DemoService 类中的onBind方法中返回DemoBinder的实例.

接下来就是在Activity中添加两个按钮 , 一个是绑定服务 ,一个是取消绑定, 再使用bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

以下是Activity中增加的一些重要代码:

private DemoBinder mBinder;

private ServiceConnection mConnection = new ServiceConnection() {

@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
mBinder=(DemoBinder)service;
mBinder.compute();
}

@Override
public void onServiceDisconnected(ComponentName arg0) {

}
};

//onClick方法中
//绑定服务
Intent intent =new Intent(this,DemoService.class);
bindService(intent , mConnection ,Context.BIND_AUTO_CREATE);

//解除绑定
unbindService(mConnection);


可以看出我们创建了一个ServiceConnection的匿名类, 并且重写了两个方法, 这两个方法会子啊service被绑定和解绑的时候调用, 在onServiceConnected()这个方法中,我们获得DemoBinder的实例,就可以调用它里面的方法, 所以就可以建立起了关联.

在bindService()这个方法中,有三个参数, 第一个是Intent对象,第二个是前面创建的ServiceConnection对象的实例, 第三个是标志位, 这里的参数意思就是Service绑定后, 自动创建Service, 即Service中的onCreate方法被调用,但onStartCommand方法不会被调用.

使用Messenger

官方解释: 如需让服务与远程进程通信,则可使用 Messenger 为您的服务提供接口。利用此方法,您无需使用 AIDL 便可执行进程间通信 (IPC)。

用法跟扩展Binder类有小小区别: 并不是直接调用服务的方法, 而是通过Handler来做出判断,官方源代码如下:

public class MessengerService extends Service {
/** Command to the service to display a message */
static final int MSG_SAY_HELLO = 1;

/**
* Handler of incoming messages from clients.
*/
class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_SAY_HELLO:
Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}

/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());

/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}


然后在Activity中代码如下:

public class ActivityMessenger extends Activity {
Messenger mService = null;

private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
}

public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};

public void sayHello(View v) {
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MessengerService.class), mConnection,
Context.BIND_AUTO_CREATE);
}

@Override
protected void onStop() {
super.onStop();
unbindService(mConnection);
}
}


上面的sayHello()方法就是在xml布局文件中为button设置的onclick方法了..

AIDL

还有一种方法就是使用AIDL这种语言来实现, 由于篇幅问题, 我会再开一篇来写这个AIDL

注:大多数应用“都不会”使用 AIDL 来创建绑定服务,因为它可能要求具备多线程处理能力,并可能导致实现的复杂性增加。因此,AIDL 并不适合大多数应用,本文也不会阐述如何将其用于您的服务。如果您确定自己需要直接使用 AIDL,请参阅 AIDL 文档。

注意

只有 Activity、服务和内容提供程序可以绑定到服务,您无法从广播接收器绑定到服务。

Service和Thread

官方解释: 简单来说, 服务就是用户不与其交互它也可以在后台运行的组件。

通俗易懂:吃饭的时候 ,一般都是通过服务员点菜,服务员再告诉厨师。

但是,Service是运行在主线程的。所以我们一般写Service都是写成下面这个样子:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 开始执行后台任务
}
}).start();
return super.onStartCommand(intent, flags, startId);
}

class MyBinder extends Binder {

public void startDownload() {
new Thread(new Runnable() {
@Override
public void run() {
// 执行具体的下载任务
}
}).start();
}
}


官方 IntentService

由于大多数启动服务都不必同时处理多个请求(这种多线程情况也很危险), 因此使用IntentService类实现服务也许比较好.

以下IntentService的实例:

public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}

@Override
protected void onHandleIntent(Intent intent) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
}


以下是IntentService的操作流程:

创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。

创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。

在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。

提供 onBind() 的默认实现(返回 null)。

提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。

注意, onStartCommand()方法依旧返回默认实现, 即return super.onStartCommand(…);

创建前台服务

前台服务就是我们所说的通知或者桌面小部件这类的, 这里就简单演示下通知吧.


NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("my notification")
.setContentText("hello world");
Intent intent = new Intent(this, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager= (NotificationManager) etSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(1,mBuilder.build());


NotificaitonManager.notify(), 这个方法传入两个参数, 唯一标识通知的整数型, 和状态栏的Notification.

若要移除服务, 可调用notificaiton.cancel()方法来移除通知..

除非发生以下情况之一,否则通知仍然可见:

用户单独或通过使用“全部清除”清除了该通知(如果通知可以清除)。

用户点击通知,且您在创建通知时调用了 setAutoCancel()。

您针对特定的通知 ID 调用了 cancel()。此方法还会删除当前通知。

您调用了 cancelAll() 方法,该方法将删除之前发出的所有通知
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: