Android 四大应用组件之Service

一、认识Service

1、Service是什么?

Service是一个应用组件, 它用来在后台完成一个时间跨度比较大的工作且没有关联任何界面 。

一个Service可以完成这些工作:访问网络 ,播放音乐 ,文件IO操作 ,大数据量的数据库操作 

2. 服务的特点:

Service在后台运行,不用与用户进行交互,即使应用退出, 服务也不会停止.。

在默认情况下,Service运行在应用程序进程的主线程(UI线程)中,如果需要在Service中处理一些网络连接等耗时的操作,那么应该将这些任务放在分线程中处理,避免阻塞用户界面

3、区别Service与Activity? 

(1)Activity:    

Activity对应一个界面 ,应用退出, Activity对象就会死亡 ,应用再次进入, 启动的Activity对象是重新创建的 

(2)Service

Service与用户界面没有任何关系,就算应用退出了Service依旧在运行,而且当再次打开应用启动的Service还是原来运行的Service对象。

4、区别Service与Thread? 

(1)Service

用来在后台完成一个时间跨度比较大的工作的应用组件 

Service的生命周期方法运行在主线程, 如果Service想做持续时间比较长的工作, 需要启动一个分线程(Thread) 

应用退出: Service不会停止 

应用再次进入: 可以与正在运行的Service进行通信 

Thread

用来开启一个分线程的类, 做一个长时间的工作 

Thread对象的run()在分线程执行 

应用退出: Thread不会停止, 

应用再次进入: 不能再控制前面启动的Thread对象

5、Service的分类

(1)Local Service(本地服务)

Service对象与Serive的启动者在同个进程中运行, 两者的通信是进程内通信 

(2)Remote Service(远程服务)

Service对象与Service的启动者不在同一个进程中运行, 这时存在一个进程间通信的问题, Android专门为此设计了AIDL来实现进程间通信

 

二、Service的生命周期

 

三、创建本地服务

创建服务常用的三个方法:

onCreate():服务第一次创建时候调用。

onStartCommand():每次服务启动时候都会调用,如果需要启动后立即执行某些逻辑,就在这里写最好。

onDestory():服务销毁时候调用,可以在这里回收资源。

 

/**
 * 自定义本地服务
 *
 */

public class MyService extends Service {

    public MyService() {
        super();
        Log.e("TAG","MyService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("TAG","onCreate");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("TAG","onBind");
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("TAG","onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.e("TAG","onDestroy");
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("TAG","onUnbind");
        return super.onUnbind(intent);
    }
}
public class MainActivity extends Activity{

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


    //启动本地服务
    public void startMyService(View v){
        Intent intent=new Intent(this,MyService.class);
        startService(intent);
        Toast.makeText(this, "开启本地服务", Toast.LENGTH_SHORT).show();
    }

    //关闭本地服务
    public void stopMyService(View v){
        Intent intent=new Intent(this,MyService.class);
        stopService(intent);
        Toast.makeText(this, "停止本地服务", Toast.LENGTH_SHORT).show();
    }

    private ServiceConnection conn;

    //绑定本地服务
    public void bindMyService(View v){
        Intent intent=new Intent(this,MyService.class);
        //创建连接对象
        if(conn==null){
            conn=new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    Log.e("TAG","ServiceConnection-->onServiceConnected ");
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                    Log.e("TAG","ServiceConnection-->onServiceDisconnected ");
                }
            };
        }
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
        Toast.makeText(this, "bind service", Toast.LENGTH_SHORT).show();
    }

    //解绑本地服务
    public void unbindMyService(View v){
        if(conn!=null){
            unbindService(conn);
            Toast.makeText(this, "unbind service", Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(this, "还没有bindservice", Toast.LENGTH_SHORT).show();
        }
    }


}

布局文件就省略了呀,就是一些按钮而已。

下面最最容易忘记的就是在清单文件中配置

<service android:name=".MyService"/>

看看运行过程:

启动本地服务:MyService--》onCreate--》onStartCommand

停止本地服务:onDestroy

绑定本地服务:MyService---》onCreate---》onBind

解绑本地服务:onUnbind--》 onDestroy

看是不是验证了上面的一句话:启动本地活动后会运行onStartCommand,但是只是单纯的绑定而不会运行。

发现了吗上面是在活动中控制服务的启动与关闭,那么怎么让服务自己停止呢?其实很简单,只要在MyService中任意位置使用stopSelf()方法,服务自己就把自己停止了。例如:使用服务后台下载文件,下载完毕可以stopSelf()让服务停下来。

 

四、活动与服务之间进行通信

要想让活动和服务之间进行进行通信,就需要使用服务的onBind(),onUnBind()方法。

绑定服务和解绑服务:服务和谁绑定?答案是活动。和谁解绑,答案也是活动。绑定了活动后,在活动中就能返回服务中的一些内容。

主要的地方就是继承Binder的子类和onBind()返回继承Binder的子类的对象,活动通过 ServiceConnection获得继承Binder的子类实例,从而获得服务中发生的事情。

public class MyService extends Service {

