**概述:**android中的Service是四大组件之一,它的功能十分类似于Activity,但是又有所不同。Activity主要是通过UI界面的交互作用完成任务,而Service则完全是一个隐藏于后台的功能执行者。比如后台播放音乐,后台监听地理位置的变化等等。根据Service启动方式的不同,可以将Service分为start型和bind型。
一个service实现的步骤如下:
1)定义一个MyService并且继承自Service(或者IntentService),根据需要的启动方式重写方法。
2)在AndroidManifest.xml中添加声明
3)声明一个intent对象,并调用启动方法
Start型Service:
start型service是通过startService(intent)方法来启动service的,启动service的线程称为主线程,启动后会运行onStartCommand()中的方法,如果该方法中的操作十分耗时,则会堵塞主线程,影响app性能。这个时候一般是在onStartCommand()中新建一个线程去完成这个耗时操作。service运行后一般不会主动销毁,需要自己在外部stopService()或者在内部使用stopSelf()方法。若自己定义的service是继承IntentService的,则无需考虑阻塞和销毁问题。
start型实例代码:
MainActivity代码:
package com.example.administrator.startedservice;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void startClickService(View v){
Intent intent = new Intent(this,MyService.class);
startService(intent);
}
}
MyService代码:
package com.example.administrator.startedservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//耗时操作
for(int i=0;i<10;i++){
System.out.println(startId+"---"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return START_STICKY;
}
}
运行上面的代码会发现,点击按钮后,按钮一直处于按下去的状态,这说明onStartCommand的操作会阻塞启动该service的主线程。所以此时的最佳方法是新建一个线程运行耗时操作,此处不再列出代码。
start型Service实现步骤总结:
1、定义自己的service,继承Service,由于是start型,所以只需要重写onStartCommand方法,此方法是用户的业务操作代码。
2、根据自身需求可以选择是否重写其他方法,如onDestroy、onCreate等方法。
3、在Activity中声明Intent对象,并通过startService启动service
4、销毁service
——–IntentService——–
若继承了IntentService则,只需要在MyService中实现onHandleIntent(Intent intent)方法,将耗时的操作放在该方法这种就不会堵塞主线程。并且运行后会自动销毁service。
start后的service若不及时stop则该service会一直存在于后台运行,即使你的app已经退出了
bind型service:以上的start型service有一个很大的缺点:无法与service的调用者进行通信,即start方法没有返回一个值,他只是被动的告诉service应该做些什么,他无法得到service的返回结果,也无法访问service内部的对象。而通过bindService()方法绑定service后,会回调一个onBind()方法,而onBind()方法会返回一个IBinder对象,调用者能通过这个IBinder对象访问service内部成员或者远程操作service。bind型service将程序分为了两大部分,访问service的那一方是客户端,一般是Activity等能够bindService的组件;另一部分是service的服务端。
bind型service实现步骤(本地通信):
1、编辑一个AIDL文件,以IPerson为例,其格式如下:
// IPerson.aidl
package com.zhouyou.bindservice;
// Declare any non-default types here with import statements
interface IPerson {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void setName(String name);
void setSex(String sex);
String getPerson();
}
编辑好后,点击Make Project会在指定位置自动生成java文件。
2、 生成的java文件位于build 文件下的generated/source/aidl/debug中,该Java文件中包含两大部分:1)继承了IBinder的静态抽象类Stub 2)和IPerson.aidl相关的属性和方法函数。在上述的IPerson.java文件中,Stub中含有一个代理类Proxy实现了IPerson接口,其远程的通信都是这个代理来完成的,另外这个抽象类Stub的最重要的方法是asInterface,该方法返回了IPerson的实例。用于判断一个通信是远程还是本地,并根据不同情况返回不同的实例。由于Proxy也是实现了IPerson的,所以当要进行远程通信时,可以返回一个Proxy实例,让这个实例帮助我们去完成相关操作。
3、编辑一个PersonImpl.java用以实现业务对象的具体操作,具体代码如下:
public class PersonImpl extends IPerson.Stub {
private String name,sex;
@Override
public void setName(String name) throws RemoteException {
this.name = name;
}
@Override
public void setSex(String sex) throws RemoteException {
this.sex = sex;
}
@Override
public String getPerson() throws RemoteException {
return "name = " + name + ", sex = " + sex;
}
}
4、开始编写自己的service,主要是重写onBind()方法。该方法会返回一个IBinder的对象,由于PersonImpl是扩展(继承)自IBinder的,所以可以返回一个PersonImpl的对象。具体代码如下:
public class MyService extends Service {
private PersonImpl person;
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
person = new PersonImpl();
return person;
}
}
5、最后,需要完成的就是Activity中的代码了,Intent 的声明和Start型完全一样,不同的是需要调用bindService(Intent intent, ServiceConnection conn,int flag)方法。其中intent包含了该组件绑定哪一个Service;conn会自动调用ServiceConnection中的相关方法,如果连接成功则是onServiceConnected(ComponentName name, IBinder service)方法。该方法中,ComponentName是绑定该服务的组件名称;service则是 MyService中onBind()方法返回的对象,而MyService作为一个服务端,将他自己的对象暴露给了客户端,这样就完成了客户端和服务端的数据通信。我们可以对这个返回的IBinder service进行操作,从而对服务端进行操作,至于如何操作,这就不需要去关心了;flag是bind的模式参数。具体代码如下:
package com.zhouyou.bindservice;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import com.zhouyou.service.MyService;
public class MainActivity extends AppCompatActivity {
private IPerson person;
private ServiceConnection conn = new ServiceConnection() {
@Override
/*
*
* 本函数在服务连接成功时调用
* 其中name表示调用service的组件名称,service则表示被调用的服务中onBind(Intent)方法返回的IBinder对象
* 本函数作用:将service端返回的IBinder对象转换成IPerson并暴露给MainActivity
*
* */
public void onServiceConnected(ComponentName name, IBinder service) {
person = IPerson.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/*
*
* 按钮的点击事件
*
* */
public void bindService(View v){
try {
person.setName("小明");
person.setSex("女");
System.out.println(person.getPerson());
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
*
*onResume()中绑定service,onPause中解绑
*这样不会出现MainActivity has leaked、、、、的错误
*/
public void onResume(){
Intent intent = new Intent(this,MyService.class);
bindService(intent, conn, BIND_AUTO_CREATE);
}
public void onPause(){
unBindService(conn);
}
}
其布局文件中只含一个按钮,十分简单所以在这不再赘述。
由于个人水平有限,难免有不足之处,希望大家不吝赐教。