Android服务 Service

作者: wxyass 分类: Android 发布时间: 2014-10-13 16:19

导语

服务 Service 介绍

服务两种启动方式

  • startService 开始服务
    • 开始服务,会使进程变成为服务进程
    • 直接开启服务,服务一旦启动跟调用者(开启者没有任何关系)
    • 启动服务的activity和服务不再有一毛钱关系
    • 调用者activity退出了,服务还是继续运行活的好好的。
    • 调用者activity,没法访问服务里面的方法。
  • bindService 绑定服务
    • 绑定服务不会使进程变成服务进程
    • 绑定开启服务,服务和开启者(调用者)有密切的关系。
    • 绑定服务,是activity与服务建立连接,如果activity销毁了,服务也会被解绑并销毁,但是如果服务被销毁,activity不会被销毁
    • 不求同时生,但求同时死。只要activity挂了,服务跟着挂了。
    • 调用者activity,可以调用服务里面的方法。

###绑定服务的生命周期
* 绑定服务和解绑服务的生命周期方法:onCreate->onBind->onUnbind->onDestroy
* 不会调用onstart()方法 和 onstartCommand()方法。

###绑定方式开启服务,调用服务方法的流程 (重要)eg:day08音乐播放器

  1. 使用bindService绑定服务的方式开启服务。
    • bindService(intent, new MyConn(), Context.BIND_AUTO_CREATE);
    • 参数1:绑定的意图,绑定那个服务
    • 参数2:ServiceConnection对象,通讯频道(中间人的联系渠道,能找到中间人)
    • 参数3:绑定方式,自动创建服务(绑定时,如果该服务不存在,自动创建该服务)

public class MainActivity extends Activity {
PublicBusiness zms;//定义接口:抽取服务中的方法
Intent intent;//定义意图:绑定那个服务
private MyConnection conn;//定义通讯频道(UI页面与服务的连接对象),可通过通讯频道获取中间人
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent(this, BaoService.class);
conn = new MyConnection();
//绑定服务
bindService(intent, conn, BIND_AUTO_CREATE);
}
}

  1. 创建一个MyConnection类 实现服务与activity的通讯频道(UI页面和服务中间人的联系渠道)能找到中间人。返回的中间人转成接口对象类型
    >为什么要设置中间人?
    >因为UI界面要操作服务里的方法,服务对象去调方法,而服务对象不能被new出来,所以需要在服务中创建中间人内部类,通过中间人对象调用自己类中方法,来调用服务里的方法,而服务又将中间人返给UI界面,这样UI界面就能通过中间人来操作服务里的方法了

Activity中
class MyConnection implements ServiceConnection{
//服务连接建立时,此方法调用。绑定服务,其实就是连接到服务,得到中间人。onBind有返回值才会调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
zms = (PublicBusiness) service;
}

  //失去服务连接时调用。当服务突然异常终止的时候
  @Override
  public void onServiceDisconnected(ComponentName name) {

  }

}

  1. 服务成功绑定的时候 会执行onBinde方法,返回中间人,
    • 中间人中定义了许多方法,可以通过这些方法去调用服务里的方法。
    • 我们只需要在UI界面Activity中得到中间人,并调用中间人的方法,就间接调用了服务的方法

public class BaoService extends Service {

            //服务成功绑定的时候 会执行onBinde方法,返回中间人
            @Override
            public IBinder onBind(Intent intent) {
                return new ZhongMiShu();
            }

            //定义中间人的类,将其私有,只对其他应用提供接口里的方法,通过onBinde方法返回中间人
            private class ZhongMiShu extends Binder implements PublicBusiness{

                //内部人员实现接口的方法帮助我们去调用服务的方法。
                @Override
                public void qianXian(){
                    //调用服务里的方法
                    banZheng();
                }
                //这些方法外部不能调用,因为ZhongMiShu中间人类是私有的。
                public void playMajiang(){
                    System.out.println("一起打麻将");
                }

                //这些方法外部不能调用,因为ZhongMiShu中间人类是私有的。
                public void xisangna(){
                    System.out.println("一起洗桑拿");
                }
            }
        }
  1. 在调用者UI界面 activity代码里面通过中间人调用服务的方法。
    • 中间人调用在自己类中定义的方法,再通过自己的方法去调用服务里的方法

Activity中
//PublicBusiness zms;//Activity中获取接口:接口中抽取中间人对外提供的方法
//服务连接建立时,此方法调用。绑定服务,其实就是连接到服务,得到中间人。
//点击事件:调用中间人实现接口的方法,
public void click(View v){
zms.qianXian();
}

  1. 解除绑定服务 unbindService(conn)。

    //点击事件:解除服务
    public void unbind(View v){
    //解绑服务
    unbindService(conn);
    }