    private static final String TAG="MyService";
    private DownBinder downBinder=new DownBinder();

    class DownBinder extends Binder{
        public void startDown(){
            Log.e(TAG, "startDown: 开始下载");
        }
        public int getProgress(){
            Log.e(TAG, "getProgress:下载进度 ");
            return 0;
        }
    }

    public MyService() {
    }

    //只有在服务第一次创建才执行
    @Override
    public void onCreate() {
        super.onCreate();
    }

    //每次服务启动都会执行
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    //服务将要被销毁时候执行
    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    //绑定服务,绑定服务不会执行onStartCommand方法
    @Override
    public IBinder onBind(Intent intent) {
        return downBinder;
    }

    //解绑服务
    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }
}
public class MainActivity extends Activity implements View.OnClickListener
{
    Button bind;
    Button unbind;
    MyService.DownBinder downBinder;

    private ServiceConnection serviceConnection=new ServiceConnection() {
        //服务绑定了就会调用
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downBinder= (MyService.DownBinder) service;
            downBinder.startDown();//开始下载
            downBinder.getProgress();//下载进度
        }

        //在连接正常关闭的情况下是不会被调用的, 该方法只在Service 被破坏了或者被杀死的时候调用
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bind=findViewById(R.id.but_1);
        unbind=findViewById(R.id.but_2);
        bind.setOnClickListener(this);
        unbind.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.but_1: //绑定服务
                Intent intent=new Intent(this,MyService.class);
                bindService(intent,serviceConnection,BIND_AUTO_CREATE);
                break;
            case R.id.but_2:
                unbindService(serviceConnection);
                break;
        }
    }
}
 <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            
        </service>

 

五、服务的生命周期

服务创建:

第一次调用startService()方法启动服务,会调用onCreate()和onStartCommand()方法。

服务已经启动后再次调用startService()启动不会再调用onCreate(),直接调用onStartCommand()方法,记住只要调用startService()就会调用onStartCommand()方法。

当服务startService()之后服务就会一直运行,除非stopService()或者在服务内部调用stopSelf()服务将停止。虽然每次调用startService()方法都会执行onStartCommand()方法,但是每个服务都只会存在一个实例。只要调用stopService()或者在服务内部调用stopSelf()一次,服务就会停止。

使用bindServie()将获得一个持久链接,然后会自动调用服务的onBind()方法,onBind()方法返回一个实例,就是上面的例子中自定义的DownBinder对象。当然如果服务还没有就先自动调用onCreat()方法再自动调用onBind()。

服务销毁:

调用了startService()方法再调用stopService()方法,就会自动调用服务的onDestroy()方法,之后服务销毁。

调用bindService()再调用unBindService()方法就会自动调用服务的onDestroy()方法,之后服务销毁。

注意:如果对服务调用了startService方法又调用了bindService()方法,根据Android系统的机制可以知道,只要服务被启动或绑定了之后就会一直处于运行状态,那么就必须同时调用stopService()和unBindService()方法然后才会自动调用服务的onDestroy()方法,服务才能销毁。

 

六、创建前台服务

前台服务和普通服务的区别在于,前台服务会一直在系统的状态栏显示一个图标,类似于通知。

只需要在服务的onCreate()方法中把当前的服务变成前台服务就可以了,

重点是startForeground(1,notification);方法。

修改上面四的MyService的代码:

public class MyService extends Service {

    private static final String TAG="MyService";
    private DownBinder downBinder=new DownBinder();

    class DownBinder extends Binder{
        public void startDown(){
            Log.e(TAG, "startDown: 开始下载");
        }
        public int getProgress(){
            Log.e(TAG, "getProgress:下载进度 ");
            return 0;
        }
    }

    public MyService() {
    }

    //只有在服务第一次创建才执行
    @Override
    public void onCreate() {
        super.onCreate();
        Intent intent=new Intent(this,MainActivity.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,0);
        Notification notification=new NotificationCompat.Builder(this)
                .setContentTitle("前台服务")
                .setContentText("为了测试前台服务")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round))
                .setContentIntent(pendingIntent)
                .build();
        //关键代码startForeground使当前的服务变成前台服务
        startForeground(1,notification);
    }

    //每次服务启动都会执行
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    //服务将要被销毁时候执行
    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    //绑定服务,绑定服务不会执行onStartCommand方法
    @Override
    public IBinder onBind(Intent intent) {
        return downBinder;
    }

    //解绑服务
    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }
}

