• $《第一行代码:Android》读书笔记——第9章 服务


    (一)Service简介
      服务适合执行那种不需要和用户交互而且还要长期运行的任务。所有的服务代码都是默认运行在主线程中,需要在服务内部手动添加子线程,在子线程中执行耗时任务。
     
    (二)线程
    1、线程的3种用法:
    (1)继承Thread:
    1 class MyThread extends Thread{
    2         public void run( ){
    3                 //执行耗时操作
    4         }
    5 }
    6 new MyThread( ).start( );

    (2)实现Runnable接口:

    1 class MyRunnable implements Runnable{   
    2         public void run( ){
    3                 //执行耗时操作
    4         }
    5 }
    6 new Thread(new MyRunnable).start( );

    (3)使用匿名类:

    1 new Thread(new Runnable( ){
    2         public void run( ){
    3                 //执行耗时操作
    4         }
    5 }).start( );

    2、Android不允许在子线程中更新UI,只能在主线程中更新。

    (三)异步消息处理
    1、不直接在子线程中进行UI操作,而是在子线程中通过Handler将Message传送给主线程,主线程中的Handler接收这个message,然后进行UI操作,这叫异步消息处理。
    2、异步消息处理四步曲:
    (1)在主线程中创建一个Handler类型的类成员变量对象,并重写handleMessage方法,用于处理相应事件:
     1 private Handler handler = new Handler( ){
     2         public void handleMessage(Message msg){
     3                 switch(msg.what){        //msg的what字段是一个标志字段,整型
     4                         case xxx:
     5                                 //在这里可以进行UI操作
     6                                 textView.setText("Change text succeed!")        //改变textView的字符
     7                                 break;
     8                         default:
     9                                 break;
    10                 }
    11         }
    12 }

    (2)在子线程中需要进行UI操作时(如按钮点击事件中),创建一个Message对象,并通过Handler对象的sendMessage方法将该消息发送出去,比如:

     1 public static final int UPDATE_TEXT = 1;        //修改UI的标志值
     2 ......
     3 @Override
     4 public void onClick(View v){
     5         switch(v.getId( )){
     6                 case R.id.chage_text_btn:
     7                         new Thread(new Runnable( ){
     8                                 @Override
     9                                 public void run( ){
    10                                         Message msg = new Message( );
    11                                         msg.what = UPDATE_TEXT ;
    12                                         handler.sendMessage(msg);
    13                                 }
    14                         }).start( );
    15                         break;
    16                 
    17                 default:
    18                         break;
    19         }
    20 }
    (3)发出的Message进入MessageQueue队列等待处理。
    (4)Looper一直尝试从MessageQueue中取出等待处理的消息,最后分发回handleMessage方法。
    注:Message有一个what字段,可以携带标志识别信息(整型),还有arg1和arg2字段,可以携带整型数据,还有一个obj字段可以带一个Object对象。
    3、异步消息处理机制示意图:

     

    (四)AsyncTask
    1、AsyncTask类是Android对异步消息处理的封装。
    2、使用AsyncTask需要自定义一个类去继承它:
    1 class MyAsyncTask extends AsyncTask<Params, Progress, Result>
    三个泛型的含义:
    (1)Params:如果在执行AsyncTask时需要传递信息给后台,则传入此参数,如果不需要则为Void。
    (2)Progress:如果在后台执行任务过程中,需要在界面上显示进程,则使用这个参数作为进程的单位,一般为Integer。
    (3)Result:后台任务执行完后,如果需要对结果进行返回,则使用这个参数作为返回值的类型,如Boolean。
    3、继承时需要实现的几个方法:
    (1)void onPreExecute( );
    该方法运行在UI线程中,可以进行UI的初始化等操作。
    (2)boolean doInBackground(Void... params);
    该方法的所有代码都在子线程中运行,在该方法中处理耗时任务,需要调用publicProgress(Progress)方法传递进度。
    (3)void onProgressUpdate(Integer... values);
    当在doInBackground中调用publicProgress方法时,会自动调用此方法,在这里进行UI操作。
    (4)void onPostExecute(Boolean result);
    执行收尾工作。
    4、要启用MyAsyncTask,在主线程中这样用:new MyAsyncTask.execute( );
     

    (五)Service

    1、定义Service:

     1 public class MyService extends Service {
     2     @Override
     3     public IBinder onBind(Intent intent) {
     4         return null;
     5     }
     6 
     7     @Override
     8     public void onCreate() {
     9         super.onCreate();
    10         Log.d("MyService", "onCreate executed");
    11     }
    12 
    13     @Override
    14     public int onStartCommand(Intent intent, int flags, int startId) {
    15         Log.d("MyService", "onStartCommand executed");
    16         return super.onStartCommand(intent, flags, startId);
    17     }
    18 
    19     @Override
    20     public void onDestroy() {
    21         super.onDestroy();
    22         Log.d("MyService", "onDestroy executed");
    23     }
    24 }

    2、Service类中有一个抽象方法onBind,还有三个常用方法:

    (1)onCreate( ):会在Service创建时调用。
    (2)onStartCommand( ):会在Service启动时调用。
    (3)onDestory( ):在Service销毁时调用。
    3、Service需要在AndroidManifest.xml中注册。
    1 <application
    2         ... >
    3         <service android:name=".MyService" >
    4         </service>
    5         ...
    6 </application>
    4、启动和停止Service:
    (1)启动:
    1 Intent startIntent = new Intent(this, MyService.class);
    2 startService(startIntent);

    (2)停止:

    1 Intent stopIntent = new Intent(this, MyService.class);
    2 stopService(stopIntent);

    5、Activity和Service通信:

    (1)在Service中安插内线Binder,并在onBind方法中发送这个内线:

     1 public class MyService extends Service {
     2 
     3     private DownloadBinder mBinder = new DownloadBinder();
     4 
     5     class DownloadBinder extends Binder {    //自定义Binder,模拟下载功能
     6         public void startDownload() {
     7             Log.d("MyService", "startDownload executed");
     8         }
     9 
    10         public int getProgress() {
    11             Log.d("MyService", "getProgress executed");
    12             return 0;
    13         }
    14     }
    15     
    16     @Override
    17     public IBinder onBind(Intent intent) {
    18         return mBinder;
    19     }
    20     ...    //其他方法
    21 }

    (2)在Activity中创建内线及ServiceConnection,其中的onServiceConnected方法即可接收内线IBinder并通过向下转型获取Binder:

     1   ...
     2     private MyService.DownloadBinder downloadBinder;
     3     private ServiceConnection connection = new ServiceConnection() {
     4         @Override
     5         public void onServiceConnected(ComponentName name, IBinder service) {
     6             downloadBinder = (MyService.DownloadBinder) service;
     7             downloadBinder.startDownload();
     8             downloadBinder.getProgress();
     9         }
    10 
    11         @Override
    12         public void onServiceDisconnected(ComponentName name) {
    13 
    14         };
    15     };
    16     ...

    (3)在合适的时候(如按钮的点击事件等)绑定Activity与Service:

    1 Intent bindIntent = new Intent(this, MyService.class);
    2 bindService(bindIntent, connection, BIND_AUTO_CREATE);    //BIND_AUTO_CREATE指当绑定后,自动创建Service

    (4)解除绑定:

    1 if(connection != null){
    2     unbindService(connection);
    3 }
    6、Service的生命周期:
    (1)每个Service都只会存在一个实例。
    (2)方法:
    1 startService( )
    2 onCreate( )
    3 onStartCommand( )
    4 onDestory( )
    5 bindService( )
    6 unbindService( )
    7 stopService( )
    8 stopSelf( )    //在Service中任何地方都可以用这个方法结束服务本身
    9 onBind( )

    (3)如果startService( )和bindService( )都调用了,那么必须同时满足unbindService( )和stopService( )都被调用才会执行onDestory( )方法。

    7、前台Service:在系统状态栏中一直显示的可见Service,只需在Service的onCreate方法中添加如下代码即可(其实是通知的用法):

     1   @Override
     2     public void onCreate() {
     3         super.onCreate();
     4         ...
     5         Notification notification = new Notification(R.drawable.ic_launcher,
     6                 "Notification comes", System.currentTimeMillis());
     7         Intent notificationIntent = new Intent(this, MainActivity.class);
     8         PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
     9                 notificationIntent, 0);
    10         notification.setLatestEventInfo(this, "This is title",
    11                 "This is content", pendingIntent);
    12         startForeground(1, notification);
    13     }

    8、IntentService:

      服务的代码默认都是在主线程中的,如果直接在服务里去处理一些耗时逻辑,就很容易出现ANR(Application Not Responding)的情况。这时就可以方便的使用已经把多线程封装好的IntentService类:

     1 public class MyIntentService extends IntentService {
     2 
     3     public MyIntentService() {        //需要一个无参的构造方法,调用父类的有参构造方法
     4         super("MyIntentService");
     5     }
     6 
     7     @Override
     8     protected void onHandleIntent(Intent intent) {    //这个方法默认在子线程中运行
     9         // 打印当前线程ID
    10         Log.d("MyIntentService", "Thread id is "
    11                 + Thread.currentThread().getId());
    12 
    13         // 在这里已经是在子线程中运行了,可以执行一些耗时操作,但不能执行UI操作
    14         try {
    15             Thread.sleep(1000);
    16         } catch (Exception e) {
    17             e.printStackTrace();
    18         }
    19     }
    20 
    21     @Override
    22     public void onDestroy() {
    23         super.onDestroy();
    24         Log.d("MyIntentService", "onDestroy executed");
    25     }
    26 }

      IntentService在任务处理完后,会自动调用onDestory方法,不用再去人工调用unbindService或stopService方法。其他用法和普通Service一样。

     

     (六)Service最佳实践:后台定时任务
    1、Android中的定时任务实现方式有两种:Java API的Timer类和Android的Alarm机制。前者会受CPU休眠的影响,后者会唤醒CPU。
    2、首先创建一个LongRunningService服务,重写其onStartCommand方法,在这里面执行定时任务:
     1 import java.util.Date;
     2 
     3 import android.app.AlarmManager;
     4 import android.app.PendingIntent;
     5 import android.app.Service;
     6 import android.content.Intent;
     7 import android.os.IBinder;
     8 import android.os.SystemClock;
     9 import android.util.Log;
    10 
    11 public class LongRunningService extends Service {
    12 
    13     @Override
    14     public IBinder onBind(Intent intent) {
    15         return null;
    16     }
    17 
    18     @Override
    19     public int onStartCommand(Intent intent, int flags, int startId) {
    20         //创建子线程打印当前时间,模拟定时任务
    21         new Thread(new Runnable() {
    22             @Override
    23             public void run() {
    24                 Log.d("LongRunningService",
    25                         "executed at " + new Date().toString());
    26             }
    27         }).start();
    28         //1.创建AlarmManager 
    29         AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
    30         int aMinute = 60 * 1000; // 一分钟的毫秒数
    31         long triggerAtTime = SystemClock.elapsedRealtime() + aMinute;
    32         //2.创建跳到广播接收器的Intent
    33         Intent i = new Intent(this, AlarmReceiver.class);
    34         //3.创建PendingIntent 
    35         PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
    36         //4.使用AlarmManager的set方法
    37         //第一个参数:指定工作类型,有四种:ELAPSED_REALTIME_WAKEUP表示定时任务触发时间从
    38         //系统开机算起,会唤醒CPU;ELAPSED_REALTIME,同ELAPSED_REALTIME_WAKEUP,但不会唤醒CPU;
    39         //RTC表示从1970-1-1 00:00算起,不会唤醒CPU,RTC_WAKEUP同RTC,但会唤醒CPU。
    40         //注:唤醒CPU和唤醒屏幕是不同的概念。
    41         manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
    42         
    43         return super.onStartCommand(intent, flags, startId);
    44     }
    45 }

    3、创建AlarmReceiver类:

     1 import android.content.BroadcastReceiver;
     2 import android.content.Context;
     3 import android.content.Intent;
     4 
     5 public class AlarmReceiver extends BroadcastReceiver {
     6 
     7     @Override
     8     public void onReceive(Context context, Intent intent) {
     9         Intent i = new Intent(context, LongRunningService.class);
    10         context.startService(i);    //反过来再启动服务,交替循环进行下去
    11     }
    12 }

    4、在活动中启动服务:

     1 import android.app.Activity;
     2 import android.content.Intent;
     3 import android.os.Bundle;
     4  
     5 public class MainActivity extends Activity {
     6  
     7     @Override
     8     protected void onCreate(Bundle savedInstanceState) {
     9         super.onCreate(savedInstanceState);
    10         setContentView(R.layout.activity_main);
    11  
    12         Intent i = new Intent(this, LongRunningService.class);
    13         startService(i);
    14     }
    15 }

    5、在AndroidManifest.xml中注册服务和广播接收器。

    6、分析:刚刚创建的这个定时任务,会每隔一分钟执行一次。

     【本章结束】

  • 相关阅读:
    USART串口通信实验
    EXTI 外部中断
    NVIC中断优先级管理
    实验1 跑马灯实验
    redis集群部署---一台主机
    zookeeper服务启动报错---Error contacting service. It is probably not running.
    shell脚本学习笔记
    最短路径算法——Floyd算法
    一篇文章学懂Shell脚本(摘抄)
    VIM空格和TAB转换
  • 原文地址:https://www.cnblogs.com/jiayongji/p/5310395.html
Copyright © 2020-2023  润新知