####抽取接口,隐藏私有方法。(这种方式经典,封装的思想,隐藏内部,暴露接口)

参考视频


#找领导办证
* 把服务看成一个领导,服务中有一个banZheng方法,如何才能访问?
* 绑定服务时,会触发服务的onBind方法,此方法会返回一个Ibinder的对象给MainActivity,通过这个对象访问服务中的方法
* 绑定服务

Intent intent = new Intent(this, BanZhengService.class);
bindService(intent, conn, BIND_AUTO_CREATE);

  • 绑定服务时要求传入一个ServiceConnection实现类的对象
  • 定义这个实现类

class MyServiceconn implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
zjr = (PublicBusiness) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}

  • 创建实现类对象
    conn = new MyServiceconn();

  • 在服务中定义一个类实现Ibinder接口,以在onBind方法中返回

服务中 创建中间人对象,需继承Binder(另外将中间人要实现的方法进行抽取成接口PublicBusiness)
class ZhongJianRen extends Binder implements PublicBusiness{
public void QianXian(){
//访问服务中的banZheng方法
BanZheng();
}
public void daMaJiang(){

    }
}
  • 把QianXian方法抽取到接口PublicBusiness中定义

#两种启动方法混合使用
* 用服务实现音乐播放时,因为音乐播放必须运行在服务进程中,可是音乐服务中的方法,需要被前台Activity所调用,所以需要混合启动音乐服务
* 先start,再bind,销毁时先unbind,在stop


##使用服务注册广播接收者
* Android四大组件都要在清单文件中注册
* 广播接收者可以使用清单文件注册
* 一旦应用部署,广播接收者就生效了,直到用户手动停止应用或者应用被删除
* 广播接收者可以使用代码注册
* 需要广播接收者运行时,使用代码注册,不需要时,可以使用代码解除注册
* 电量改变、屏幕开关,必须使用代码注册

1定义java类继承BroadcastReceiver(重写onReceive方法)
public class ScreenReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(Intent.ACTION_SCREEN_OFF.equals(action)){
// Toast.makeText(context, “关闭”, 0).show();
System.out.println(“关闭”);
}
else if(Intent.ACTION_SCREEN_ON.equals(action)){
// Toast.makeText(context, “开启”, 0).show();
System.out.println(“开启”);
}
}
}
2 服务注册广播接收者(使用代码注册接收什么样的广播:屏幕开关广播)
/**
* 此RegisterService服务用于屏幕开关广播接收者的注册
* @author Administrator
*
*/
public class RegisterService extends Service {

        private ScreenReceiver receiver;//定义屏幕开关广播接收者对象

        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }

        @Override
        public void onCreate() {
            super.onCreate();
            //创建广播接收者对象
            receiver = new ScreenReceiver();
            //通过IntentFilter对象指定广播接收者接收什么类型的广播
            IntentFilter filter = new IntentFilter();
            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_SCREEN_ON);

            //注册广播接收者
            registerReceiver(receiver, filter);
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            //解除注册
            unregisterReceiver(receiver);
        }
    }

* 解除注册广播接收者
unregisterReceiver(receiver);

  • 解除注册之后,广播接收者将失去作用

3 只需要在UI界面,开启此RegisterService服务,就表示屏幕开关广播接收者在清单中进行了配置
//点击事件:开启服务
public void click1(View v){
intent = new Intent(this, RegisterService.class);
startService(intent);
}

  • 4在UI界面,停止了此RegisterService服务,就表示不在接收屏幕开关时系统发出的广播
    //点击事件:停止RegisterService服务
    public void click2(View v){
    //1.创建意图
    Intent intent = new Intent(this,RegisterService.class);
    //2.停止服务
    stopService(intent);
    }

##本地服务:服务和启动它的组件在同一个进程
##远程服务:服务和启动它的组件不在同一个进程
* 1、服务和启动它的组件不在同一个进程时,远程服务要先在清单中进行配置配置,类似隐式启动Activity,在清单文件中配置Service标签时,必须配置intent-filter子节点,并指定action子节点

    <service android:name="com.itheima.baopay.PayService">
        <intent-filter >
            <action android:name="com.itheima.baopay"/>
        </intent-filter>
    </service>

* 2、服务和启动它的组件不在同一个进程时,调用者Activity界面启动远程服务时只能隐式启动,同样绑定远程服务,需要隐式绑定远程服务,绑定时第二个参数直接new,得到的接口对象需要用 pb = Stub.asInterface(service)转换类型。
* 2.1、隐式启动远程服务

    //点击事件:启动远程服务
    public void click1(View v)
    {
        //隐式启动远程服务
        Intent intent = new Intent();
        intent.setAction("com.itheima.service");
        startService(intent);
    }

