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 }
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 }
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>
注意:
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 }
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 }
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 }
最后:服务需要注册,广播接收器需要注册
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>