第七章 后台服务(本地服务)

参考资料:
《Android应用程序开发》ISBN 9787302283164
参考软件:
Android Studio、Eclipse+ADT、Android SDK、JDK
本地服务
本地服务的调用者和服务都在同一个程序中,是不需要跨进程就可以实现服务的调用
步骤1:新建子类继承Service类
需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法
步骤2:构建用于启动Service的Intent对象
步骤3:调用startService()启动Service、调用stopService()停止服务
步骤4:在AndroidManifest.xml里注册Service
一、实现启动服务(1)界面实现

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello">
</TextView>
<Button android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动Service" >
</Button>
<Button android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止Service" >
</Button>
</LinearLayout>
(2)建立Service类
package edu.hrbeu.SimpleRandomServiceDemo;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;
public class RandomService extends Service{
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "(1) 调用onCreate()",
Toast.LENGTH_LONG).show();
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Toast.makeText(this, "(2) 调用onStart()",
Toast.LENGTH_SHORT).show();
double randomDouble = Math.random();
String msg = "随机数:"+ String.valueOf(randomDouble);
Toast.makeText(this,msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "(3) 调用onDestroy()",
Toast.LENGTH_SHORT).show();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
(3)注册Service

<service android:name=".RandomService"/>
(4)实现启动和停止
显示启动
1 final Intent serviceIntent = new Intent(this, RandomService.class);
2 startService(serviceIntent);
隐式启动
1 <service android:name=".RandomService">
2 <intent-filter>
3 <action android:name="edu.hrbeu.RandomService" />
4 </intent-filter>
5 </service>
package edu.hrbeu.SimpleRandomServiceDemo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class SimpleRandomServiceDemoActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button startButton = (Button)findViewById(R.id.start);
Button stopButton = (Button)findViewById(R.id.stop);
final Intent serviceIntent = new Intent(this, RandomService.class);
startButton.setOnClickListener(new Button.OnClickListener(){
public void onClick(View view){
startService(serviceIntent);
}
});
stopButton.setOnClickListener(new Button.OnClickListener(){
public void onClick(View view){
stopService(serviceIntent);
}
});
}
}
停止一个started服务有两种方法:
(1)在外部使用stopService()
(2)在服务内部(onStartCommand方法内部)使用stopSelf()方法。
(5)查看
我们还可以在正在“设置--应用---运行”中找到这个服务,如下图所示:

点开上图中的红框部分,可以看到:

