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

 

posted @ 2015-11-06 12:15  洱海  阅读(243)  评论(0编辑  收藏  举报
.First { margin: 10px 0; font-family: 'Microsoft Yahei'; text-align: left; padding: 6px 20px; color: #fff; background: #55895B; font-size: 20px; border-radius: 4px; clear: both; } .Second { margin: 10px 0; font-family: 'Microsoft Yahei'; padding: 6px 20px; background: #93C8A2; color: white; font-size: 18px; border-radius: 4px; clear: both; } .Third { margin: 10px 0; padding: 6px 20px; font-family: 'Microsoft Yahei'; margin: 15px 0; font-size: 16px; color: black; background: #C6EFD2; border-radius: 4px; clear: both; } .note { margin: 10px 0; padding: 15px 20px 15px 60px; background: #FCFAA9 url('http://images.cnblogs.com/cnblogs_com/libaoheng/305804/o_yellow-pin.png') no-repeat 20px 0; font-size: 15px; font-family: 'Microsoft Yahei'; box-shadow: 0 0 8px #aaa; clear: both; } .demo { text-align: left; padding: 6px 20px; overflow: auto; border-radius: 4px; background: orange; color: #fff; font-size: 16px; clear: both; } .cnblogs_Highlighter { border: solid 1px #ccc; clear: both; } .cnblogs_code { background: #EFFFF4; border: solid 0px #939393; font-size: 14px; clear: both; padding: 10px 20px; } .cnblogs_code pre { font-size: 14px; } .cnblogs_code span { font-family: Courier New; font-size: 14px; }