zoukankan      html  css  js  c++  java
  • Android——服务

    服务默默的在后台工作着,执行着不需要和用户交互的工作。

    服务依赖于应用程序进程而存活

    作为四大组件之一,服务具备共同的特点——需要在AndroidManifest中注册

    Android多线程编程

     需要注意的是——一定不要在子线程中进行UI操作,否则会阻塞主线程出现异常

     1     /** 主要的逻辑是在这里完成,但是考虑到:服务默认是在主线程执行的,如果在这里进行比较费时的操作<br/>
     2      * 就容易出现ANR(Application Not Responding).<br/>
     3      * 所以标准的写法是在这里新建一个子线程执行逻辑处理<br/>
     4      * 但是:这样做了之后,必须要使用stopService或stopSelf才能停得下来
     5      */
     6     @Override
     7     public int onStartCommand(Intent intent, int flags, int startId) {
     8         Log.d("test", "service onStartCommand");
     9         new Thread(new Runnable() {
    10             
    11             @Override
    12             public void run() {
    13                 // 逻辑处理——不能进行UI更新
    14                 // ...
    15                 // 这里处理完之后调用停止服务
    16                 stopSelf();
    17                 
    18             }
    19         }).start();
    20         return super.onStartCommand(intent, flags, startId);
    21     }

    通过异步消息处理机制进行UI操作

    在主线程定义一个handler

        public static final int SHOW_RESPONSE = 0;
        private Handler handler = new Handler() {
    
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case SHOW_RESPONSE:
                    String response = (String) msg.obj;
                    // 进行UI更新:根据子线程换回来的msg.obj的值和msg.what的值break;
    
                default:
                    break;
                }
            }
    
        };

    子线程通过handler发送数据

            new Thread(new Runnable() {
    
                @Override
                public void run() {try {
    // 将获取到的数据交给Handler处理
                        Message msg = new Message();
                        msg.what = SHOW_RESPONSE;
                        msg.obj = 对象; 
                        handler.sendMessage(msg);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        }
                    }
    
                }
            }).start();

    1.在主线程中创建Handler(处理者)对象,重写handMessage()方法,在该方法中进行UI操作

    2.当子线程需要UI更新时,new一个Message(消息)对象,通过handler发送这个消息,包括.what,.obj等

    3.消息会被发送到消息队列中,Looper(消息队列管家)会循环从队列中尝试取出消息,并转给handler

    4.handler收到消息后,就会调用handMessage方法,进行UI更新操作

    使用AsyncTask

    主要方法

    1.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

    2.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。

    3.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。

    4.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

    后续待看

    在doInBackground中进行耗时逻辑操作,在onProgressUpdate中进行UI操作

    服务的基本用法

    定义服务

    public class MyService extends Service {
    
        @Override
        public IBinder onBind(Intent intent) {
            // 当活动与服务绑定的时候,这个方法就会被执行
            return null;
        }
    
        @Override
        public void onCreate() {
            // 主要初始化
            super.onCreate();
            Log.d("test", "service onCreate");
        }
    
        /** 主要的逻辑是在这里完成,但是考虑到:服务默认是在主线程执行的,如果在这里进行比较费时的操作,
         * 就容易出现ANR(Application Not Responding).<br/>
         * 所以标准的写法是在这里新建一个子线程执行逻辑处理<br/>
         * 但是:这样做了之后,必须要使用stopService或stopSelf才能停得下来
         */
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d("test", "service onStartCommand");
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    // 逻辑处理
                    // ...
                    // 处理完之后调用停止服务
                    stopSelf(); 
                }
            }).start();
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            // 摧毁前调用
            super.onDestroy();
            Log.d("test", "service onDestroy"); 
        }
        
    
    }

    开启服务——和活动很相似!!

                intent = new Intent(MainActivity.this, MyService.class);
                // 启动服务
                startService(intent);

    停止服务

                intent = new Intent(MainActivity.this, MyService.class);
                // 暂停服务,也可用stopSelf()
                stopService(intent);

    注册服务——在application中

    <service android:name="com.example.servicetest.MyService"></service>

    活动与服务的通信——Binder

    通常要做到:活动要让服务干活了,说一下立马行动;活动要知道服务的活干的怎么样了,服务要能立马汇报

    1.在服务中定义一个Binder对象,该对象具有干活的方法

    2.在onBind方法中,将改对象返回

    3.在活动中也定义一个该Binder对象,用于接收onBind返回的对象

    4.活动中定义一个ServiceConnection对象,重写onServiceConnected,onServiceDisconnected方法,并在onServiceConnected中为Binder对象赋值

    5.调用bindService(intent, conn, BIND_AUTO_CREATE)绑定,绑定之后,就可以通过binder对象干活了

      1 package com.example.servicetest;
      2 
      3 import com.example.servicetest.MyService.DownloadBinder;
      4 
      5 import android.app.Activity;
      6 import android.content.ComponentName;
      7 import android.content.Intent;
      8 import android.content.ServiceConnection;
      9 import android.os.Bundle;
     10 import android.os.IBinder;
     11 import android.util.Log;
     12 import android.view.View;
     13 import android.view.View.OnClickListener;
     14 import android.widget.Button;
     15 import android.widget.Toast;
     16 
     17 
     18 public class MainActivity extends Activity implements OnClickListener{
     19 
     20     private Button startButton;
     21     private Button stoptButton;
     22     private Button bindButton;
     23     private Button unbindButton;
     24     private Button workButton;
     25     private Button progressButton;
     26     private Button intentServiceButton;
     27     private MyService.DownloadBinder downloadBinder = null;
     28     private  ServiceConnection conn = new ServiceConnection() {
     29         
     30         @Override
     31         public void onServiceDisconnected(ComponentName name) {
     32             // 连接意外丢失时
     33             
     34         }
     35         
     36         @Override
     37         public void onServiceConnected(ComponentName name, IBinder service) {
     38             // 连接成功时
     39             downloadBinder = (DownloadBinder) service;
     40             
     41         }
     42     };
     43     
     44     @Override
     45     protected void onCreate(Bundle savedInstanceState) {
     46         super.onCreate(savedInstanceState);
     47         setContentView(R.layout.activity_main);
     48         
     49         startButton = (Button) findViewById(R.id.start);
     50         stoptButton = (Button) findViewById(R.id.stop);
     51         bindButton = (Button) findViewById(R.id.bind);
     52         unbindButton = (Button) findViewById(R.id.unbind);
     53         workButton = (Button) findViewById(R.id.start_work);
     54         progressButton = (Button) findViewById(R.id.re_progress);
     55         intentServiceButton = (Button) findViewById(R.id.start_intent_service);
     56         startButton.setOnClickListener(this);
     57         stoptButton.setOnClickListener(this);
     58         bindButton.setOnClickListener(this);
     59         unbindButton.setOnClickListener(this);
     60         workButton.setOnClickListener(this);
     61         progressButton.setOnClickListener(this);
     62         intentServiceButton.setOnClickListener(this);
     63         
     64     }
     65 
     66     @Override
     67     public void onClick(View v) {
     68         Intent intent;
     69         switch (v.getId()) {
     70         case R.id.start:
     71             intent = new Intent(this, MyService.class);
     72             // 启动服务
     73             startService(intent);
     74             break;
     75         case R.id.stop:
     76             intent = new Intent(this, MyService.class);
     77             // 暂停服务
     78             stopService(intent);
     79             break;
     80         case R.id.bind:
     81             intent = new Intent(this, MyService.class);
     82             // 绑定服务——BIND—_AUTO_CREATE标志位,表示活动与服务绑定后自动创建服务,调用服务的onCreate方法,但不调用onStartCommand
     83             bindService(intent, conn, BIND_AUTO_CREATE);
     84             break;
     85         case R.id.unbind:
     86             // 解除绑定服务
     87             unbindService(conn);
     88             break;
     89         case R.id.start_work:
     90             // 让服务开始干活——这里的判断条件还不明白
     91             if(downloadBinder!=null){
     92                 downloadBinder.startDownload();
     93             }else {
     94                 Toast.makeText(this, "ensure binded?", Toast.LENGTH_SHORT).show();
     95             }
     96             break;
     97         case R.id.re_progress:
     98             // 让服务汇报进度
     99             if(downloadBinder!=null){
    100                 downloadBinder.getProgress();
    101             }else {
    102                 Toast.makeText(this, "ensure binded?", Toast.LENGTH_SHORT).show();
    103             }
    104             break;
    105         case R.id.start_intent_service:
    106             // 让intentservice服务工作
    107             Log.d("test", "MainActivity Thread name:"+Thread.currentThread().getName());
    108             Intent intent2 =new Intent(this, MyIntentService.class);
    109             startService(intent2);
    110             break;
    111 
    112         default:
    113             break;
    114         }
    115         
    116     }
    117 
    118 }
    MainActivity
     1 package com.example.servicetest;
     2 
     3 
     4 import android.app.Notification;
     5 import android.app.PendingIntent;
     6 import android.app.Service;
     7 import android.content.Intent;
     8 import android.os.Binder;
     9 import android.os.IBinder;
    10 import android.support.v4.app.NotificationCompat;
    11 import android.support.v4.app.NotificationCompat.Builder;
    12 import android.util.Log;
    13 
    14 
    15 public class MyService extends Service {
    16     private DownloadBinder mBinder = new DownloadBinder();
    17     class DownloadBinder extends Binder{
    18         public void startDownload(){
    19             Log.d("test", "startDownload is work  by DownloadBinder");
    20             
    21         }
    22         public int getProgress(){
    23             Log.d("test", "getProgress is work  by DownloadBinder");
    24             return 0;
    25         }
    26     }
    27     @Override
    28     public IBinder onBind(Intent intent) {
    29         // TODO Auto-generated method stub
    30         return mBinder;
    31     }
    32 
    33     @Override
    34     public void onCreate() {
    35         // 主要初始化
    36         // 这里创建一个前台服务:后台服务可能会因系统内存不足而回收,如果希望一直运行,可以考虑前台服务,类似通知
    37         super.onCreate();
    38         NotificationCompat.Builder mbuilder = new NotificationCompat.Builder(this);
    39         mbuilder
    40         .setTicker("this is ticker")
    41         .setWhen(System.currentTimeMillis())
    42         .setContentTitle("this is title")
    43         .setContentText("this is content")
    44         .setSmallIcon(R.drawable.ic_launcher)
    45         .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0));
    46         
    47         Notification notification = mbuilder.build();
    48         startForeground(1, notification);// 将MyService变成一个前台服务!!
    49         Log.d("test", "service onCreate");
    50     }
    51 
    52     /** 主要的逻辑是在这里完成,但是考虑到:服务默认是在主线程执行的,如果在这里进行比较费时的操作,
    53      * 就容易出现ANR(Application Not Responding).<br/>
    54      * 所以标准的写法是在这里新建一个子线程执行逻辑处理<br/>
    55      * 但是:这样做了之后,必须要使用stopService或stopSelf才能停得下来
    56      */
    57     @Override
    58     public int onStartCommand(Intent intent, int flags, int startId) {
    59         Log.d("test", "service onStartCommand");
    60         new Thread(new Runnable() {
    61             
    62             @Override
    63             public void run() {
    64                 // 逻辑处理
    65                 // ...
    66                 // 处理完之后调用停止服务
    67                 stopSelf();
    68                 // 但是如果再这里调用了停止服务的话,新的服务请求将得不到执行,因为服务将会停止
    69                 
    70             }
    71         }).start();
    72         return super.onStartCommand(intent, flags, startId);
    73     }
    74 
    75     @Override
    76     public void onDestroy() {
    77         // 摧毁前调用
    78         super.onDestroy();
    79         Log.d("test", "service onDestroy");
    80         
    81     }
    82     
    83 
    84 }
    MyService.java
     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     android:layout_width="match_parent"
     3     android:layout_height="match_parent"
     4     android:orientation="vertical" >
     5 
     6     <Button
     7         android:id="@+id/start"
     8         android:layout_width="match_parent"
     9         android:layout_height="wrap_content"
    10         android:text="start service" />
    11 
    12     <Button
    13         android:id="@+id/stop"
    14         android:layout_width="match_parent"
    15         android:layout_height="wrap_content"
    16         android:text="stop service" />
    17 
    18     <Button
    19         android:id="@+id/bind"
    20         android:layout_width="match_parent"
    21         android:layout_height="wrap_content"
    22         android:text="Bind service" />
    23 
    24     <Button
    25         android:id="@+id/unbind"
    26         android:layout_width="match_parent"
    27         android:layout_height="wrap_content"
    28         android:text="Unbind service" />
    29 
    30     <Button
    31         android:id="@+id/start_work"
    32         android:layout_width="match_parent"
    33         android:layout_height="wrap_content"
    34         android:text="let service do work!" />
    35 
    36     <Button
    37         android:id="@+id/re_progress"
    38         android:layout_width="match_parent"
    39         android:layout_height="wrap_content"
    40         android:text="See the progress of service" />
    41 
    42     <Button
    43         android:id="@+id/start_intent_service"
    44         android:layout_width="match_parent"
    45         android:layout_height="wrap_content"
    46         android:text="let IntentService go work" />
    47 
    48 </LinearLayout>
    activity_main.xml

    注意:

    MyService可以和应用程序内的任何活动进行绑定

    服务的生命周期

    当startService(...)时

    onCreate()——只会执行一次,服务若已创建,则不再执行

    onStartCommand()——startService一次就执行一次

    当stopService()或stopSelf()时

    onDestroy()会被执行

    当binService()时

    前台服务

     后台的服务可能会由于系统回收内存而kill掉,使用前台服务则不会,前台服务类似于通知一样,会在状态栏一直显示信息

     改变MyService的onCreate方法为:

     1     @Override
     2     public void onCreate() {
     3         // 主要初始化
     4         // 这里创建一个前台服务:后台服务可能会因系统内存不足而回收,如果希望一直运行,可以考虑前台服务,类似通知
     5         super.onCreate();
     6         NotificationCompat.Builder mbuilder = new NotificationCompat.Builder(this);
     7         mbuilder
     8         .setTicker("this is ticker")
     9         .setWhen(System.currentTimeMillis())
    10         .setContentTitle("this is title")
    11         .setContentText("this is content")
    12         .setSmallIcon(R.drawable.ic_launcher)
    13         .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0));
    14         
    15         Notification notification = mbuilder.build();
    16         startForeground(1, notification);// 将MyService变成一个前台服务!!
    17         Log.d("test", "service onCreate");
    18     }

    可以看到,只需要创建一个Notification对象,然后调用startForeground方法即可!简直不要和通知太像!!

    IntentService

    当需要服务执行一次任务之后就停止服务的时候——可以使用IntentService

    当然,之前开起子线程的方法同样可用,只是需要在执行完之后添加一句stopSelf()

    IntentService只不过是封装得比较好一点,仅此而已

    定义一个服务类继承抽象类IntentService

     1 package com.example.servicetest;
     2 
     3 import android.app.IntentService;
     4 import android.content.Intent;
     5 import android.util.Log;
     6 
     7 public class MyIntentService extends IntentService {
     8 
     9     /**
    10      * 无参构造函数,利用父类的有参构造函数
    11      */
    12     public MyIntentService() {
    13         super("MyIntentService");
    14     }
    15 
    16     /**
    17      * 异步处理逻辑,不用担心ANR的问题
    18      */
    19     @Override
    20     protected void onHandleIntent(Intent intent) {
    21         Log.d("test", "MyIntentService's onHandleIntent is executed");
    22         Log.d("test", "MyIntentService Thread name:"+Thread.currentThread().getName());
    23     }
    24 
    25     /**
    26      * 验证一下,当IntentService执行完毕之后会不会自动执行停止服务的操作
    27      */
    28     @Override
    29     public void onDestroy() {
    30         super.onDestroy();
    31         Log.d("test", "MyIntentService is sure stopped");
    32     }

    开启这个服务,和开启普通服务并没有什么两样

    1         case R.id.start_intent_service:
    2             // 让intentservice服务工作
    3             Log.d("test", "MainActivity Thread name:"+Thread.currentThread().getName());
    4             Intent intent2 =new Intent(this, MyIntentService.class);
    5             startService(intent2);
    6             break;

    最佳实例——后台定时执行任务

          MainActivity——创建时,直接开启服务 

     1 package com.example.servicebesttest;
     2 
     3 import android.app.Activity;
     4 import android.content.Intent;
     5 import android.os.Bundle;
     6 import android.view.Menu;
     7 import android.view.MenuItem;
     8 
     9 
    10 public class MainActivity extends Activity {
    11 
    12     @Override
    13     protected void onCreate(Bundle savedInstanceState) {
    14         super.onCreate(savedInstanceState);
    15         setContentView(R.layout.activity_main);
    16         Intent service = new Intent(this, LongRunningService.class);
    17         startService(service);
    18     }
    19 
    20 }
    MainActivity

      

      LongRunningService——输出当前时间,并通过Alarm机制定时开启广播接收器

     1 package com.example.servicebesttest;
     2 
     3 import java.text.SimpleDateFormat;
     4 import java.util.Date;
     5 
     6 import android.app.AlarmManager;
     7 import android.app.PendingIntent;
     8 import android.app.Service;
     9 import android.content.Context;
    10 import android.content.Intent;
    11 import android.os.IBinder;
    12 import android.os.SystemClock;
    13 import android.util.Log;
    14 
    15 public class LongRunningService extends Service {
    16 
    17     @Override
    18     public IBinder onBind(Intent intent) {
    19         return null;
    20     }
    21 
    22     @Override
    23     public int onStartCommand(Intent intent, int flags, int startId) {
    24         // 输出当前时间
    25         new Thread(new Runnable() {
    26 
    27             @Override
    28             public void run() {
    29                 Log.d("test","executed at:"
    30                                 + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    31             }
    32         }).start();
    33         // 定时打开广播接收器——10s一次
    34         AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    35         /*
    36          * type还可以用RTC,RTC_WAKEUP,对应的触发时间应该使用System.currenTimetMillis()
    37          */
    38         // (int type, long triggerAtMillis, PendingIntent operation)
    39         int type = AlarmManager.ELAPSED_REALTIME_WAKEUP; // 从系统开机时累积的总时间
    40         long triggerAtMillis = SystemClock.elapsedRealtime() + 10 * 1000;
    41         Intent i = new Intent(this, ServiceReceiver.class);
    42         PendingIntent operation = PendingIntent.getBroadcast(this, 0, i, 0);
    43 
    44         // 通过set定时执行可能会延迟,4.4之后,因为手机会有省电设计,如果要准确无误,用setExact()
    45         alarmManager.set(type, triggerAtMillis, operation);
    46 
    47         return super.onStartCommand(intent, flags, startId);
    48     }
    49 
    50     @Override
    51     public void onDestroy() {
    52         super.onDestroy();
    53     }
    54 
    55 }
    LongRunningService.java

      ServiceReceiver——激活就开启服务,从而达到循环执行

     1 package com.example.servicebesttest;
     2 
     3 import android.content.BroadcastReceiver;
     4 import android.content.Context;
     5 import android.content.Intent;
     6 
     7 public class ServiceReceiver extends BroadcastReceiver {
     8 
     9     @Override
    10     public void onReceive(Context context, Intent intent) {
    11         // 被激活则直接开启服务
    12         Intent service = new Intent(context, LongRunningService.class);
    13         context.startService(service);
    14     }
    15 
    16 }
    ServiceReceiver.java

      最后:服务需要注册,广播接收器需要注册

      

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     3     package="com.example.servicebesttest"
     4     android:versionCode="1"
     5     android:versionName="1.0" >
     6 
     7     <uses-sdk
     8         android:minSdkVersion="14"
     9         android:targetSdkVersion="19" />
    10 
    11     <application
    12         android:allowBackup="true"
    13         android:icon="@drawable/ic_launcher"
    14         android:label="@string/app_name"
    15         android:theme="@style/AppTheme" >
    16         <activity
    17             android:name=".MainActivity"
    18             android:label="@string/app_name" >
    19             <intent-filter>
    20                 <action android:name="android.intent.action.MAIN" />
    21 
    22                 <category android:name="android.intent.category.LAUNCHER" />
    23             </intent-filter>
    24         </activity>
    25 
    26         <service android:name="com.example.servicebesttest.LongRunningService" >
    27         </service>
    28 
    29         <receiver android:name="com.example.servicebesttest.ServiceReceiver" >
    30         </receiver>
    31     </application>
    32 
    33 </manifest>
    AndroidManifest.xml
  • 相关阅读:
    15 Django组件-中间件
    Android学习笔记-Dialog详解
    python爬取世界疫情信息到Mysql
    《构建之法》阅读笔记3
    团队项目--校园百晓生
    团队项目--校园百晓生
    php安装配置及问题解决
    《构建之法》阅读笔记2
    云服务器配置(转载)
    第五周总结
  • 原文地址:https://www.cnblogs.com/erhai/p/4942229.html
Copyright © 2011-2022 走看看