参考:
https://blog.csdn.net/guolin_blog/article/details/11952435
bindService+startService Demo
清单文件
注意注册service
<service android:name=".MyService" >
<intent-filter>
<action android:name="com.test.chj" />
</intent-filter>
</service>
布局文件
非常简单,四个按钮。最外面是个相对布局
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="start service" />
<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/start"
android:text="stop service" />
<Button
android:id="@+id/bind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/stop"
android:text="bind service" />
<Button
android:id="@+id/unbind"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/bind"
android:text="unbind service" />
Java文件
先创建Service类,onBind和MyBinder可以先不看后面会说,所以主要就是三个方法:onCreate onStartCommand onDestroy
public class MyService extends Service {
private static final String TAG = "MyService";
@Override
public void onCreate() {
Log.v(TAG, "onCreate");
Log.d(TAG, "MyService thread id is " + Thread.currentThread().getId());
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.v(TAG, "onDestroy");
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends Binder {
public void startDownload() {
Log.d("TAG", "startDownload() executed");
}
}
}
主界面内容也很简单,就是4个按钮,一个标志位。在onCreate初始化,设置点击事件。注意内部类ServiceConnection只服务于bind的service
public class MainActivity extends Activity implements OnClickListener {
protected static final String TAG = "MainActivity";
Button start, stop, bind, unbind;
boolean isBound =false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "MainActivity thread id is " + Thread.currentThread().getId());
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.start);
stop = (Button) findViewById(R.id.stop);
bind = (Button) findViewById(R.id.bind);
unbind = (Button) findViewById(R.id.unbind);
start.setOnClickListener(this);
stop.setOnClickListener(this);
bind.setOnClickListener(this);
unbind.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent i = new Intent();
i.setAction("com.test.chj");
switch (v.getId()) {
case R.id.start:
startService(i);
break;
case R.id.stop:
stopService(i);
break;
case R.id.bind:
bindService(i, conn, Context.BIND_AUTO_CREATE);
isBound = true;
break;
case R.id.unbind:
if(isBound){
unbindService(conn);
isBound= false;
}
break;
default:
break;
}
}
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.v(TAG, "onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "onServiceConnected");
}
};
}
Service 启动方式startService
依次点击start service和stop service:
点击start service触发Service的 onCreate和 onStartCommand
点击stop service触发Service的 onDestroy
多次点击start service
onCreate只会触发一次,onStartCommand会触发多次
多次点击start service后多次点击stop service
onDestroy只会触发一次
Service启动方式bindService
依次点击bind service和unbind service:
点击bind service会触发Service的 onCreate和内部类ServiceConnection的onServiceDisconnected方法
点击unbind service触发Service的 onDestroy
多次点击bind service后多次点击unbind service
onCreate onServiceDisconnected onDestroy都只会调用一次。
注意:如果没有bindService就unbind会报错:
java.lang.IllegalArgumentException: Service not registered:
所以需要加个bool变量,确保解绑前确实调用过bind
Service混合启动
实验:
点击bind Service后点击stop Service不会触发Service的onDestroy
点击start Service后点击unbind Service也不会触发Service的onDestroy
无论先点击的bind Service还是start Service,需要stop Service和unbind Service都点击之后onDestroy才会触发
结论:
如果一个Service既通过start方法启动,又通过bind启动,则必须调用两个方法对应的接触Service方法,Service才会销毁。
Start和Bind service比较
Android 4.4验证如下
bind service与Activity紧密结合。当Activity销毁时(比如按了返回键),bind方式启动的activity会直接销毁(调用onDestroy),如果从最近列表删除应用程序,Service连onDestroy都不会调用就被销毁了。
而startService和Activity没有关系,除非Activity主动调用stopService,否则该Service与启动它的Activity无关,即使那个Activity已经关闭
另一个区别就是bindService可以和启动它的Activity通信,就是通过上面没有说的onBind方法和MyBinder对象
利用binder进行Service与Activity的进行通信(限bind启动)
其实之前bind启动时,触发的方法应该有三:
Service的onCreate onBind和Activity内部类的onServiceConnected
具体通信方式如下:
给MyBinder添加返回MyService对象的方法,添加MyService和MyBinder的测试方法
在onServiceConnected中既可以获得MyBinder的实例,又可以通过MyBinder获得MyService的实例,所以两者的方法都可以调用,这样就实现了Activity和Service的通信(通过Binder对象)
MyService:
public void test(){
//do sth
Log.v(TAG, "test");
}
class MyBinder extends Binder {
public MyService getService(){
return MyService.this;
}
public void startDownload() {
Log.d(TAG, "startDownload");
new Thread(new Runnable() {
@Override
public void run() {
// 执行具体的下载任务
}
}).start();
}
}
内部类ServiceConnection onServiceConnected的实现
public void onServiceConnected(ComponentName name, IBinder service) {
Log.v(TAG, "onServiceConnected");
MyBinder binder = (MyBinder) service;
binder.startDownload();
MyService serv =binder.getService();
serv.test();
}
前台Service与后台Service
之前的几种启动方法创建的Service都是后台Service,即没有界面,只能通过开发者选项查看正在运行的服务可以看到这些Service。
那么前台Service区别于后台Service,它有一个通知栏界面
要想把一个后台Service变成一个前台Service很简单,只要在Service的onCreate方法添加如下代码(给Service添加一个通知)
Notification notification = new Notification(R.drawable.ic_launcher,
"有通知到来", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
notification.setLatestEventInfo(this, "这是通知的标题", "这是通知的内容",
pendingIntent);
startForeground(1, notification);
Log.d(TAG, "onCreate() executed");
前台service除了比后台service多了一个通知栏以外,其实最大的区别是前台service优先级高于后台service,不会因为内存不足而被操作系统轻易的kill掉
Service与Thread的关系?
Service的启动给我们一种幻觉,让我们觉得Service是Activity另起的一个线程。其实不然,本地Service仍然是运行在主线程的,我们可以通过在Activity的onCreate和Service的onCreate打印如下log:
Log.d(TAG, " thread id is " + Thread.currentThread().getId());
Log.d(TAG, " process id is " +Process.myPid());
来查看进程号和线程号。打印会发现Service的进程号和线程号都和Activity一致。也就是说,Service同样是运行在主线程的,因此不难得知,在Service中同样是不能直接执行耗时操作的,要另起线程。否则会导致主线程阻塞,发生ANR。
那么,既然如此,为什么不直接在Activity中创建Thread,做耗时操作呢?
原因是:
这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。