1、一个项目刚刚做完,,,接着又一个项目开始了。我正在做地 这个新项目的需求里面要点击一个开始巡视的按钮就会在后台一直记录这个手机的路径,然后我第一时间想到了服务。然后我花了两天时间去看各种大神的博客自己在汇总。。最后写在自己的博客李,,,当做记事本一样的来方便的时候好找。
2、服务:没有界面在后台长期运行的四大组件(activity,Service,BroadcastReceiver,ContentProvider)之一,是activity的上一级但不是其父类。
普通生命周期:OnCreate onStartCommand onDestroy 因为没有界面所以没有 onResume onPause onStop onRestart
3、开启服务:
(1)startService(new Intent(this,MyService.class)); (MyService extends Service)。这一种方式启动的服务可以在后台中找到。
生命周期:OncReate onStartCommand 关闭的时候:onDestroy。
如果你是用start方式启动的服务,关闭服务的时候不能用成员变量来stopService(service); 因为如果你开始了
服务然后退出了activity,服务在运行,你又重新进入了activity直接点击关闭服务,这个时候会报空指针异常,因为你的
stopService的Service是全局变量,你没有点击开启服务就不会有值。所以你如果是startService方式开启服务,那么
关闭服务的时候最好自己再new Intent来关闭: Intent intent = new Intent(); intente.setClass(this,MyService.class);
stopService(intent);这样就绝对不会空指针了。
(2)bind方式启动服务:bindService(service,conn,flag);
需要三个参数:第一个是intent,第二个是连接器(连接activity和服务的,第三个是一个标记直接
填:BIND_AUTO_CREATE这个常量的意思是如果没有这个服务就自动创建。这种方式启动的服务在后台是看不见的,这种服务
生命周期非常短,在activity创建之后才被创建开启,在activiy销毁之前解绑销毁。一般用这种方式启动服务是为了调用服务里面
的方法。通过activity与服务通过连接器连接成功之后,服务返回代理人对象,然后通过代理人对象来调用服务里的方法。那么就会
有一个疑问,为什么不直接new Service来调用方法呢?是因为四大组件如果直接new就是一个普通类,就没有上下文,只有start来启动
然后走他们的生命周期才能拿到上下文。
生命周期:onCreate onBind 关闭的时候:onUnBind onDestroy
Service代码:
public class TestService extends Service {
@Override
public IBinder onBind(Intent intent) {
//2返回代理人对象
return new MyBinder();
}
public void test(int num){
Log.i("Service",num+");
}
//1创建一个代理人对象
private class MyBinder extends Binder implements Iservice{
//方法名一定不能和 服务里的方法名因为会形成递归
public void callTest(int num){
test(num);
}
}
IService接口:
public interface Iservice {
//之所以写这个接口,是因为只想暴露服务里面的一部分方法。
/**
*场景:服务里面有五个方法,返回代理人对象之后,activity就可以直接调用这五个方法,如果是自己的方法那还好说,如果不是自己的服务那这样岂不是没有隐私
*可言了么。所以别人就把代理人对象私有,返回私有的代理人对象之后,activity一个方法都调用不了。这个时候写一个接口,里面写两个自己想暴露给外界使用的
*方法,那么就让代理人对象实现这个接口,返回给activity,activity可以在连接器的连接成功的地方,拿到代理人对象。强转成接口,然后调用接口里面的方法,
*因为接口的方法没有方法体,就会去调用实现了这个接口的子类的方法(其实就是多肽)。那么间接的调用了代理人对象里面的方法,然后代理人对象里面的方法调用
*了服务里面的方法。
*/
void callTest(int num);
}
MainActivity 开启服务:
public class MainActivity extends Activity {
private Iservice iservice;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent service=new Intent();
service.setClass(this, TestService.class);
//绑定服务
bindService(service, new MyServiceConn(), BIND_AUTO_CREATE);
}
//点击调用服务里的方法
public void click(View v){
//4 使用返回的代理人对象 调用服务里的方法
iservice.callTest(100);
}
class MyServiceConn implements ServiceConnection{
//当连接完成的时候调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iservice = (Iservice) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
}
}
(3)混合开启服务,因为satrt服务生命周期长,但是不能调用服务里的方法,比如我开启了一个服务想听歌,那么这个服务在一创建的
时候就一直在播放音乐,除非我手动关闭或者是代码关闭服务。那么如果我想切换上下首或者是暂停播放呢?那我就需要调用服务里
的方法来对播放器进行操控。当然这里要理清楚一个关系,是因为我想持续听音乐所以才把播放器放在服务里面让其达到一直播放
的效果。并非是吃饱了撑得非要放在服务里面。既然把音乐播放器放在服务器里面了,那么我的操控也只能调用服务里面的方法才
能对播放器进行控制。那么就需要绑定服务,可是绑定了服务只能猜activity的生命周期内部进行调用,如果退出了界面就无法调用
服务里面的方法。所以我么想要start启动服务的那种长生命周期又想要绑定服务的启动方式来调用服务里面的方法。所以要两者结合
那么问题来了是先satrt还是先bind,其实研究发现都一样的。不过是先unbind还是先onDestroy就有讲究了,必须先解绑才能推出。
虽然你可以先onDestroy服务在解绑也不会报错,但是我看到别的大神博客里说还是先解绑再onDestroy
服务代码和接口代码跟上面的是一样的,不一样的是MainActivity的启动服务代码:
public class MainActivity extends Activity {
private MyServiceConn conn;
private IService mService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//当界面创建的时候 就去开启服务 绑定服务
Intent service=new Intent();
service.setClass(this, TestService.class);
//开启服务
startService(service);
//绑定服务
conn = new MyServiceConn();
bindService(service, conn, BIND_AUTO_CREATE);
}
//播放
public void click(View v){
mService.callTest(200);
}
@Override
protected void onDestroy() {
super.onDestroy();
//当activity销毁的时候
//解除绑定
unbindService(conn);
}
class MyServiceConn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = (IService) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
}
4、我看到别人博客里面讲到服务的时候还讲到了远程服务:
远程服务:其他应用程序的服务,如果想调用别app的服务,必须自己的app和别人的app在一个手机里面
AIDL:android interface definition language 安卓接口描述语言,就是就是专门解决调用远程服务的方式,通俗点说就是通过aidl调用别热
应用里面的服务。我们调用服务里面的方法必须要有一个意图:new Intent(this,Service.class) 这种意图是显示意图,但是别人不会吧
自己的.class文件给你的,那样就直接反编译可以拿到代码了,所以只能用隐式意图 setAction虽然通过隐式意图可以访问到别人的服务
但还是调用不了别人服务里面的方法。因为我们调用服务里面的方法,后来是通过连接器的连接成功之后返回一个代理人对象,当然按照编码
习惯和约定俗成的方式别人也会实现接口然后把代理人对象私有,但是你没有那个接口类,你怎么多肽调用服务里面的方法呢。这个时候就要
用到aidl。
(1)把那个接口的后缀名改成.aidl ,然后会报错,让我们把interface前面的public去掉,因为你改成.aidl是为了让别的app,调用,你还
public别人怎么调用呢?
(2)你改完之后service里面就会报错,找不到接口。然后你去看你的gen下面,会自动生成一个跟接口一样名字的.java文件。你点开文件
仔细看看里面的Stub的继承关系,他继承了binder和实现了你修改的那个接口,所以你直接把之前那个代理人对象的继续和实现都是删掉
然后直接实现Stub(看清楚导包)
(3)把提供远程服务的应用的manifest里面的包名copy一下,然后在你自己想要调用远程服务的那个应用的那个项目的,点击src新建包粘贴包名
然后把远程服务的那个aild文件copy到该包下,就会在gen目录下自动生成一个跟那个接口一样的名字的.java文件。你自习看看Stub的asInterface
方法测参数和意思,是传进去一个IBinder然后强转成一个接口。你就Stub.asInterface(service)强转成接口就可以直接调用了。
(4)如果你的版本大于5.0,还需要再setAction哪里在添加一行:service.setPackage("包名");把提供远程服务的包名写进来