Days23 Service(一)

1、概念:
a、可以长时间运行在后台,而没有用户界面的组件,不可以与用户直接交互。
b、一个服务不是一个单独的线程。默认情况下,Service中的所有代码都运行在主线程中。注意:因为Service默认工作在主线程中,所以不能直接用它做耗时的工作,最好在Service中开启新的线程运行耗时的任务。
c、一个服务不是一个单独的进程。默认情况下,它与运行程序工作在同一线程中。
d、Service很大程度上充当了应用程序后台线程管理器的角色。(如果在Activity中开启一个线程,当Activity关闭后,线程依然在工作,但是与开启它的Activity失去联系,也就是说这个线程处于失去管理的状态。使用Service可以对后台运行的线程进行有效的管理)
e、服务的分类:
1、本地服务Local Service 用于应用程序内部;用于实现应用程序自己的耗时任务
2、远程服务Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其它应用进行操作。
2、Service需要注册:
总结四大组件均如何注册、继承关系图…………….
3、IntentService
相对Service的好处:
1、自带线程,onHandleIntent()方法工作在子线程中,不必手动开启新的线程
2、线程工作结束后自动终止线程,无需手动调用stopService()或者stopSelf()方法终止线程(终止服务的方法)
3、提供了一个onStartCommand()方法的默认实现,它将Intent先传送至工作队列,然后从工作队列中每次取出一个Intent传送至onHandleIntent()方法,在该方法中对Intent做相应的处理
4、提供了一个onBind()方法的默认实现,它返回null
**5、IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程。但你若是想在Service中让多个线程并发的话,就得使用startService()方法,在Service内部起多个线程,但是这样的话,你可要处理好线程的同步。
4、备注:
a、Service是不能自己启动的,只有通过 Context 对象调用startService() 和bindService() 方法来启动。
b、在Service每一次的开启关闭过程中,只有onStartCommand()可被多次调用(通过多次startService调用),其他onCreate()、onBind()、onUnbind()、onDestory()在一个生命周期中只能被调用一次。
c、当Activity被销毁,Service是否被销毁?
bindService会,Activity被销毁会调用Service的onUnBind(),onDestroy()方法
StartService不会
注意:多个Activity可以使用同一个Service,当Service被创建过之后,若再有Activity开启服务则只调用onStartCommand()方法,并且,在任一Activity中关闭Service,就会调用onDestroy方法销毁服务。
5、启动Service有两种方法
(一)Context.startService(Intent)
调用者与服务之间没有关联,即使调用者退出,服务仍然可以运行
1、startService的生命周期:
onCreate() 创建服务
onStartCommand() 服务开始运行
onDestory() 服务被停止
在程序中调用Context.startService()会先构造一个Service,然后触发执行生命周期中的onCreate() onStartCommand()方法,此时服务就开始正式运行;
若此时退出程序,没有调用stopService(),Service会一直在后天运行,再打开程序,则由于Service没有被停止,所以只调用onStartCommand()方法(所以一个Service的onStartCommand()方法可能会被调用很多次);
如果在程序中调用Context.stopService()会触发执行生命周期中的onDestory()方法,会让服务停止,此时,再想运行服务,就会再构造一个Service再执行onCreate()、onStartCommand()方法
2、案例:
a、在主页面中点击download按钮,开启一个Service下载视频,将视频保存在SD卡的指定位置;
b、下载完成后发送一个广播,广播的意图action为“ido”;
c、新建一个接收广播类继承BroadcastReceiver,在MainActivity中动态注册,在该类的onReceive方法中判断接受的Intent的action是否是“ido”频段,若是,发送一个通知,通知中包含一个等待意图(PendingIntent);
d、用户点击后跳转到播放界面,播放界面有一个VideoView,设置VideoView的视频地址,start()方法开始播放视频,并取消通知栏的指定通知
主界面:

public class MainActivity extends AppCompatActivity {

    private NotificationReceiver receiver = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

//        注意:***第一次:忘记写了
        receiver = new NotificationReceiver();
    }

    @Override
    protected void onResume() {
        super.onResume();

        IntentFilter intentFilter = new IntentFilter();

        intentFilter.addAction("ido");

        registerReceiver(receiver,intentFilter);
    }

    @Override
    protected void onPause() {
        super.onPause();

        if(receiver != null){
            unregisterReceiver(receiver);
        }
    }

    public void download(View view) {

        Intent intent = new Intent(this, VedioDownLoadService.class);

        startService(intent);
    }
}

下载视频服务:

public class VedioDownLoadService extends IntentService {

//重写该方法时,原方法有一个参数是name,可以删除后给父类带参构造传入一个“”
    public VedioDownLoadService() {
        super("");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        byte[] data = OkHttpUtils.getBytesByUrl(MyUrl.VIDEO_URL);

        boolean flag = SDCardUtils.saveData2SDCard(data,"sign","hehe.mp4");

        if(flag){
            Intent intent1 = new Intent();

            intent1.setAction("ido");

            sendBroadcast(intent1);
        }


    }

}

