分类: Android平台
2015-03-03 22:03:34
1、如何定义(启动)一个Service:
核心步骤如下:
新建一个Android项目ServiceTest,具体步骤如下:
(1)新建一个MyService类,继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法,代码如下:
可以看到,我们只是在onCreate()、onStartCommand()和onDestroy()方法中分别打印了一句话,并没有进行其它任何的操作,注意代码注释中这三个方法的作用。
onBind()方法是Service中唯一的一个抽象方法,所以必须要在子类里实现。我们知道,Service可以有两种启动方式:一种是startService(),另一种是bindService()。第二种启动方式才会用到onBind()方法。我们这先用第一种方式启动Service,所以暂时忽略onBind()方法。
(2)在清单文件中声明:(和Activity标签并列)
<service android:name=".MyService"> service>
(3)修改activity_main.xml代码,如下:
<LinearLayout xmlns:android="" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1_start_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Service" /> <Button android:id="@+id/button2_stop_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Stop Service" /> LinearLayout>
我们在布局文件中加入了两个按钮,一个用于启动Service,一个用于停止Service。
(4)在MainActivity作为程序的主Activity,在里面加入启动Service和停止Service的逻辑,代码如下:
package com.example.servicetest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
private Button button1_start_service;
private Button button2_stop_service;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1_start_service = (Button) findViewById(R.id.button1_start_service);
button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
button1_start_service.setOnClickListener(this);
button2_stop_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1_start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.button2_stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
default:
break;
}
}
}
核心代码:31行至32行、35行至36行。
可以看到,在Start Service按钮的点击事件里,我们构建出了一个Intent对象,并调用startService()方法来启动MyService,这时MyService会调用自身的onCreate()方法(该Service未创建),接着调用onStart()方法。然后在Stop Serivce按钮的点击事件里,我们同样构建出了一个Intent对象,并调用stopService()方法来停止MyService 这时MyService会调用自身的onDestory()方法。注意startService()和stopService()方法都是定义在Context类当中的,所以可以在MainActivity中直接调用这两个方法.代码的逻辑非常简单。
这样的话,一个简单的带有Service功能的程序就写好了。
启动和停止服务:
定义好服务之后,接下来看一下如何启动和停止一个服务,这主要是借助Intent来实现的。注意startService()和stopService()方法都是定义在Context类当中的,所以可以在MainActivity中直接调用这两个方法。
运行上面的程序,点击button1_start_service按钮,启动服务,后台打印日志如下:
说明服务启动成功。
那么如果我再连续点三次button1_start_service按钮,后台增加的日志如下:
事实上,onCreate()方法只会在Service第一次被创建的时候调用,而onStartCommand()方法在每次启动服务的时候都会调用。
我们还可以在正在“设置--应用---运行”中找到这个服务,如下图所示:
点开上图中的红框部分,可以看到:
如果我们再点击button2_stop_service按钮或者点击上图中的“Stop”,MyService服务就停止掉了:
需要注意的是:
2、停止一个started服务有两种方法:
(1)在外部使用stopService()
(2)在服务内部(onStartCommand方法内部)使用stopSelf()方法。
3、onStartCommand方法的返回值:
onStartCommand方法执行时,返回的是一个int型。这个整型可以有三个返回值:START_NOT_STICKY、START_STICKY、START_REDELIVER_INTENT
IntentService的引入:---平行于service,因为有优于service的属性而被使用
我们在第一段中就已经说了,服务中的代码默认运行在主线程中,如果直接在服务里执行一些耗时操作,容易造成ANR(Application Not Responding)异常,所以就需要用到多线程的知识了。
因此一个比较标准的服务可以这样写:
1 package com.example.servicetest; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.IBinder; 6 7 public class MyService extends Service { 8 9 public static final String TAG = "MyService"; 10 11 //服务执行的操作 12 @Override 13 public int onStartCommand(Intent intent, int flags, int startId) { 14 new Thread(new Runnable() { 15 public void run() { 16 //处理具体的逻辑 17 stopSelf(); //服务执行完毕后自动停止 18 } 19 }).start(); 20 return super.onStartCommand(intent, flags, startId); 21 } 22 23 @Override 24 public IBinder onBind(Intent intent) { 25 // TODO Auto-generated method stub 26 return null; 27 } 28 29 }
核心代码:14至19行,在子线程中处理具体的逻辑。
需要注意的是,如果没有第17行的stopSelf(),服务一旦启动后,就会一直处于运行状态,必须调用stopService()或者stopSelf()方法才能让服务停止下来;所以我们添加了17行的stopSelf(),服务执行完毕后会自动停止。
虽说上面的这种写法并不复杂,但总会有一些程序猿忘记开启线程,或者忘记调用stopSelf()方法。为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类,这个类就很好的解决了上面所提到的两种尴尬。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent()回调方法中执行,并且每次只会执行一个工作线程,执行完第一个后,再执行第二个,以此类推。
2、IntentService的作用:
当我们需要这样一次性完成的任务时,就可以使用IntentService来完成。
3、IntentService的用法:
我们在上面的项目ServiceTest基础上进行修改,步骤如下:
(1)新建一个MyIntentService类,继承自IntentService,并重写父类的onHandleIntent()方法,代码如下:
1 package com.example.servicetest; 2 3 import android.app.IntentService; 4 import android.content.Intent; 5 import android.util.Log; 6 7 public class MyIntentService extends IntentService{ 8 9 public MyIntentService() { 10 super("MyIntentService");//调用父类有参构造函数。这里我们手动给服务起个名字为:MyIntentService 11 // TODO Auto-generated constructor stub 12 } 13 14 //该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止 15 @Override 16 protected void onHandleIntent(Intent intent) { 17 // TODO Auto-generated method stub 18 for(int i = 0;i<3;i++) { 19 //打印当前线程的id 20 Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId()); 21 try { 22 Thread.sleep(1000); 23 } catch (InterruptedException e) { 24 // TODO Auto-generated catch block 25 e.printStackTrace(); 26 } 27 } 28 } 29 30 @Override 31 public void onDestroy() { 32 // TODO Auto-generated method stub 33 super.onDestroy(); 34 Log.d("MyIntentService","onDestroy"); 35 } 36 }
这里首先要提供一个无参的构造方法,并且必须在其内部调用父类的有参构造方法(9至12行),我们在第10行手动将服务的名字改为“MyIntentService”。
然后在子类中实现onHandleIntent()这个抽象方法,可以在这个方法里去处理一些具体的逻辑,我们就用三次for循环,打印当前线程的id,每次延时1秒。
因为这个服务在运行结束后会自动停止,所以我们在onDestroy()方法中打印日志验证一下。
(2)在清单文件中对服务进行注册服务:
<service android:name=".MyIntentService"> service>
(3)在activity_main.xml中添加一个按钮button3_stop_intentservice,用于启动MyIntentService服务,代码略。
(4)在MainActivity里面加入启动IntentService的逻辑,核心代码如下:
1 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:
我们在第02行中,打印主线程的id。
运行程序,点击按钮button3_stop_intentservice,显示如下:
由此可见,启动一个IntentService和启动一个普通的Service,步骤是一样的。
4、Service和Thread的关系:
不少Android初学者都可能会有这样的疑惑,Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案可能会有点让你吃惊,因为Service和Thread之间没有任何关系!
之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,你还会觉得它和Thread有什么关系吗?
其实,后台和子线程是两个完全不同的概念:
Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,Service既然是运行在主线程里,在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。
既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例;而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
所以说,一个比较标准的Service,就可以写成本段中第1节的样子。
part IV
使用Bind Service完成Service和Activity之间的通信
有没有什么办法能让它们俩的关联更多一些呢?比如说在Activity中指挥Service去干什么,Service就去干什么。当然可以,只需要让Activity和Service建立关联就好了。
1、Bind Service的介绍: ---所谓绑客户端定服务,指的是service.onBind()收到bindService函数发出的intent对象后返回对应的IBinder对象
应用程序组件(客户端)通过调用bindService()方法能够绑定服务,然后Android系统会调用服务的onBind()回调方法,则个方法会返回一个跟服务器端交互的Binder对象。
这个绑定是异步的,bindService()方法立即返回,并且不给客户端返回IBinder对象。要接收IBinder对象,客户端必须创建一个ServiceConnection类的实例,并且把这个实例传递给bindService()方法。ServiceConnection对象包含了一个系统调用的传递IBinder对象的回调方法,即 onServiceConnected().
注意:只有Activity、Service、Content Provider能够绑定服务;BroadcastReceiver广播接收器不能绑定服务。
2、实现Service和Activity之间通信步骤:
我们依然在第二段中的项目ServiceTest基础上进行修改。
观察上面第二段中MyService中的代码,你会发现一直有一个onBind()方法我们都没有使用到,这个方法其实就是用于和Activity建立关联的,修改MyService中的代码,如下所示:
1 package com.example.servicetest; 2 3 import android.app.Service; 4 import android.content.Intent; 5 import android.os.Binder; 6 import android.os.IBinder; 7 import android.util.Log; 8 9 public class MyService extends Service { 10 11 public static final String TAG = "MyService"; 12 13 private MyBinder mBinder = new MyBinder(); 14 15 @Override 16 public void onCreate() { 17 super.onCreate(); 18 Log.d(TAG, "onCreate"); 19 } 20 21 @Override 22 public int onStartCommand(Intent intent, int flags, int startId) { 23 Log.d(TAG, "onStartCommand"); 24 return super.onStartCommand(intent, flags, startId); 25 } 26 27 @Override 28 public void onDestroy() { 29 super.onDestroy(); 30 Log.d(TAG, "onDestroy"); 31 } 32 33 @Override 34 public IBinder onBind(Intent intent) { 35 return mBinder; //在这里返回新建的MyBinder类 36 } 37 38 //MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度 39 class MyBinder extends Binder { 40 41 public void startDownload() { 42 Log.d("TAG", "startDownload() executed"); 43 // 执行具体的下载任务 44 } 45 public int getProgress(){ 46 Log.d("TAG", "getProgress() executed"); 47 return 0; 48 } 49 50 } 51 52 }
38至50行:新建一个MyBinder类,继承Binder:让里面的方法执行下载任务,并获取下载进度。当然,这里只是两个模拟方法,并没有实现真正的功能,我们通过打印日志的形式来体现。
接着创建MyBinder的实例(13行),然后在onBind()方法里返回这个实例(35行)。
核心代码是35行,返回这个mBinder,是一个IBinder类型,就可以把这个IBinder类型传递到MainActivity中,从而调用Service里面的方法。下面就要看一看,在MainActivity是如何调用Service里面的两个方法的。
(2)检查清单文件,是否已经对Service进行注册:
(3)在activity_main.xml中继续添加两个按钮button3_bind_service和button4_unbind_service,用于绑定服务和取消绑定服务。最终,activity_main.xml的完整代码如下:
<LinearLayout xmlns:android="" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/button1_start_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Start Service" /> <Button android:id="@+id/button2_stop_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Stop Service" /> <Button android:id="@+id/button3_bind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="bind Service" /> <Button android:id="@+id/button4_unbind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="unbind Service" /> LinearLayout>
(4)接下来再修改MainActivity中的代码,让MainActivity和MyService之间建立关联,代码如下所示:
package com.example.servicetest02;
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 android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity implements OnClickListener {
private Button button1_start_service;
private Button button2_stop_service;
private Button button3_bind_service;
private Button button4_unbind_service;
private MyService.MyBinder myBinder;
boolean mBound = false; //一开始,并没有和Service绑定.这个参数是用来显示绑定状态
//匿名内部类:服务连接对象
private ServiceConnection connection = new ServiceConnection() {
//当服务异常终止时会调用。注意,解除绑定服务时不会调用
@Override
public void onServiceDisconnected(ComponentName name) {
mBound = false; //服务异常终止时,状态为未绑定
}
//和服务绑定成功后,服务会回调该方法
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
//在Activity中调用Service里面的方法
myBinder.startDownload();
myBinder.getProgress();
mBound = true; //true说明是绑定状态
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button1_start_service = (Button) findViewById(R.id.button1_start_service);
button2_stop_service = (Button) findViewById(R.id.button2_stop_service);
button3_bind_service = (Button) findViewById(R.id.button3_bind_service);
button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service);
button1_start_service.setOnClickListener(this);
button2_stop_service.setOnClickListener(this);
button3_bind_service.setOnClickListener(this);
button4_unbind_service.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1_start_service:
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.button2_stop_service:
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.button3_bind_service:
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.button4_unbind_service:
//如果和Service是绑定的状态,就解除绑定。
if(mBound){
unbindService(connection);
mBound=false;
}
break;
default:
break;
}
}
}
可以看到,这里我们首先创建了一个ServiceConnection的匿名类(24行),在里面重写了onServiceConnected()方法和onServiceDisconnected()方法,如果当前Activity与服务连接成功后,服务会回调onServiceConnected()方法,
在onServiceConnected()方法中,我们又通过向下转型得到了MyBinder的实例(34行),有了这个实例,Activity和Service之间的关系就变得非常紧密了。现在我们可以在Activity中根据具体的场景来调用MyBinder中的任何public方法(36、37行),即实现了Activity指挥Service干什么Service就去干什么的功能。
当然,现在Activity和Service其实还没关联起来了呢,这个功能是在Bind Service按钮的点击事件里完成的。可以看到,这里我们仍然是构建出了一个Intent对象,然后调用bindService()方法将Activity和Service进行绑定。bindService()方法接收三个参数,第一个参数就是刚刚构建出的Intent对象,第二个参数是前面创建出的ServiceConnection的实例,第三个参数是一个标志位,这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后会自动创建Service(即使之前没有创建Service也没有关系),这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行。
然后如何我们想解除Activity和Service之间的关联怎么办呢?调用一下unbindService()方法就可以了,这也是Unbind Service按钮的点击事件里实现的逻辑。
现在让我们重新运行一下程序吧,在MainActivity中点击一下Bind Service按钮,LogCat里的打印日志如下图所示:
可以看到,只点击了Bind Service按钮,但是oncreate()方法得到了执行,而onStartCommand()方法不会执行。
另外需要注意,任何一个Service在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity建立关联,还可以和任何一个Activity建立关联,而且在建立关联时它们都可以获取到相同的MyBinder实例。
如何销毁Service:
根据上面第一段的知识,我们介绍了销毁Service最简单的一种情况:现在卸载程序,重新运行程序,点击Start Service按钮启动Service,再点击Stop Service按钮停止Service,这样MyService就被销毁了:
现在回到本段内容。卸载程序,重新开始。那么如果我们只点击的Bind Service按钮呢?由于在绑定Service的时候指定的标志位是BIND_AUTO_CREATE,说明点击Bind Service按钮的时候Service也会被创建,这时应该怎么销毁Service呢?其实也很简单,点击一下Unbind Service按钮,将Activity和Service的关联解除就可以了:
以上这两种销毁的方式都很好理解。那么如果我们既点击了Start Service按钮,又点击了Bind Service按钮会怎么样呢?这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将Unbind Service按钮和Stop Service按钮都点击一下(没有先后顺序),Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。
点击Unbind Service按钮后,再次点击Unbind Service按钮按钮引发的问题:
假设现在Service和Activity已经相关联了,点击Unbind Service按钮能够解除绑定,如果继续点击Unbind Service按钮,程序会异常退出,这说明代码不够完善,我们需要在代码中加一个判断是否绑定的标记mBound。在改MainActivity中增加一部分代码,最终改MainActivity的完整代码如下:(加粗字体是添加的内容)
1 package com.example.servicetest02; 2 3 import android.app.Activity; 4 import android.content.ComponentName; 5 import android.content.Intent; 6 import android.content.ServiceConnection; 7 import android.os.Bundle; 8 import android.os.IBinder; 9 import android.util.Log; 10 import android.view.View; 11 import android.view.View.OnClickListener; 12 import android.widget.Button; 13 14 public class MainActivity extends Activity implements OnClickListener { 15 private Button button1_start_service; 16 private Button button2_stop_service; 17 private Button button3_bind_service; 18 private Button button4_unbind_service; 19 private MyService.MyBinder myBinder; 20 21 boolean mBound = false; //一开始,并没有和Service绑定.这个参数是用来显示绑定状态 22 23 //匿名内部类:服务连接对象 24 private ServiceConnection connection = new ServiceConnection() { 25 26 //当服务异常终止时会调用。注意,解除绑定服务时不会调用 27 @Override 28 public void onServiceDisconnected(ComponentName name) { 29 mBound = false; //服务异常终止时,状态为未绑定 30 } 31 32 //和服务绑定成功后,服务会回调该方法 33 @Override 34 public void onServiceConnected(ComponentName name, IBinder service) { 35 myBinder = (MyService.MyBinder) service; 36 //在Activity中调用Service里面的方法 37 myBinder.startDownload(); 38 myBinder.getProgress(); 39 mBound = true; //true说明是绑定状态 40 } 41 }; 42 @Override 43 protected void onCreate(Bundle savedInstanceState) { 44 super.onCreate(savedInstanceState); 45 setContentView(R.layout.activity_main); 46 button1_start_service = (Button) findViewById(R.id.button1_start_service); 47 button2_stop_service = (Button) findViewById(R.id.button2_stop_service); 48 button3_bind_service = (Button) findViewById(R.id.button3_bind_service); 49 button4_unbind_service = (Button) findViewById(R.id.button4_unbind_service); 50 button1_start_service.setOnClickListener(this); 51 button2_stop_service.setOnClickListener(this); 52 button3_bind_service.setOnClickListener(this); 53 button4_unbind_service.setOnClickListener(this); 54 } 55 @Override 56 public void onClick(View v) { 57 switch (v.getId()) { 58 case R.id.button1_start_service: 59 Intent startIntent = new Intent(this, MyService.class); 60 startService(startIntent); 61 break; 62 case R.id.button2_stop_service: 63 Intent stopIntent = new Intent(this, MyService.class); 64 stopService(stopIntent); 65 break; 66 case R.id.button3_bind_service: 67 Intent bindIntent = new Intent(this, MyService.class); 68 bindService(bindIntent, connection, BIND_AUTO_CREATE); 69 break; 70 case R.id.button4_unbind_service: 71 //如果和Service是绑定的状态,就解除绑定。 72 if(mBound){ 73 unbindService(connection); 74 mBound=false; 75 } 76 break; 77 78 default: 79 break; 80 } 81 } 82 }
添加的代码是第21行、29行、72行至74行。
这样的话,连续点击Unbind Service按钮,就不会使程序出现异常。
3、started服务与bind服务的区别:
区别一:生命周期
区别二:参数传递
实际开发中的技巧;
4、Service的生命周期:
一旦在项目的任何位置调用了Context的startService()方法,相应的服务就会启动起来,并回调onstartCommand()方法。如果这个服务之前还没有创建过,onCreate()方法会先于onstartCommand()方法执行。服务启动过后,会一直保持运行状态,直到stopService()或stopself()方法被调用。注意虽然每次调用一次startService()方法,onstartCommand()方法就会以执行一次,但实际上每个服务都只会存在一个实例。所以不管你调用了多少次startService()方法,只需调用一次stopService()或stopself()方法,服务就会停止。
另外,还可以调用Context的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法。类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后调用方可以获取到onBind()方法里返回的IBinder对象的实例,这样,就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。
part V 使用Bind Service完成IPC进程间通信
http://www.cnblogs.com/smyhvae/p/4070518.html 五 六