TelephonyManager tm =(TelephonyManager) getSystemService(TELEPHONY_SERVICE);
tm.listen(newMyPhoneStateListener(),PhoneStateListener.LISTEN_CALL_STATE);
classMyPhoneStateListenerextendsPhoneStateListener{
//当电话状态改变时,此方法调用
@Override
publicvoid onCallStateChanged(int state,String incomingNumber){
// TODO Auto-generated method stub
super.onCallStateChanged(state, incomingNumber);
switch(state){
caseTelephonyManager.CALL_STATE_IDLE://空闲
if(recorder !=null){
recorder.stop();
recorder.release();
}
break;
caseTelephonyManager.CALL_STATE_OFFHOOK://摘机
if(recorder !=null){
recorder.start();
}
break;
caseTelephonyManager.CALL_STATE_RINGING://响铃
recorder =newMediaRecorder();//单词:媒体录音机
//设置声音来源
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//设置音频文件格式
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//3gp
recorder.setOutputFile("sdcard/haha.3gp");
//设置音频文件编码
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try{
recorder.prepare();
}catch(IllegalStateException e){
// TODO Auto-generated catch block
e.printStackTrace();
}catch(IOException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
}
publicclassBootReceiverextendsBroadcastReceiver{
@Override
publicvoid onReceive(Context context,Intent intent){
//开机自启广播,启动录音机服务,在mainactivity中也启动服务
Intent it =newIntent(context,RecorderService.class);
context.startService(it);
}
publicclassLeaderServiceextendsService{
@Override
publicIBinder onBind(Intent intent){
// 返回一个Binder对象,这个对象就是中间人对象
returnnewZhouMi();
}
classZhouMiextendsBinderimplementsPublicBusiness{
publicvoidQianXian(){
banZheng();
}
publicvoid daMaJiang(){
System.out.println("陪李处打麻将");
}
}
publicvoid banZheng(){
System.out.println("李处帮你来办证");
}
}
把QianXian方法抽取到接口PublicBusiness中定义,因为周密的如果有自己的方法(打麻将)返回onbind去,那在mainactivity中能访问这个方法了。 可以把想对外提供的方法定义一个接口,然后实现它。在mainactivity中可以强转成接口,提供对外的方法。pb.damajiang就不能调用了 当然也可以把打麻将方法弄成私有,不过一般都不那样写
publicinterfacePublicBusiness{
voidQianXian();
}
publicclassMainActivityextendsActivity{
privateIntent intent;
privateMyServiceConn conn;
PublicBusiness pb;
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent =newIntent(this,LeaderService.class);
conn =newMyServiceConn();
//绑定领导服务
bindService(intent, conn, BIND_AUTO_CREATE);
}
publicvoid click(View v){
//调用服务的办证方法
pb.QianXian();
}
//绑定服务时要求必须传入一个ServiceConnection实现类的对象
classMyServiceConnimplementsServiceConnection{
//连接服务成功,此方法调用
@Override
publicvoid onServiceConnected(ComponentName name,IBinder service){
// TODO Auto-generated method stub
pb =(PublicBusiness) service;
}
//服务失去连接时,此方法调用
@Override
publicvoid onServiceDisconnected(ComponentName name){
// TODO Auto-generated method stub
}
}
}
LeaderService.ZhouMi zm = ( LeaderService.ZhouMi) service;书中是这样转的 书中的方法
publicclassMyServiceextendsService{
privateDownloadBinder mBinder =newDownloadBinder();
classDownloadBinderextendsBinder{
publicvoid startDownload(){
Log.d("MyService","startDownload executed");
}
publicint getProgress(){
Log.d("MyService","getProgress executed");
return0;
}
}
@Override
publicIBinder onBind(Intent intent){
return mBinder;
}
……
}
privateMyService.DownloadBinder downloadBinder;
privateServiceConnection connection =newServiceConnection(){
@Override
publicvoid onServiceDisconnected(ComponentName name){
}
@Override
publicvoid onServiceConnected(ComponentName name,IBinder service){
downloadBinder =(MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
};
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
……
bindService =(Button) findViewById(R.id.bind_service);
unbindService =(Button) findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
publicclassMusicServiceextendsService{
@Override
publicIBinder onBind(Intent intent){
// TODO Auto-generated method stub
returnnewMusicController();
}
//必须继承binder,才能作为中间人对象返回
classMusicControllerextendsBinderimplementsMusicInterface{
publicvoid play(){//不写
MusicService.this就成了死循环了,自己调用自己MusicService.this.play();
}
publicvoid pause(){
MusicService.this.pause();
}
}
publicvoid play(){
System.out.println("播放音乐");
}
publicvoid pause(){
System.out.println("暂停播放");
}
}
publicinterfaceMusicInterface{
void play();
void pause();
}
publicclassMainActivityextendsActivity{
MusicInterface mi;
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent =newIntent(this,MusicService.class);
//混合调用
//为了把服务所在进程变成服务进程
startService(intent);
//为了拿到中间人对象
bindService(intent,newMusicServiceConn(), BIND_AUTO_CREATE);
}
classMusicServiceConnimplementsServiceConnection{
@Override
publicvoid onServiceConnected(ComponentName name,IBinder service){
// TODO Auto-generated method stub
mi =(MusicInterface) service;
}
@Override
publicvoid onServiceDisconnected(ComponentName name){
// TODO Auto-generated method stub
}
}
//开始播放按钮(an)
publicvoid play(View v){
mi.play();
}
//暂停播放按钮
publicvoid pause(View v){
mi.pause();
}
}
服务的生命周期
1.一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onStartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于onStartCommand()方法执行。
2.服务启动了之后会一直保持运行状态,直到 stopService()或stopSelf()方法被调用。注意虽然每调用一次startService()方法,onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。所以不管调用了多少次startService()方法,只需调用一次stopService()或stopSelf()方法,服务就会停止下来了。
3.还可以调用Context的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到onBind()方法里返回的IBinder对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。
4.当调用了startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了startService()方法,又调用了bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据Android系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行这样就把服务的生命周期完整地走了一遍。
远程服务只能隐式启动,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点
publicclassMainActivityextendsActivity{
privateMyserviceConn conn;
PublicBusiness pb;
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
conn =newMyserviceConn();
}
publicvoid click(View v){
//启动远程服务
Intent intent =newIntent();
intent.setAction("com.itheima.remote");
startService(intent);
}
publicvoid click2(View v){
//停止远程服务
Intent intent =newIntent();
intent.setAction("com.itheima.remote");
stopService(intent);
}
publicvoid click3(View v){
Intent intent =newIntent();
intent.setAction("com.itheima.remote");
bindService(intent, conn, BIND_AUTO_CREATE);
}
publicvoid click4(View v){
unbindService(conn);
}
classMyserviceConnimplementsServiceConnection{
@Override
publicvoid onServiceConnected(ComponentName name,IBinder service){
//把Ibinder中间人对象强转成publicbusiness
pb =Stub.asInterface(service);
}
@Override
publicvoid onServiceDisconnected(ComponentName name){
// TODO Auto-generated method stub
}
}
publicvoid click5(View v){
try{
pb.qianXian();
}catch(RemoteException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
不是非得调用startservice,bindService也可以,能访问支付宝服务里的方法就行,上面只是演示
publicclassMainActivityextendsActivity{
PayInterface pi;
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent =newIntent();
intent.setAction("com.itheima.pangzhi");
bindService(intent,newServiceConnection(){
@Override
publicvoid onServiceDisconnected(ComponentName name){
// TODO Auto-generated method stub
}
@Override
publicvoid onServiceConnected(ComponentName name,IBinder service){
// 使用aidl中自动生成的方法来强转
pi =Stub.asInterface(service);
}
}, BIND_AUTO_CREATE);
}
publicvoid click(View v){
//调用远程服务的支付方法
try{
pi.pay();
}catch(RemoteException e){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
使用服务注册广播接收者
publicclassMainActivityextendsActivity{
privateIntent intent;
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent =newIntent(this,RegisterService.class);
}
publicvoid start(View v){
startService(intent);
}
publicvoid stop(View v){
stopService(intent);
}
}
publicclassRegisterServiceextendsService{
privateScreenReceiver receiver;
@Override
publicIBinder onBind(Intent intent){
// TODO Auto-generated method stub
returnnull;
}
@Override
publicvoid onCreate(){
super.onCreate();
//1.创建广播接收者对象
receiver =newScreenReceiver();
//2.创建intent-filter对象
IntentFilter filter =newIntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
//3.注册广播接收者
registerReceiver(receiver, filter);
}
@Override
publicvoid onDestroy(){
super.onDestroy();
//解除注册
unregisterReceiver(receiver);
}
}
publicclassScreenReceiverextendsBroadcastReceiver{
@Override
publicvoid onReceive(Context context,Intent intent){
// TODO Auto-generated method stub
String action = intent.getAction();
if(Intent.ACTION_SCREEN_OFF.equals(action)){
System.out.println("屏幕关闭");
}
elseif(Intent.ACTION_SCREEN_ON.equals(action)){
System.out.println("屏幕打开");
}
}
}
使用前台服务
//和创建通知的方法类似。只不过这次在构建出Notification对象后并没有使用NotificationManager来将通知显示出来,而是调用了startForeground()方法。这个方法接收两个参数,第一个参数是通知的id,类似于notify()方法的第一个参数,第二个参数则是构建出的Notification对象。调用startForeground()方法后就会让MyService变成一个前台服务,并在系统状态栏显示出来。现在重新运行一下程序,并点击StartService或BindService按钮,MyService就会以前台服务的模式启动了,并且在系统状态栏会显示一个通知图标,下拉状态栏后可以看到该通知的详细内容
Notification notification=newNotification(R.drawable.ic_launcher,"notification comes",System.currentTimeMillis());
Intent notificationIntent=newIntent(this,MainActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0,notificationIntent,0);
notification.setLatestEventInfo(this,"This is title","This iscontent", pendingIntent);
startForeground(1, notification);
Log.d("MyService","onCreate executed");
后台定时任务
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pendingIntent);
RTC_WAKEUP,则这里传入 1970年 1月 1日 0点至今的时间再加上延迟执行的时间。
long triggerAtTime = System.currentTimeMillis() + 10 * 1000;
manager.set(AlarmManager.RTC_WAKEUP, triggerAtTime, pendingIntent);
创建一个 ServiceBestPractice项目, 然后新增一个 LongRunningService类,代码如下所示:
publicclassLongRunningServiceextendsService{
@Override
publicIBinder onBind(Intent intent){
returnnull;
}
@Override
publicint onStartCommand(Intent intent,int flags,int startId){
newThread(newRunnable(){
@Override
publicvoid run(){
Log.d("LongRunningService",
"executed at "+newDate().toString());
}
}).start();
AlarmManager manager =(AlarmManager) getSystemService(ALARM_SERVICE);
int anHour =60*60*1000;// 这是一小时的毫秒数
long triggerAtTime =SystemClock.elapsedRealtime()+ anHour;
Intent i =newIntent(this,AlarmReceiver.class);
PendingIntent pi =PendingIntent.getBroadcast(this,0, i,0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
returnsuper.onStartCommand(intent, flags, startId);
}
}
下一步是要新建一个 AlarmReceiver类,并让它继承自 BroadcastReceiver,代码如下所示:
publicclassAlarmReceiverextendsBroadcastReceiver{
@Override
publicvoid onReceive(Context context,Intent intent){
Intent i =newIntent(context,LongRunningService.class);
context.startService(i);
}
}
这就已经将一个长期在后台定时运行的服务完成了。因为一旦启动 LongRunningService,就会在onStartCommand()方法里设定一个定时任务,这样一小时后 AlarmReceiver的 onReceive()方法就将得到执行,然后在这里再次启动 LongRunningService,这样就形成了一个永久的循环,保证 LongRunningService可以每隔一小时就会启动一次,一个长期在后台定时运行的服务自然也就完成了。
接下来需要在打开程序的时候启动一次LongRunningService,之后 LongRunningService就可以一直运行了。修改 MainActivity中的代码,如下所示:
publicclassMainActivityextendsActivity{
@Override
protectedvoid onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Intent intent =newIntent(this,LongRunningService.class);
startService(intent);
}
}
<service android:name=".LongRunningService">
</service>
<receiver android:name=".AlarmReceiver">
</receiver>
使用 IntentService
服务中的代码都是默认运行在主线程当中的,如果直接在服务里去处理一些耗时的逻辑,就很容易出现 ANR(Application NotResponding)的情况。所以这个时候就需要用到Android多线程编程的技术,应该在服务的每个具体的方法里开启一个子线程,然后在这里去处理那些耗时的逻辑,但是,这种服务一旦启动之后,就会一直处于运行状态,必须调用 stopService()或者stopSelf()方法才能让服务停止下来。所以,如果想要实现让一个服务在执行完毕后自动停止的功能,就可以这样写:
publicint onStartCommand(Intent intent,int flags,int startId){
returnsuper.onStartCommand(intent, flags, startId);
newThread(newRunnable(){
@Override
publicvoid run(){
// 处理具体的逻辑
stopSelf();
}
}).start
}
@Override
publicvoid onDestroy(){
super.onDestroy();
}
虽说这种写法并不复杂,但是总会有一些程序员忘记开启线程,或者忘记调用stopSelf()方法。为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类,这个类就很好地解决了前面所提到的两种尴尬,下面就来看一下它的用法。新建MyIntentService类继承自IntentService
publicclassMyIntentServiceextendsIntentService
{
//服务里开启子线程
//1.提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数
publicMyIntentService(){
super("MyIntentService");//调用父类的有参构造函数
}
//2.在子类中去实现onHandleIntent()这个抽象方法,在这个方法中可以去处理一些具体的逻辑,而且不用担心ANR的问题,因为这个方法已经是在子线程中运行的了。这里为了证实一下在onHandleIntent()方法中打印了当前线程的id
@Override
protectedvoid onHandleIntent(Intent p1)
{
// TODO: Implement this method
// 打印当前线程的id
Log.d("MyIntentService","Thread id is "+Thread.currentThread().getId());
}
//3.根据IntentService的特性,这个服务在运行结束后应该是会自动停止的,所以重写onDestroy()方法,在这里也打印了一行日志,以证实服务是不是停止掉了。
publicvoid onDestroy(){
super.onDestroy();
Log.d("MyIntentService","onDestroy executed");
}
//4.加入一个用于启动MyIntentService这个服务的按钮
//5.在StartIntentService按钮的点击事件里面去启动MyIntentService这个服务, 并在这里打印了一下主线程的id,稍后用于和IntentService进行比对,其实IntentService的用法和普通的服务没什么两样。
//6.不要忘记,服务都是需要在AndroidManifest.xml里注册的点击Start IntentService按钮后,观察LogCat中的打印日志可以看到,不仅MyIntentService和MainActivity所在的线程id不一样,而且onDestroy()方法也得到了执行,说明MyIntentService在运行完毕后确实自动停止了。集开启线程和自动停止于一身,IntentService还是博得了不少程序员的喜爱
}
遇到的错误:
//这是在服务里,接收activity传递过来的数据,每次用户点击ListActivity当中的一个条目时,就会服务里的该方法
@Override
publicint onStartCommand(Intent intent,int flags,int startId){
// TODO Auto-generated method stub
//从Intent对象当中将Mp3Info对象取出
Mp3Info mp3Info =(Mp3Info)intent.getSerializableExtra("mp3Info");
//生成一个下载线程,并将Mp3Info对象作为参数传递到线程对象当中
DownloadThread downloadThread =newDownloadThread(mp3Info);
//启动新线程
Thread thread =newThread(downloadThread);
thread.start();
returnsuper.onStartCommand(intent, flags, startId);
}