嘿嘿,是不是看到手机状态栏出来图标拉呀,嗯,肯定的。

 

七、IntentService的使用

默认服务是在主线程中运行,如果需要在服务中做耗时的操作就需要在服务中开启一个分线程处理耗时操作。

例如:

public class MyService extends Service {
    private static final String TAG="MyService";
    public MyService() {
    }

    //只有在服务第一次创建才执行
    @Override
    public void onCreate() {
        super.onCreate();

    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //处理具体的逻辑
                stopSelf();//处理完了就停止服务,不要忘记呀
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

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

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

上面没有使用IntentService,而是自己写的分线程。

下面看一看IntentService,它是一个简单异步处理还能自动停止的服务。

public class MyIntentService extends IntentService{

     public MyIntentService(){
         super("MyIntentService");
     }

     public MyIntentService(String name) {
         super(name);
     }

     @Override
     protected void onHandleIntent(Intent intent) {
         //这里直接处理耗时逻辑,不用担心ANR,也不用自己写分线程
     }

     @Override
     public void onDestroy() {
         super.onDestroy();
     }
 }

 

八、下面来个实例

让服务控制下载文件,用AsyncTask下载图片并更新UI,由服务控制AsyncTask的下载暂停和取消,由Activity负责绑定启动服务然后点击按钮控制开始下载,暂停,取消。

public interface IDownloadListener {
    //文件下载进度
    void onProgress(int progress);
    //下载成功
    void onSuccess();
    //暂停下载
    void onPause();
    //取消下载
    void onCancel();
    //下载失败
    void onFailed();
    //开始下载
    void onStart();
}
public class DownloadListener implements IDownloadListener {

    private Context mContext;
    private Service mService;
    private DownloadTask mTask;

    public DownloadListener(Context context, Service service,DownloadTask task){
        mContext=context;
        mService=service;
        mTask=task;
    }
    @Override
    public void onProgress(int progress) {
        this.getNotification("正在下载:",progress);
    }

    @Override
    public void onSuccess() {
        //释放资源
        mTask=null;
        //下载成功将前台服务通知关闭
        mService.stopForeground(true);
        //启动一个提示下载成功的通知
        getNotificationManager().notify(1,getNotification("下载成功了",-1));
    }

    @Override
    public void onPause() {
        mTask=null;
        Toast.makeText(mContext,"暂停下载",Toast.LENGTH_LONG).show();
    }

    @Override
    public void onCancel() {
        mTask=null;
        mService.stopForeground(true);
    }

    @Override
    public void onFailed() {
        mTask=null;
        mService.stopForeground(true);
        getNotificationManager().notify(1,getNotification("下载失败了",-1));

    }

    @Override
    public void onStart() {
        mService.startForeground(1,getNotification("开始下载",-1));

    }

    private NotificationManager getNotificationManager(){
        return (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
    }

    private Notification getNotification(String title, int progress){
        Intent intent=new Intent(mContext,MainActivity.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(mContext,0,intent,0);
        Notification.Builder builder =new Notification.Builder(mContext);
        builder .setContentTitle(title);
        builder.setSmallIcon(R.mipmap.ic_launcher);
        builder.setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(),R.mipmap.ic_launcher_round));
        builder.setContentIntent(pendingIntent);
        if(progress>0){
            builder.setProgress(100,progress,false);
            builder.setContentText(progress+"%");
        }
        return builder.build();
    }
}

上面的实现类主要 是控制UI,根据状态变化更新UI。

// url地址String类型, 进度Integer类型, 执行结果Integer类型
public class DownloadTask extends AsyncTask<String,Integer,Integer> {

    private static final int TYPE_SUCCESS=0; //成功
    private static final int TYPE_FAILED=1;  //失败
    private static final int TYPE_PAUSED=2;  //暂停
    private static final int TYPE_CANCEL=3;  //取消
    private boolean isPause=false;            //是否暂停
    private boolean isCnacel=false;           //是否取消
    private DownloadListener downloadListener; //这个对象主要就是负责更新下载时候的UI变化
    private Context mContext;
    private Service mService;
    private int lastProgress=0;

    public DownloadTask(Context context, Service service){
        mContext=context;
        mService=service;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        downloadListener=new DownloadListener(mContext,mService,this);
        downloadListener.onStart();
    }

    @Override
    protected Integer doInBackground(String... params) {
        InputStream inputStream=null;
        RandomAccessFile randomAccessFile=null;
        File file=null;
        try {
            long alreadDownloadLenght=0;
            //获得文件路径
            String url=params[0];
            //截取文件名
            String name=url.substring(url.lastIndexOf("/"));
            //获取SD卡下载路径
            String sdDownDir= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
            file=new File(sdDownDir+name);
            if(file.exists()){
                alreadDownloadLenght=file.length(); //已经下载的文件的长度
            }
            //获得下载的文件的总字节数
            long contentLenght=getContentLenth(url);
            //下载的文件不存在
            if(contentLenght==0){
                return TYPE_FAILED;
            }
            //已经下载完成了
            if(contentLenght==alreadDownloadLenght){
                Log.e("kankan", "doInBackground:已经下载完成了: "+contentLenght);
                return TYPE_SUCCESS;
            }
            OkHttpClient client=new OkHttpClient();
            Request request=new Request.Builder()
                    //断点续传,指定从哪个字节开始下载
                    .addHeader("RANGE","bytes="+alreadDownloadLenght+"-")
                    .url(url)
                    .build();
            Response response=client.newCall(request).execute();
            if(response!=null && response.isSuccessful()){
                inputStream=response.body().byteStream();
                randomAccessFile=new RandomAccessFile(file,"rw");
                //跳过已经下载的字节
                randomAccessFile.seek(alreadDownloadLenght);
                int len=0;
                int total=0;
                byte[] flush=new byte[1024];
                while( (len=inputStream.read(flush))!=-1){
                    //如果暂停下载
                    if(isPause){
                        return TYPE_PAUSED;
                    }else if(isCnacel){ //如果取消下载
                        return TYPE_CANCEL;
                    }
                    randomAccessFile.write(flush,0,len);
                    total+=len;
                    int progress= (int) ((alreadDownloadLenght+total)*100/contentLenght);
                    publishProgress(progress);
                }
                randomAccessFile.close();
                response.body().close();
                return TYPE_SUCCESS;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //如果文件取消下载就删除文件
            if(isCnacel && file!=null){
                file.delete();
            }
        }
        return TYPE_FAILED;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        //更新下载进度
        int progress=values[0];
        //r如果当前的小于等于上次的就不更新进度了
        if(progress<=lastProgress){
            downloadListener.onProgress(progress);
        }
    }

    @Override
    protected void onPostExecute(Integer integer) {
        super.onPostExecute(integer);
        //处理结果,其实就是doInBackground(String... strings)的返回值而已
        switch (integer){
            case TYPE_SUCCESS:
                downloadListener.onSuccess();
                break;
            case TYPE_CANCEL:
                downloadListener.onCancel();
                break;
            case TYPE_PAUSED:
                downloadListener.onPause();;
            case TYPE_FAILED:
                downloadListener.onFailed();
        }
    }

    /**
     * 获得下载文件的字节数
     * @param url
     * @return
     */
    public long getContentLenth(String url){
        long length=0;
        try {
            OkHttpClient client=new OkHttpClient();
            Request request=new Request.Builder().url(url).build();
            Response response=client.newCall(request).execute();
            if(response!=null && response.isSuccessful()){
                length= response.body().contentLength();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return length;
    }

    //暂停下载
    public void pauseDown(){
        isPause=true;
    }

    //取消下载
    public void cancelDown(){
        isCnacel=true;
    }
}

上面的类主要是实现文件下载和判断状态的改变。

public class DownloadService extends Service {

    private DownloadBinder downloadBinder=new DownloadBinder();
    DownloadTask task;

    public class DownloadBinder extends Binder {
        //开始下载
        public void startDown(String url){
            task.execute(url);
        }

        //暂停下载
        public void pauseDown(){
            task.cancelDown();
        }

        //取消下载
        public void cancelDown(){
            task.cancelDown();
        }
    }

    public DownloadService() {
    }

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("service", "onBind: "+getApplication().getClass() );
        task=new DownloadTask(getApplication(),this);
        return super.onStartCommand(intent, flags, startId);
    }
}

 

public class MainActivity extends Activity implements View.OnClickListener
{

    private Button down;
    private Button cancel;
    private Button pause;

    DownloadService.DownloadBinder binder=null;

    private ServiceConnection connection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            binder= (DownloadService.DownloadBinder) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        down=findViewById(R.id.down);
        pause=findViewById(R.id.pause);
        cancel=findViewById(R.id.cancel);
        down.setOnClickListener(this);
        pause.setOnClickListener(this);
        cancel.setOnClickListener(this);
        //绑定服务
        Intent intent=new Intent(this,DownloadService.class);
        startService(intent);
        bindService(intent,connection,BIND_AUTO_CREATE);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.down: //下载
                binder.startDown("http://uploads.oh100.com/allimg/1709/117-1FZ5102542-52.jpg");
                break;
            case R.id.cancel://取消
                binder.cancelDown();
                break;
            case R.id.pause: //暂停
                binder.pauseDown();
                break;
        }
    }
}

记得注册服务和开启网络权限呀

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<service android:name=".DownloadService"/>

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值