接收广播并发送通知:

public class NotificationReceiver extends BroadcastReceiver {
    public NotificationReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if("ido".equalsIgnoreCase(action)){
            NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

            builder.setSmallIcon(R.mipmap.ic_launcher);
            builder.setTicker("biubiu");
            builder.setContentTitle("下载完成");
            builder.setContentText("请观看!");

            Intent intent2 = new Intent(context,VedioPlayActivity.class);
            PendingIntent pi = PendingIntent.getActivity(context,1,intent2,PendingIntent.FLAG_ONE_SHOT);

            builder.setContentIntent(pi);

            NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

            manager.notify(2,builder.build());
        }
    }
}

播放视频界面

public class VedioPlayActivity extends AppCompatActivity {

    private VideoView vvPlay = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_vedio_play);

        initView();

        String path = SDCardUtils.getSDCardPath()+ File.separator+"sign"+File.separator+"hehe.mp4";

        vvPlay.setVideoPath(path);

        vvPlay.start();

        NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        manager.cancel(2);
    }

    private void initView() {
        vvPlay = (VideoView) findViewById(R.id.vvPlay);
    }
}

3、onStartCommand回调方法的返回值
START_STICKY(常量值:1):sticky的意思是“粘性的”。使用这个返回值时,我们启动的服务跟应用程序”粘”在一起,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务。当再次启动服务时,传入的第一个参数将为null;
START_NOT_STICKY(常量值:2):“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
START_REDELIVER_INTENT(常量值:3):重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值再次传入。
START_STICKY_COMPATIBILITY(常量值:0):如果执行完onStartCommand()方法后服务被异常终止,系统并不保证该服务会被再次启动
可以理解为发生车祸后的人:
START_STICKY:(常量值:1)车祸后自己苏醒,但是失忆;
START_NOT_STICKY:(常量值:2)车祸后再也没有苏醒;
START_REDELIVER_INTENT:(常量值:3)车祸后自己苏醒,依然保持记忆。
4、自定义线程,使程序终止

//    自定义一个线程,目的是让程序发生OOM异常,以终止当前app,来观察服务是否会自动重新启动,是否会携带数据
    class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            List<Bitmap> list = new ArrayList<Bitmap>();

            while(true){
                list.add(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher));
            }
        }
    }

(二)Context. bindService(intent, conn, BIND_AUTO_CREATE);
第一个参数指定了要绑定的Service的意图;方法的第二个参数为ServiceConnection接口的实现类的对象;方法的第三个参数指的是在service不存在时创建一个。
内部流程:在程序中调用 bindService(intent, conn, BIND_AUTO_CREATE)方法会先创建Service实例然后触发执行Service生命周期的onCreate()、onBind()方法,此时服务开始运行,onBind()方法会返回一个IBinder接口实现类的实例;此实例作为参数传到onServiceConneted方法中,可以通过实现类的实例进行操作。
生命周期:
onCreate()创建服务
onBind()绑定服务,服务开始运行
onUnbind()取消绑定
onDestory()服务被停止
ServiceConnection接口实现类:
作用:它监视服务类与Activity之间的连接,在重写onServiceConnected(ComponentName componentName, IBinder iBinder)方法时,其中第二个参数IBinder即onBind()方法返回的接口实例
必须重写两个方法:
onServiceConnected(ComponentName componentName, IBinder iBinder)
onServiceDisconnected(ComponentName componentName)Android系统与Service连接意外丢失时调用这个,比如服务崩溃了或被强杀了。
注意:当客户端解除绑定时,这个方法不会被调用。
案例:
启动MediaPlayer播放音乐:
实现功能:
1、点击start后播放
2、可以拖动滑块调节进度,调节音频播放进度
3、播放时可以暂停,暂停了可以播放,停止后归零
MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener,SeekBar.OnSeekBarChangeListener{

    private SeekBar sbShow = null;
    private TextView txtAll , txtAlso;
    private Button btnStart,btnStop;

    private PlayService playService = null;
    private PlayConn conn = null;
    private PlayReceiver receiver = null;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initView();

        setListioner();

        setTitle("早睡早起身体好");

        Intent intent = new Intent(this,PlayService.class);

        conn = new PlayConn();

        bindService(intent,conn,BIND_AUTO_CREATE);
    }

    @Override
    protected void onStart() {
        super.onStart();
        receiver = new PlayReceiver();

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("sign");

        registerReceiver(receiver,intentFilter);
    }

    @Override
    protected void onStop() {
        super.onStop();

        if(receiver != null){
            unregisterReceiver(receiver);
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btnStart:
                playService.play();

                if(playService.isPlaying()){
                    btnStart.setText("pause");
                }else{
                    btnStart.setText("start");
                }
                break;
            case R.id.btnStop:
                playService.stop();

                btnStart.setText("start");

                sbShow.setProgress(0);

                txtAll.setText(R.string._00_00);
                txtAlso.setText(R.string._00_00);
                break;
        }
    }

