第一行代码 服务

10 服务

10.1服务是什么

服务(Service)是Android中实现程序后台运行的解决方案,适合执行不需要和用户交互但还要求长期执行的任务。
服务的运行不依赖于任何用户界面。
服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程,应用程序进程被杀掉时,所有依赖于该晋城的服务也会停止运行。
服务并不会自动开启线程,所有的代码默认运行在主线程当中。我们需要在服务内部手动创建子线程,并在这里进行具体的任务,否则可能穿线主线程被阻塞住的情况。

10.2Android多线程

10.2.1线程的基本用法
定义一个线程:1.新建一个类继承自Thread 2.重写父类run()方法,并在里面编写耗时逻辑。

class MyThread extends Thread{
     @Override
     public void  run(){
         //处理具体的逻辑 
     }
}

启动这个线程:

  1. new出来它的实例
  2. 调用start()方法(这样run方法中的代码就会在子线程中运行了)

使用集成的方式耦合性有点高,更多时候会使用Runnable接口的方式来定义一个线程。

new MyThread implements Runnable{
     @Override
     public void run(){
          //处理具体的逻辑
     }
}
MyThread myThread = new MyThread();
new Thread(myThread).start();//Thread的构造函数接收一个Runnable参数
也可使用匿名类的方式
new Thread(new Runnable(){
    @Override
    public void run(){
        //处理具体的逻辑
    }
}).start();

10.2.2在子线程中更新UI
同许多其他GUI库一样,Android的UI也是线程不安全的。想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。
错误代码我特喵就不写了。
有事必须在子线程中执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件。对此,Android提供了一套异步消息处理机制。
下面我们来学习一下一步消息处理机制的使用方法。(脑补陈佩斯的皇军托我给您带个话儿)【斜眼笑】

public class MainActivity extends AppCompat implements View.OnClickListener{

    private TextView text;
    public static final int UPDATE_TEXT = 1;

