09. Android基础——服务

定义

  1. 服务(Service)
    1. Android中实现程序后台运行的解决方案
    2. 通常用于不需要和用户交互,但是又要长期运行的程序
    3. 服务的运行不依赖于用户界面
    4. 服务不是运行在一个独立的进程中的,而是依赖于创建服务时所在的程序的进程,当它所属的程序的进程被终止,这个服务也就停止了
    5. 服务不会自动开启线程,所有的代码默认运行在主线程中,为了避免线程阻塞,执行具体任务的子线程需要手动创建

Android中的多线程

基本用法

  1. 例子

    • 使用继承的方法来创建线程和启动线程

      // 新建一个类,继承Thread类来定义线程
      class MyThread extends Thread{
          @Override
          public void run(){
              
          }
      }
      
      // 启动线程
      MyThread myThread = new MyThread();
      myThread.start();
      
    • 使用实现接口的方法来创建线程和启动线程

      // 新建一个类,实现Runnable接口来定义线程
      class MyThread implements Runnable{
          @Override
          public void run(){
              
          }
      }
      
      
      // 启动线程
      MyThread myThread = new MyThread();
      new Thread(myThread).start();
      
    • 使用匿名类实现

      new Thread(new Runnable(){
          @Override
          public void run(){
              
          }
      }).start();
      

异步消息处理机制

  1. Android的UI也是线程不安全的,如果需要更新程序汇总的UI元素,必须在主线程中进行,否则程序会崩溃
  2. 子线程中通常会执行一些耗时任务,Android提供了一套异步消息处理机制,从而可以在子线程中进行UI操作
  3. Android中的异步消息处理主要由4个部分组成
    1. Message
      • 在线程之间传递消息,可以在内部携带少量的信息
      • message.what:用户自定义的消息代码
      • message.arg1:传递的int类型的值1
      • message.arg2:传递的int类型的值2
      • message.obj:传递一个object对象
    2. Handler
      • 用于发送和处理消息
      • 调用Handler中的sendMessage()方法发送消息
      • 消息经过处理之后传递到Handler的handleMessage()方法中
    3. MessageQueue
      • 每个线程中只有一个MessageQueue对象
      • 消息队列,用于存放所有通过Handler发送的消息
      • 这些消息会一直存放在队列中,直到被处理
    4. Looper
      • 每个线程中只有一个Looper对象
      • 管理MessageQueue
      • Looper的loop()方法会让程序进行循环中
      • 然后每当MessageQueue中存在一条消息,就将消息取出来
      • 将消息传递给handleMessage()方法
  4. 异步消息处理流程
    1. 在主线程中创建Handler对象,并重写handleMessage()方法
    2. 当子线程中需要进行某些操作的时候(比如修改UI),创建一个Message对象,通过Handler将这个消息发出去
    3. 这个消息会被添加到MessageQueue队列中等待处理,同时Looper会一直尝试从MessageQueue中取出等待处理的消息
    4. 消息被处理之后会发还到Handler中的handleMessage()方法。注意,因为Handler在主线程中创建,所以handleMessage()方法在主线程中运行

AsyncTask

  1. AsyncTask的原理也是异步消息处理机制,但是Android对它进行了封装

  2. AsyncTask是一个抽象类,需要创建一个子类去继承它,可以指定三个泛型参数

    1. Params:执行AsyncTask时传入的参数,用于在后台任务中使用

    2. Progress:后台执行任务的时候,如果需要在界面上显示当前的进度,就使用它来作为进度单位

    3. Result:当任务执行完毕之后,如果需要对结果进行返回,就使用它作为返回值的类型

    4. 例子

      class MyTask extends AsyncTask<Void,Integer,Boolean>{}
      
      • 第一个泛型参数指定为Viod,表示不需要传入参数给后台任务
      • 第二个泛型参数指定为Integer,表示使用整型数据作为进度显示的单位
      • 第三个泛型参数指定为Boolean,表示使用布尔型数据来反馈执行结果
  3. 继承AsyncTask之后需要重写的方法

    1. onPreExecute()

      • 在后台任务开始执行之前调用
      • 用于执行一些界面上的初始化操作
    2. doInBackground(Params)

      • 这个方法所有的代码都在子线程中运行,在这里处理耗时的任务,任务完成之后通过return语句将任务执行的结果返回
      • 如果AsyncTask第三个泛型参数指定为Void,就不需要返回任务执行结果
    3. onProgressUpdate(Progress)

      • 在这个方法中可以对UI进行操作,利用参数中的数值可以对界面元素进行相应的更新
      • 参数是后台任务中传递过来的
    4. onPostExecute(Result)

      • 后台任务执行完毕,且通过return语句进行返回时,调用这个方法
      • 返回的数据会作为参数传递到这个方法中,可以利用返回的数据进行UI操作
    5. 例子

      public class MyTask extends AsyncTask<Void,Integer,Boolean> {
          @Override
          protected void onPreExecute(){
              progressDialog.show();
          }
      
          @Override
          protected Boolean doInBackground(Void...params){
              // 在这里执行具体的下载任务,这个方法中的代码都在子线程中运行
              try{
                  while (true){
                      // doDownload()用于计算当前的下载进度并返回
                      int downloadPercent = doDownload();
                      
                      // 调用publishProgress()方法并将当前的下载进度传进来
                      publishProgress(downloadPercent);
                      if (downloadPercent >= 100){
                          break;
                      }
                  }
              } catch (Exception e){
                  e.printStackTrace();
              }
              return true;
          }
      
          @Override
          protected  void onProgressUpdate(Integer...values){
              progressDialog.setMessage("下载进度+"+values[0]+"%");
          }
      
          @Override
          protected void onPostExecute(Boolean result){
              progressDialog.dismiss();// 关闭对话框
              if (result){
                  Toast.makeText(context, "下载完成", Toast.LENGTH_SHORT).show();
              } else {
                  Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
              }
          }
      }
      
      // 启动任务
      new MyTask().execute();
      
  4. 总结

    1. 在doInBackground()方法中执行具体的耗时的任务(比如下载)
    2. 在onProgressUpdate()方法中进行UI操作
    3. 在onPostExecute()方法中执行收尾工作