//    用户拖动时因为没有广播发送,所以要根据滑块的进度来更新时间
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        currentTime(sbShow.getMax(),progress);
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        playService.isSend(false);
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        playService.isSend(true);

        playService.setPregress(seekBar.getProgress());
    }

    class PlayReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if("sign".equalsIgnoreCase(intent.getAction())){
                int duration = intent.getIntExtra("duration",0);
                int position = intent.getIntExtra("position",0);

                sbShow.setMax(duration);
                sbShow.setProgress(position);

                currentTime(duration,position);
            }
        }
    }

    public void currentTime(int duration,int position){
        int max = duration/1000;
        int mm = max/60;
        int ms = max%60;
        String strMM="";
        String strMS="";
        if(mm<10){
            strMM = "0"+mm;
        }else{
            strMM = ""+ms;
        }
        if(ms<10){
            strMS = "0"+ms;
        }else {
            strMS = ""+ms;
        }

        txtAll.setText(strMM+":"+strMS);

        int also = (duration-position)/1000;
        int am = also/60;
        int as = also%60;
        String strAM = "";
        String strAS = "";
        if(am<10){
            strAM = "0"+am;
        }else{
            strAM = ""+as;
        }
        if(as<10){
            strAS = "0"+as;
        }else{
            strAS = ""+as;
        }

        txtAlso.setText(strAM+":"+strAS);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }

    class PlayConn implements ServiceConnection{

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            PlayService.MyBinder ibinder = (PlayService.MyBinder) service;

            playService = ibinder.getService();

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    }

    private void setListioner() {
        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
        sbShow.setOnSeekBarChangeListener(this);
    }

    private void initView() {
        sbShow = (SeekBar) findViewById(R.id.sbShow);
        txtAll = (TextView) findViewById(R.id.txtAll);
        txtAlso = (TextView) findViewById(R.id.txtAlso);
        btnStart = (Button) findViewById(R.id.btnStart);
        btnStop = (Button) findViewById(R.id.btnStop);
    }
}

PlayService

public class PlayService extends Service {

    private MediaPlayer mediaPlayer = null;

    private boolean isSend = true;

    public PlayService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();

//        创建MediaPlayer对象,包含两个参数,context和歌曲的资源id
        mediaPlayer = MediaPlayer.create(this, R.raw.hua);
//        设置媒体一直循环
        mediaPlayer.setLooping(true);

//        开启播放线程
        new PlayThread().start();
    }

//    播放线程,每隔一秒发送广播,发送当前进度和最大值
    class PlayThread extends Thread {
        @Override
        public void run() {
            super.run();
            while (true) {
//                当mediaPlayer正在播放且不为null发送广播
                if (mediaPlayer.isPlaying() & mediaPlayer != null) {
                    int duration = mediaPlayer.getDuration();
                    int position = mediaPlayer.getCurrentPosition();

                    Intent intent = new Intent("sign");
                    intent.putExtra("duration", duration);
                    intent.putExtra("position", position);

//                    如果isSend为true即用户没有拖动滑块,发送广播
                    if (isSend) {
                        sendBroadcast(intent);
                    }

//                每隔一秒
                    SystemClock.sleep(1000);
                }
            }
        }
    }

//    控制mediaPlayer的播放与暂停(若正在播放就暂停,若暂停就设置为播放)
    public void play() {
        if (mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
        } else {
            mediaPlayer.start();
        }
    }

//    获得当前mediaPlayer的播放状态
    public boolean isPlaying() {
        return mediaPlayer.isPlaying();
    }

//    停止播放
//    parse是暂停,之后可以直接调用start从原位置恢复播放
//    stop是停止,停止播放并暂时释放资源,需要prepare后再调用start才可以从原位置播放
    public void stop() {
        mediaPlayer.seekTo(0);

        mediaPlayer.pause();
//        mediaPlayer.stop();
    }

//    标志滑块是否处在被触摸或被拖动的状态
    public void isSend(boolean isSend) {
        this.isSend = isSend;
    }

//    根据用户拖动后滑块后的位置设置音频的播放进度
    public void setPregress(int progress) {
        mediaPlayer.seekTo(progress);
    }

//    让媒体播放器停止
    @Override
    public void onDestroy() {
        super.onDestroy();
        mediaPlayer.stop();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

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

//    内部类PlayBinder,目的是将该对象返回给onBind()方法的IBinder,并且可以获得的IBinder实例获得PlayService实例
    class MyBinder extends Binder {
        public PlayService getService() {
            return PlayService.this;
        }
    }
}

案例:
class CounterBinder extends Binder implements ICounter
在IBinder实现类中实现接口ICounter的方法,在MainActivity中即可将IBinder对象转为ICounter对象,进而调用ICounter中的方法。
备注:
getSystemService(String name)是Android中很重要的一个方法,根据NAME来取得对应的Object,然后转化为相应的服务对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值