    @Override
    protected void onCreate(Bundel savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layou.activity_main);
        text = (TextView)findViewById(R.id.text);
        Button changeText = (Button)dinfViewById(R.id.change_text);
        changeText.setOnClickListener(this);
    }

    private Handler handler = new Handler(){
        public void handlerMessage(Message msg){
            switch(msg.what){
                case UPDATE_TEXT:
                    text.setText("Nice to meet you");
                    break;
                default:
                    break;
            }
        }
    }

    @Override
    public void onClick(View v){
        switch(v.getId()){
            case R.id.change_text:
                new Thread(new Runnable(){
                    @Override
                    public void run(){
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(messsage);
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}
  1. 定义整形常量UPDATE_TEXT,表示更新TextView这个动作。
  2. 新增Handler对象,并重写父类handlerMessage方法,方法中对具体的Message进行处理。
  3. 创建Message对象,将what字段的值定为UPDATE_TEXT,然后调用Message的sendMessage方法将这条Message发送出去。
    4.Hanlder会收到这条Message,并在hanlderMessage()方法中进行处理。
  4. 此时HandlerMessage中的代码就是在主线程当中进行的了。

10.2.3解析异步消息处理机制
Android一步消息处理主要由四部分组成:Message,Handler,MessageQueue,Looper。

  • Message:线程之间传递的消息,可以在内部携带少量信息,用于在不同线程之间交换信息。what为用户定义的message code(so
    that the recipient can identify what this message is
    about.),arg1,arg2携带整型数据,obj携带一个Object对象。
  • Handler:处理者,主要用于发送和处理消息。发送消息一般使用Handler的sendMessage()方法,发出的消息经过一系列辗转处理后最终传递到Handler的handleMessage()方法中。
  • MessageQueue:消息队列,主要存放所有通过Handler发送的消息,这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
  • Looper:是每个线程中MessageQueue的管家,调用Looper的loop()方法后,会进入到一个无限循环中,每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对象。

异步消息处理流程:

  1. 主线程中创建一个Handler对象,并重写handleMessage()方法。
  2. 当子线程中需要进行UI操作时,在子线程中创建一个Message对象,并通过Handler(可以开始就创建)的sendMessage()方法发送消息。
  3. 消息被添加到MessageQueue的队列中等待被处理。
  4. 一直密切关注 MessageQueue 的
    Looper会将这条待处理的消息取出,最后分发回Handler的handleMessage()方法中。因为Handler在主线程中被创建,handleMessage方法中的代码也会在主线程中进行。

tips:9.2.1中使用的runOnUiThread()方法是异步消息处理机制的接口封装,用法简单,原理一样。
10.2.4使用AsyncTask
对异步消息处理机制不了解,也可以简单的从子线程切换到主线程。原理同样基于异步消息处理机制。
基本用法:
由于AsyncTask是一个抽象类,需要有一个子类去继承他。继承时可以为AsyncTask类指定3个泛型参数。

  1. Params 执行AysncTask时需要传入的参数。可同于在后台任务中使用。
  2. Progress 后台执行任务时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
  3. Result 任务执行完毕后,如果需要对结果进行返回则使用这里指定的泛型作为返回值类型。

最简单的自定义AsyncTask可以写成如下方式:

class DownloadTask extends AsyncTask<Void,Void,Void>{
    ... ...
}

需要重写几个方法才能完成定制。经常需要重写的四个方法:

  • onPreExecute() 在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
  • doInBackground(Params)
    该方法中所有代码在子线程中运行,应在这里处理所有耗时任务。任务一旦完成可以通过return语句来将任务的执行结果返回。若AsyncTask第三个泛型参数指定的是Void,则不返回任务执行结果。注意,该方法中不能进行UI操作,若要更新UI操作,比如反馈当前任务的执行进度,可调用publicProgress(Progress…)方法来完成。
  • onProgressUpdate(Progress)
    当后台任务中调用了publishProgress(Progress)方法后,onProgressUpdate(Progress)方法就会很快被调用。该方法中携带的参数就是在后台任务重传递进来的。在这个方法中可以对UI进行操作,利用参数中的数值可以对界面元素进行相应的更新。
  • onPostExecute(Result)
    当后台任务执行完毕并通过return语句进行返回时,该方法很快被调用,返回的数据会作为参数传递到此方法中,可利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

官方文档:

When an asynchronous task is executed, the task goes through 4 steps:
onPreExecute(), invoked on the UI thread before the task is executed.
This step is normally used to setup the task, for instance by showing
a progress bar in the user interface. doInBackground(Params…),
invoked on the background thread immediately after onPreExecute()
finishes executing. This step is used to perform background
computation that can take a long time. The parameters of the
asynchronous task are passed to this step. The result of the
computation must be returned by this step and will be passed back to
the last step. This step can also use publishProgress(Progress…) to
publish one or more units of progress. These values are published on
the UI thread, in the onProgressUpdate(Progress…)step.
onProgressUpdate(Progress…), invoked on the UI thread after a call
to publishProgress(Progress…). The timing of the execution is
undefined. This method is used to display any form of progress in the
user interface while the background computation is still executing.
For instance, it can be used to animate a progress bar or show logs in
a text field. onPostExecute(Result), invoked on the UI thread after
the background computation finishes. The result of the background
computation is passed to this step as a parameter.

关于什么是UI Thread:https://stackoverflow.com/questions/3652560/what-is-the-android-uithread-ui-thread
UITread就是主线程啦~

一个较完整的自定义AsyncTask可以写成如下方式;

class DownloadTask extends AsyncTask<Void,Integer,Boolean> {

    protected ProgressDialog progressDialog;

    @Override
    protected void onPreExecute(){
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... params){
        try{
            while(true){
                int downloadPercent = doDownload();//假装有`方法。
                publishProgress(downloadPercent);
                if(downloadPercent >= 100){
                    break;
                }
            }
        }catch(Exception e){
            return false;
        }
        return true;
    }

    @Override
    protected void onProgressUpdate(Integer... values){
    //更新下载进度
        progressDialog.setMessage("Dowmload" + values[0] + "%");
    }

    @Override
    protected void onPostExecute(Boolean result){
        progressDialog.dismiss();
        if(result){
            Toast.makeText(context,"Download succeeded",Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
        }
    }
}

方法里代码是在子线程中进行的,因此不会影响到主线程(UI Thread)的运行。
使用AysncTask的诀窍:在doInBackground()中进行具体耗时操作,在onProgressUpdate()中进行UI操作,在onPostExecute()中执行一些任务的收尾工作。
需要启动这个任务,需要代码:

new DownloadTask().execute(); 

10.3服务的基本用法

10.3.1定义一个服务
在项目中定义一个服务:右击包-》new-》service-》service
Export属性表示是否允许除了当前应用程序之外的其他程序访问这个服务,Enabled属性表示是否启用这个服务。
MyService代码如下

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public Binder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

onBind()是唯一一个抽象方法,必须在子类里实现。处理事情的逻辑需要重写Service其他方法,放在那里面。
常用的三个方法:
onCreate()) , 在服务创建的时候调用。服务销毁后会重新创建。
onStartCommand() , 在每次服务启动的时候调用。如果希望服务一旦启动就执行某个动作,将逻辑写在该方法中。
onDestroy() , 在服务销毁的时候调用。应在此方法中回收不再使用的资源。

public class MyService extends Service{
    ...
    @Override
    public void onCreate(){
        super.onCreate();
    }
    @Override
    public int onStartCommand(Intent intent,int flags,int stratId){
        return super.onStartCommand(intent,flags,startId);
    }
    @Override
    public void onDestroy(){
        super.onDestroy();
    }
}

每一个服务都要在AndroidManifest.xml中注册才能生效。这是Android四大组件共有的特点。聪明如你,as已经给你在清单里面写好辣!
10.3.2启动和停止服务
启动和停止服务主要救助Intent来实现。
修改布局文件中的代码,添加两个Button。
单击事件代码:

@Override
public void onClick(View v){
    switch(v.getId()){
        case R.id.strat_service:
            Intent startIntent = new Intent(MainActivity.this,MyService.class);
            startService(startIntent);
            break;
        case R.id.stop_service:
            Intent startIntent = new Intent(MainActivity.this,MyService.class);
            startService(startIntent);
            break;
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
}

使用startService()方法来启动,stopService()方法来停止服务。startService()和stopService()定义在Context类中,因此活动里可以直接调用。
注意,这里完全由活动来决定服务何时停止,如果没有点击StopService按钮,服务会一直处于运行状态。而只需要在MyService的任何一个位置调用stopSelf()方法就能使服务停止。
验证服务是否成功启动或停止。加入打印日志。
10.3.3活动和服务进行通信
服务启动后一直处于运行状态,但具体运行的逻辑就与活动无关啦。活动若要指挥服务,可以使用onBind()方法。
在活动中指挥服务,要借助Service的onBind()方法。
for example,希望在MyService里提供一个下载功能,然后在活动中可以决定何时开始下载,以及随时查看下载进度。
修改MyService中的相关代码。
创建DownloadBinder类继承自Binder,在类中写出要实现的方法逻辑,这里写的startDownload和getProgress两个方法。布局中添加两个按钮用于绑定和取消绑定服务。
当一个服务和活动绑定之后就可以调用Binder里面提供的方法了。

  • onServiceConnected():活动与服务成功绑定时调用
  • onServiceDiaconnected();活动与服务解除绑定时调用。

在onServiceConnected()中通过下转型,获得DownloadBinder的实例。有了这个实例,活动参与服务至今的关系就变得十分密切了,可以通过这个实例在活动中调用DownloadBinder中所有public方法。
在MainActivity中单击事件中使用bindService方法绑定服务于活动。
onBind的三个参数

  1. Intent实例
  2. ServiceConnection实例
  3. 标志位,BIND_AUTO_CREATE 表示活动与服务绑定后自动创建服务,onCreate执行单onStartCommand不执行。

任何一个服务在整个应用程序范围内是通用的,可以和任何一个活动绑定,并且绑定完成后,都可以获取到相同的功能性Binder类实例。

10.3服务用法总结

  • 【MainActivity】启动和停止活动 startService()/stopService()方法,传入连接服务与活动的意图
  • 【MyService】继承自Service 重写onCreate()/onStaryCommand()/onDestroy()方法
  • 【MyService】二者间通信
    在MyService中创建实现相应功能的Binder类(继承自Binder),在onBind()方法中返回IBender类型的功能Binder类实例
  • 【MainActivity】准备工作
    重写ServiceConnection()类的onServiceConnected()/onServiceDisconnected
    在中用功能Binder类实例调用相关功能逻辑
  • 【MainActivity】绑定活动与服务
    创建服务意图,使用bindService()/unbindService()方法绑定、解绑服务。
    onBind()三个参数1、意图2、ServiceConnection实例,调用后得到onBind返回的IBinder实例。下转型得到功能Binder类实例,并调用功能类中定义的public的逻辑方法

Service 的 onCreate每次第一次创建服务时执行一次。
Service 的 onStartCommand每次服务启动时执行。可多次执行。
ServiceConnection 的 onServiceConnected在服务与活动绑定时执行一次。

stopService在活动中调用。
stopSelf在Service中调用。

10.4活动的生命周期

startService启动服务,调用后如果服务为创建则onCreate在onStartCommand,若服务已创建则直接onStartCommand
服务启动了会一直处在运行状态,知道stopService或stopSelf被调用。虽然之后每startService一次,onStartCommand都会被调用,但是实际上只存在一个实例。所以不管调用多少次startService,只需调用一下停止方法服务就会停下。
还可以调用bindService,会回调服务中onBind方法。若服务之前还没有被创建,onCreate方法先于onBind方法调用。

这里写图片描述

startService + stopService onDestroy执行
bindService + unbindService onDestroy执行
startService + bindService 需要同时执行 stopService + unbindService,onDestroy才能执行。
根据Android系统的机制,一个服务只要被启动或者被绑定了之后,会一直处于运行状态,必须要以上两种条件同时不满足,服务才能被销毁。

10.5服务的更多技巧

10.5.1使用前台服务
服务几乎在后台运行,系统优先级较低,系统出现内存不足时,就有可能回收掉正在后台运行的服务。如果希望服务一直保持运行状态,不被回收,可以考虑使用前台服务。
二者区别:前台服务会一直有个正在运行的图标,在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息。非常类似通知效果。
代码区别在于服务的onCreate方法。

@Override
public void onCreate() {
    super.onCreate();
    Log.d("MyService", "onCreate executed");
    Toast.makeText(this, "aaa", Toast.LENGTH_SHORT).show();
    Intent intent = new Intent(this,MainActivity.class);
    PendingIntent pi = PendingIntent.getActivity(this,0,intent,0);
    Notification no = new NotificationCompat.Builder(this)
        .setContentTitle("this is content title")
        .setContentText("this is content text")
        .setWhen(System.currentTimeMillis())
        .setSmallIcon(R.mipmap.ic_launcher)
        .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
        .setContentIntent(pi)
        .build();
    startForeground(1,no);
}

10.5.2使用IntentService
在本章一开始我们就知道服务中代码都是默认运行在主线程中的,如果直接在服务中处理耗时操作,很容易出现ANR(Application Not Responding)的情况,
解决方法:在服务中每个具体方法内开启子线程,在子线程中处理耗时逻辑。

public class MyService extends Service{
    ...
    @Override
    public int onStartCommand(Intent intent,int flags,int startId){
        new Thread(new Runnable(){
            @Override
            public void run(){
                //处理具体的逻辑
            }
        }).start();
        return super.onStartCommand(intent,flags,startId);
    }
}

but.这种服务一旦启动,就会一直处于运行状态,必须调用stopService()或者stopSelf()才能使服务停止下来,可以在耗时逻辑最后加上stopSelf().
这很简单,但是总有人忘开线程或调用stopSelf。
于是Android专门提供IntentService类,解决前面提到的情况。
新建类继承自IntentService,提供一个无参的构造函数,必须在其内部调用父类有参构造函数。然后在子类中实现onHandleIntent这个抽象方法,在该方法中处理一些相关逻辑,并且不用担心ANR的问题。
根据IntentService的特性,这个服务在运行结束后应该是会自动停止的(调用onDestroy)。
启动服务同样需要startService方法,传入与服务相关的Intent即可。注意,不要忘了服务需要在AndroidManifest.XML中注册!

10.6服务的最佳实践—-完整版的下载示例

服务的常用功能–下载。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值