Android获取手机状态和监听手机来电状态例子
获取手机状态:
import android.content.Context;
import android.telephony.TelephonyManager;
//获得相应的系统服务
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
/**
* 返回电话状态
*
* CALL_STATE_IDLE 无任何状态时
* CALL_STATE_OFFHOOK 接起电话时
* CALL_STATE_RINGING 电话进来时
*/
tm.getCallState();
if(tm.getCallState() == TelephonyManager.CALL_STATE_IDLE) {
Log.d("test", "call state idle...");
} else if(tm.getCallState() == TelephonyManager.CALL_STATE_OFFHOOK) {
Log.d("test", "call state offhook...");
} else if(tm.getCallState() == TelephonyManager.CALL_STATE_RINGING) {
Log.d("test", "call state ringing...");
}
监听手机来电状态:
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
//使用TelephonyManager对象的listen(PhoneStateListener listener, int events)
//实现PhoneStateListener listener并实现相应的方法
public class MyPhoneCallListener extends PhoneStateListener
{
@Override
public void onCallStateChanged(int state, String incomingNumber)
{
switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK: //电话通话的状态
Toast.makeText(Main.this, "正在通话...", Toast.LENGTH_SHORT).show();
break;
case TelephonyManager.CALL_STATE_RINGING: //电话响铃的状态
Toast.makeText(Main.this, incomingNumber, Toast.LENGTH_SHORT).show();
break;
}
super.onCallStateChanged(state, incomingNumber);
}
第一个参数需要实现PhoneStateListener listener并实现相应的方法,第二个参数是PhoneStateListener的静态常量,此处由于是监听电话状态,所以需要传入LISTEN_CALL_STATE,而同时也需要在AndroidManifest中注册相应的权限
<uses-permission Android:name="android.permission.READ_PHONE_STATE" />
二、实现绑定服务
以绑定方式使用Service,能够获取到Service实例,不仅能够正常启动Service,还能够调用Service中的公有方法和属性
应用程序组件(客户端)通过调用bindService()方法能够绑定服务,然后Android系统会调用服务的onBind()回调方法,则个方法会返回一个跟服务器端交互的Binder对象。
这个绑定是异步的,bindService()方法立即返回,并且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须创建一个ServiceConnection类的实例,并且把这个实例传递给bindService()方法。ServiceConnection对象包含了一个系统调用的传递IBinder对象的回调方法。
注意:只有Activity、Service、Content Provider能够绑定服务;BroadcastReceiver广播接收器不能绑定服务。
(1)界面实现

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello">
</TextView>
<Button android:id="@+id/bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="服务绑定" >
</Button>
<Button android:id="@+id/unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消绑定" >
</Button>
<Button android:id="@+id/compute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加法运算" >
</Button>
</LinearLayout>
(2)建立服务类
package edu.hrbeu.SimpleMathServiceDemo;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.widget.Toast;
public class MathService extends Service{
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder{
MathService getService() {
return MathService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(this, "本地绑定:MathService",
Toast.LENGTH_SHORT).show();
return mBinder;
}
@Override
public boolean onUnbind(Intent intent){
Toast.makeText(this, "取消本地绑定:MathService",
Toast.LENGTH_SHORT).show();
return false;
}
public long Add(long a, long b){
return a+b;
}
}
(3)注册

<service android:name=".MathService"/>
(4)实现启动和停止
package edu.hrbeu.SimpleMathServiceDemo;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class SimpleMathServiceDemoActivity extends Activity {
private MathService mathService;
private boolean isBound = false;
TextView labelView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
labelView = (TextView)findViewById(R.id.label);
Button bindButton = (Button)findViewById(R.id.bind);
Button unbindButton = (Button)findViewById(R.id.unbind);
Button computButton = (Button)findViewById(R.id.compute);
bindButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if(!isBound){
final Intent serviceIntent = new Intent(SimpleMathServiceDemoActivity.this,MathService.class);
bindService(serviceIntent,mConnection,Context.BIND_AUTO_CREATE);
isBound = true;
}
}
});
unbindButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if(isBound){
isBound = false;
unbindService(mConnection);
mathService = null;
}
}
});
computButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if (mathService == null){
labelView.setText("未绑定服务");
return;
}
long a = Math.round(Math.random()*100);
long b = Math.round(Math.random()*100);
long result = mathService.Add(a, b);
String msg = String.valueOf(a)+" + "+String.valueOf(b)+
" = "+String.valueOf(result);
labelView.setText(msg);
}
});
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mathService = ((MathService.LocalBinder)service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mathService = null;
}
};
}
三、IntentService(多线程)
IntentService是Android里面的一个封装类,继承自四大组件之一的Service。
作用是:处理异步请求,实现多线程
1. 工作流程

工作流程
注意:若启动IntentService 多次,那么每个耗时操作则以队列的方式在 IntentService的onHandleIntent回调方法中依次执行,执行完自动结束。
2. 实现步骤
· 步骤1:定义IntentService的子类:传入线程名称、复写onHandleIntent()方法
· 步骤2:在Manifest.xml中注册服务
· 步骤3:在Activity中开启Service服务
3. 例子
服务中的代码默认运行在主线程中,如果直接在服务里执行一些耗时操作,容易造成ANR(Application NotResponding)异常,所以就需要用到多线程的知识了。
一个比较标准的服务可以这样写
package com.example.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {
public static final String TAG = "MyService";
//服务执行的操作
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
public void run() {
//处理具体的逻辑
stopSelf(); //服务执行完毕后自动停止
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
(1)新建一个MyIntentService类
package com.example.servicetest;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
public class MyIntentService extends IntentService{
public MyIntentService() {
super("MyIntentService");//调用父类有参构造函数。这里我们手动给服务起个名字为:MyIntentService
// TODO Auto-generated constructor stub
}
//该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止
@Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
for(int i = 0;i<3;i++) {
//打印当前线程的id
Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d("MyIntentService","onDestroy");
}
}
(2)在清单文件中对服务进行注册服务
<service android:name=".MyIntentService"></service>
(3)实现启动
在activity_main.xml中添加一个按钮button3_stop_intentservice,用于启动MyIntentService服务,代码略
case R.id.button3_stop_intentservice:
2 Log.d("MainActivity","主线程的id是:"+Thread.currentThread().getId());
3 Intent intentService = new Intent(this,MyIntentService.class);
4 startService(intentService);
5 default:

3. 总结
从上面源码可以看出,IntentService本质是采用Handler & HandlerThread方式:通过HandlerThread单独开启一个名为IntentService的线程
创建一个名叫ServiceHandler的内部Handler
把内部Handler与HandlerThread所对应的子线程进行绑定
通过onStartCommand()传递给服务intent,依次插入到工作队列中,并逐个发送给onHandleIntent()
通过onHandleIntent()来依次处理所有Intent请求对象所对应的任务
因此我们通过复写方法onHandleIntent(),再在里面根据Intent的不同进行不同的线程操作就可以了