* 2.2、隐式绑定远程服务(绑定服务,就为了操作服务中的方法)隐式绑定远程服务的第二个参数最好直接new MyConnection(),不要创建通讯频道对象传入对象。

    //点击事件:绑定远程服务
    public void click2(View v)
    {
        Intent intent = new Intent();
        intent.setAction("com.itheima.service");
        bindService(intent, new MyConnection(), BIND_AUTO_CREATE);
    }
    // 创建一个MyConnection类,能找到中间人
    class MyConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 强转成publicbusiness
            pb = Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

* 2.3 注意:
– 服务和启动它的组件不在同一个进程时,UI界面获取的中间人,与服务返回的中间人不一致。
– 需要新技术让UI界面获取的中间人,和服务返回的中间人一致。用到AIDL。
* 3 使用得到的接口对象调用服务内部方法

    //点击事件:调用远程服务的方法
    public void click(View v){
        try {
            pb.banzheng();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

#AIDL
* Android interface definition language
* 安卓接口定义语言
* 作用:跨进程通信
* 应用场景:远程服务中的中间人对象,其他应用是拿不到的,那么在通过绑定服务获取中间人对象时,就无法强制转换,使用aidl,就可以在其他应用中拿到中间人类所实现的接口
###AIDL实现步骤
#####对远程服务项目的操作
1. 把接口的后缀名改成aidl
2. 把aidl中的public删掉
3. 让中间人类直接继承stub
#####对访问者项目的操作
1. 把远程服务项目的aidl复制到本项目中
2. aidl文件所在的包,必须与远程服务项目中aidl所在的包一致
3. 使用stub的asInterface方法强转中间人对象

    // 创建一个MyConnection类,能找到中间人
    class MyConnection implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 强转成publicbusiness
            pb = Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

###远程服务步骤示例
/**
* 创建远程MyService服务:用于提供办证服务
* 1、先创建一个PublicBusiness接口,封装MyService服务能够对外提供的内部方法。
* 2、在MyService服务中创建中间人类继承Binder类实现PublicBusiness接口
* 3、中间人实现接口里的方法去调用MyService服务里的方法。
* 4、将中间人返回调用者Activity。
* 5、在清单中注册该MyService服务

 * 1. 把接口的后缀名改成aidl
 * 2. 把aidl中的public删掉
 * 3. 让中间人类直接继承stub

 * @author Administrator
 *
 */

###启动远程服务步骤示例
/**
* 此Activity用于调用远程服务里的方法
* 1、绑定远程服务(隐式启动远程服务),绑定时第二个参数直接new,
* 2、创建通信渠道类,得到中间人.得到的接口对象需要用 pb = Stub.asInterface(service)转换类型。
* 3、通过中间人调用远程服务的方法
* @author Administrator
*
*/

##支付宝远程服务
1. 定义支付宝的服务,在服务中定义pay方法
2. 定义中间人对象,把pay方法抽取成接口
3. 把抽取出来的接口后缀名改成aidl
4. 中间人对象直接继承Stub对象
5. 注册这个支付宝服务,定义它的intent-Filter

##需要支付的应用
1. 把刚才定义好的aidl文件拷贝过来,注意aidl文件所在的包名必须跟原包名一致
2. 远程绑定支付宝的服务,通过onServiceConnected方法我们可以拿到中间人对象
3. 把中间人对象通过Stub.asInterface方法强转成定义了pay方法的接口
4. 调用中间人的pay方法

##五种前台进程
1. 此进程activity执行了onresume方法,获得焦点
2. 此进程拥有一个跟正在与用户交互的activity绑定的服务
3. 此进程拥有一个服务执行了startForeground()方法????
4. 此进程拥有一个正在执行onCreate()、onStart()或者onDestroy()方法中的任意一个的服务
5. 此进程拥有一个正在执行onReceive方法的广播接收者????

##两种可见进程
1. activity执行了onPause方法,失去焦点,但是可见
2. 拥有一个跟可见或前台activity绑定的服务

#国际化 I18N
* Android国际化还是比较方便,一般情况下写代码是不允许直接把文本直接写在代码里,都要使用Strings。
* 代码的提示文本也需要放在资源文件目录下
* string.xml 放在对应的文件夹
– values-en-rGB
– string.xml
– values-en-rUS
– string.xml
* drawable 放在对应的文件夹
– rawable-en-rGB
– drawable-en-rUS
* 文件夹的名称 参考 IE

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注