服务的基本用法

定义一个服务

  1. 例子

    public class MyService extends Service {
        public MyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            throw new UnsupportedOperationException("Not yet implemented");
        }
    
    
        @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();
        }
    }
    
  2. 需要在AndroidManifest.xml中注册

    <service
                android:name=".MyService"
                android:enabled="true"
                android:exported="true">
                
            </service>
    

启动服务与停止服务

  1. 借助Intent来启动和停止服务

  2. 例子

    1. 设置两个Button按钮来启动服务和停止服务

      public class MainActivity extends AppCompatActivity implements View.OnClickListener {
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              Button startService = (Button) findViewById(R.id.start_service);
              Button stopService = (Button) findViewById(R.id.stop_service);
      
              startService.setOnClickListener(this);
              stopService.setOnClickListener(this);
          }
      
          @Override
          public void onClick(View v) {
              switch (v.getId()) {
                  case R.id.start_service:
                      Intent startIntent = new Intent(this, MyService.class);
                      startService(startIntent);
                      break;
                  case R.id.stop_service:
                      Intent stopIntent = new Intent(this, MyService.class);
                      stopService(stopIntent);
                      break;
                  default:
                      break;
              }
          }
      }
      
    2. 自定义MyService

      public class MyService extends Service {
          public MyService() {
          }
      
          @Override
          public IBinder onBind(Intent intent) {
              throw new UnsupportedOperationException("Not yet implemented");
          }
      
      
          @Override
          public void onCreate(){
              super.onCreate();
              Log.d("MyService","onCreate方法执行了");
          }
          @Override
          public int onStartCommand(Intent intent,int flags,int startId){
      
              Log.d("MyService","onStartCommand方法执行了");
              return super.onStartCommand(intent,flags,startId);
          }
      
          @Override
          public void onDestroy(){
              super.onDestroy();
              Log.d("MyService","onDestroy方法执行了");
          }
      }
      

活动和服务进行通信

  1. 例子

    public class MyService extends Service {
        
        private DownloadBinder mBinder = new DownloadBinder();
        class DownloadBinder extends Binder{
            public void startDownload(){
                Log.d("MyService","开始下载");
            }
            
            public int getProgress(){
                Log.d("MyService","获取进度的方法执行了");
                return 0;
            }
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
        public MyService() {
        }
    }
    
    • 新建了一个DownloadBinder类,这个类继承Binder
    • 这个类实现了两个方法,开始下载和查看下载进度
    • 最后在onBind()方法中返回这个实例
  2. 当一个活动和服务绑定了之后,就可以使用这个服务中Binder提供的方法了

    1. 例子

      public class MainActivity extends AppCompatActivity implements View.OnClickListener {
      
          private MyService.DownloadBinder downloadBinder;
          
          // 创建ServiceConnection的匿名类,重写了两个方法
          private ServiceConnection connection = new ServiceConnection() {
              
              @Override
              // 在活动与服务绑定的时候调用
              public void onServiceConnected(ComponentName name, IBinder service) {
                  // 向下转型得到DownloadBinder的实例
                  downloadBinder  = (MyService.DownloadBinder) service;
                  downloadBinder.startDownload();
                  downloadBinder.getProgress();
              }
      
              @Override
              // 在活动与服务解绑的时候调用
              public void onServiceDisconnected(ComponentName name) {
      
              }
          };
          
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
      
              Button bindService = (Button) findViewById(R.id.bind_service);
              Button unbindService = (Button) findViewById(R.id.unbind_service);
              
              bindService.setOnClickListener(this);
              unbindService.setOnClickListener(this);
      
          }
      
          @Override
          public void onClick(View v) {
              switch (v.getId()) {
                  case R.id.bind_service:
                      Intent bindIntent = new Intent(this,MyService.class);
                      // 使用bindService()方法将MainActivity和Myservice绑定
                      // 参数1:Intent对象,
                      // 参数2:ServiceConnection的实例
                      // 参数3:标志位,这里的BIND_AUTO_CREATE表示活动和服务绑定后自动创建服务
                      bindService(bindIntent,connection,BIND_AUTO_CREATE);
                      break;
                  case R.id.unbind_service:
                      // 解除绑定
                      unbindService(connection);
                  default:
                      break;
              }
          }
      }
      
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容