Android 4 学习(19):Services
参考《Professional Android 4 Development》
Services
Service是invisible的,因此其优先级不高于visible的Activity,之所以说不高于,是因为我们可以设置Service为在前台运行。
创建Service
Android提供了Service抽象类,继承它便可以创建一个Service类:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service {
@Override
public void onCreate() {
super.onCreate();
// TODO: Actions to perform when service is created.
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Replace with service binding implementation.
return null;
}
}
创建Service类之后,还需要在Manifest里面注册:
<service android:enabled=”true” android:name=”.MyService” android:permission=”com.paad.MY_SERVICE_PERMISSION”/>
若要默认只有自己的程序可以使用这个Service,需要添加Android:permission属性。其他Application若要使用这个服务,需要加入这个permission。
执行Service
当Service由startService()方法调用时,即可引起onStartCommand方法的调用,因此需要重写Service中的onStartCommand方法,并且onStartCommand方法可能会被多次调用。onStartCommand()方法将返回一个int值,用于指定当Service被runtime杀掉又重启的时,系统该如何响应:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startBackgroundTask(intent, startId);
return Service.START_STICKY;
}
重启Service
onStartCommand()方法可以返回这些参数,它们的意义是:
START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
START_NOT_STICKY:“非粘性的”。如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
START_REDELIVER_INTENT:使用这个返回值时,如果未执行完onStartCommand,服务在调用stopSelf之前被kill掉,系统会自动重启该服务,并将Intent的值传入。
参考:http://www.krislq.com/2013/05/android-class-return-value-of-onstartcommand/
启动和停止服务
调用startService方法可以启动服务:
private void explicitStart() {
// Explicitly start My Service
Intent intent = new Intent(this, MyService.class);
// TODO Add extras if required.
startService(intent);
}
private void implicitStart() {
// Implicitly start a music Service
Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
intent.putExtra(MyMusicService.ALBUM_NAME_EXTRA, “United”);
intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA, “Pheonix”);
startService(intent);
}
调用stopService方法可以停止服务:
// Stop a service explicitly.
stopService(new Intent(this, MyService.class));
// Stop a service implicitly.
Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
stopService(intent);
服务自杀
服务内部调用stopSelf方法,可以停止该服务。
绑定Service到Activity
首先,Service要实现IBind接口:
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public class MyBinder extends Binder {
MyMusicService getService() {
return MyMusicService.this;
}
}
private final IBinder binder = new MyBinder();
ServiceConnection类用于表示Service到Activity的绑定,每个绑定都需要创建一个ServiceConnection:
// Reference to the service
private MyMusicService serviceRef;
// Handles the connection between the service and activity
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// Called when the connection is made.
serviceRef = ((MyMusicService.MyBinder)service).getService();
}
public void onServiceDisconnected(ComponentName className) {
// Received when the service unexpectedly disconnects.
serviceRef = null;
}
};
最后,调用bindService方法,传入用于启动Service的Intent,ServiceConnection和标志位:
// Bind to the service
Intent bindIntent = new Intent(MyActivity.this, MyMusicService.class);
bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);
通常情况下,Android应用在自己的内存空间中运行,并不共享内存。若要与其他进程通信,可以使用广播,或在Intent中添加Bundle参数启动其他Service的方法。如果需要更紧密的通信,可以使用Android Interface Defi nition Language(AIDL)。AIDL使用OS级别的简单变量定义了接口,可以跨应用传递对象。
创建前台服务
使用startForeground方法启动服务,可以使服务获得与Visible Activity相同的优先级,例如:
private void startPlayback(String album, String artist) {
int NOTIFICATION_ID = 1;
// Create an Intent that will open the main Activity if the notification is clicked.
Intent intent = new Intent(this, MyActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
// Set the Notification UI parameters
Notification notification = new Notification(R.drawable.icon, “Starting Playback”, System.currentTimeMillis());
notification.setLatestEventInfo(this, album, artist, pi);
// Set the Notification as ongoing
notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
// Move the Service to the Foreground
startForeground(NOTIFICATION_ID, notification);
}
使用stopForeground方法可以取消Service的前台属性:
public void pausePlayback() {
// Move to the background and remove the Notification
stopForeground(true);
}
使用后台线程
Service和Activity一样,也是在主线程中运行的,为了更好地响应用户,我们需要使用后台线程的方式执行Service。Android提供了两个抽象类来帮助我们实现:AsyncTask和IntentService。
使用AsyncTask
AsyncTask不仅能帮助我们将费时操作放到后台执行,还可以实现和UI线程的同步。AsyncTask适合执行那些耗时比较短并且和UI线程有交互的任务,对于耗时较久的任务(例如下载),使用Service更为合适。需要注意的是AsyncTask在应用restart之后会被cancel掉。
private class MyAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected String doInBackground(String... parameter) {
// Moved to a background thread.
String result = “”;
int myProgress = 0;
int inputLength = parameter[0].length();
// Perform background processing task, update myProgress]
for (int i = 1; i <= inputLength; i++) {
myProgress = i;
result = result + parameter[0].charAt(inputLength-i);
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
publishProgress(myProgress);
}
// Return the value to be passed to onPostExecute
return result;
}
@Override
protected void onProgressUpdate(Integer... progress) {
// Synchronized to UI thread.
// Update progress bar, Notification, or other UI elements
asyncProgress.setProgress(progress[0]);
}
@Override
protected void onPostExecute(String result) {
// Synchronized to UI thread.
// Report results via UI update, Dialog, or notifications
asyncTextView.setText(result);
}
}
使用AsyncTask,首先要创建一个AsyncTask的子类类并实现doInBackground,onProgressUpdate,onPostExecute方法。这三个方法的说明:
- doInBackground: 这个方法用于执行需要在后台线程中长期运行的操作,可以通过publishProgress方法传递参数给onProgressUpdate;在这个方法执行完毕之后,此方法将返回值传递给onProgressUpdate。
- onProgressUpdate: 接收publishProgress方法传入的参数,更新UI。
- onPostExecute: doInBackground执行结束后,将返回值传入此方法。
执行AsyncTask
String input = “redrum ... redrum”;
new MyAsyncTask().execute(input);
Intent Service简介
IntentService可以通过传入Intent参数调用,传入的Intent将进入一个队列中被异步执行。IntentService封装了消息的异步处理,后台线程创建以及与UI线程的同步。继承IntentService类并实现onHandleIntent方法,即可创建一个Intent Service:
import android.app.IntentService;
import android.content.Intent;
public class MyIntentService extends IntentService {
public MyIntentService(String name) {
super(name);
// TODO Complete any required constructor tasks.
}
@Override
public void onCreate() {
super.onCreate();
// TODO: Actions to perform when service is created.
}
@Override
protected void onHandleIntent(Intent intent) {
// This handler occurs on a background thread. TODO The time consuming task should be implemented here.
// Each Intent supplied to this IntentService will be processed consecutively here. When all incoming Intents have been processed the Service will terminate itself.
}
}
Loader简介
Loader是一个抽象类,封装了异步加载数据的最佳实践,最典型的就是CursorLoader了。Android中创建Loader类的简单方法是继承AsyncTaskLoader类,并实现这两个功能:
- 异步加载数据
- 监测数据源的变化并及时更新
手动创建线程及GUI线程同步
尽管AsyncTask和Intent Service提供了简单易用的异步类封装,但我们也可以创建自定义的异步线程:
// This method is called on the main GUI thread.
private void backgroundExecution() {
// This moves the time consuming operation to a child thread.
Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);
thread.start();
}
// Runnable that executes the background processing method.
private Runnable doBackgroundThreadProcessing = new Runnable() {
public void run() {
backgroundThreadProcessing();
}
};
// Method which does some processing in the background.
private void backgroundThreadProcessing() {
// [ ... Time consuming operations ... ]
}
与GUI线程同步:
runOnUiThread方法会在UI线程执行:
runOnUiThread(new Runnable() {
public void run() {
// Update a View or other Activity UI element.
}
});
此外,可以使用Handler类更新UI线程:
//This method is called on the main GUI thread.
private void backgroundExecution() {
// This moves the time consuming operation to a child thread.
Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);
thread.start();
}
// Runnable that executes the background processing method.
private Runnable doBackgroundThreadProcessing = new Runnable() {
public void run() {
backgroundThreadProcessing();
}
};
// Method which does some processing in the background.
private void backgroundThreadProcessing() {
// [ ... Time consuming operations ... ]
// Use the Handler to post the doUpdateGUI
// runnable on the main UI thread.
handler.post(doUpdateGUI);
}
//Initialize a handler on the main thread.
private Handler handler = new Handler();
// Runnable that executes the updateGUI method.
private Runnable doUpdateGUI = new Runnable() {
public void run() {
updateGUI();
}
};
// This method must be called on the UI thread.
private void updateGUI() {
// [ ... Open a dialog or modify a GUI element ... ]
}
Handler类还可以使用postDelayed和postAtTime实现推迟运行和推迟指定时间运行:
// Post a method on the UI thread after 1sec.
handler.postDelayed(doUpdateGUI, 1000);
// Post a method on the UI thread after the device has been in use for 5mins.
int upTime = 1000*60*5;
handler.postAtTime(doUpdateGUI, SystemClock.uptimeMillis()+upTime);
使用ALARMS
与Timer不太,Alarms属于系统服务,独立于应用程序。即使应用程序为启动,也可以使用Alarms启动应用程序并获取其服务,这样不仅减少了耦合,也减少了系统资源的占用。Android中Alarms常与Broadcast Receiver一起使用。创建Alarm之前,首先要创建AlarmManager:
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
创建,设置和取消ALARMS
创建Alarm需要这些参数:alarm类型,触发时间,Alarm将要触发的Pending Intent。目前Alarm类型有这些:
- RTC_WAKEUP:在指定时间启动指定Pending Intent,可以唤醒sleep中的设备。
- RTC:在指定时间启动指定Pending Intent,但不能唤醒sleep中的设备。
- ELAPSED_REALTIME:在某段时间后启动指定的Pending Intent,某段时间是从设备启动但还没有唤醒设备算起。
- ELAPSED_REALTIME_WAKEUP: 这个和ELAPSED_REALTIME的区别没搞明白,以后遇到了再查吧。
下面是一个10秒后启动Pending Intent的Alarm示例:
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
// Set the alarm to wake the device if sleeping.
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
// Trigger the device in 10 seconds.
long timeOrLengthofWait = 10000;
// Create a Pending Intent that will broadcast and action
String ALARM_ACTION = “ALARM_ACTION”;
Intent intentToFire = new Intent(ALARM_ACTION);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
// Set the alarm
alarmManager.set(alarmType, timeOrLengthofWait, alarmIntent);
取消Alarm
alarmManager.cancel(alarmIntent);
这里的alarmIntent是指使用Alarm启动的Pending Intent。
创建可重复的Alarm
使用setRepeating或setInexactRepeating方法替代前面的set方法,并传递响应的参数进去,就可以实现可重复的Alarm。
相比setRepeating,setInexactRepeating更省电,但不能指定某个具体的时间间隔。
setInexactRepeating可以接收的时间间隔参数:
- INTERVAL_FIFTEEN_MINUTES
- INTERVAL_HALF_HOUR
- INTERVAL_HOUR
- INTERVAL_HALF_DAY
- INTERVAL_DAY
下面这个例子指定半小时后启动Alarm,然后每隔半小时启动一次:
// Get a reference to the Alarm Manager
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
// Set the alarm to wake the device if sleeping.
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
// Schedule the alarm to repeat every half hour.
long timeOrLengthofWait = AlarmManager.INTERVAL_HALF_HOUR;
// Create a Pending Intent that will broadcast and action
String ALARM_ACTION = “ALARM_ACTION”;
Intent intentToFire = new Intent(ALARM_ACTION);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
// Wake up the device to fire an alarm in half an hour, and every half-hour after that.
alarmManager.setInexactRepeating(alarmType, timeOrLengthofWait, timeOrLengthofWait, alarmIntent);
Android 4 学习(19):Services的更多相关文章
- 学Android开发 这19个开发工具助你顺风顺水
学Android开发 这19个开发工具助你顺风顺水 要想快速开发一个Android应用,通常会用到很多工具,巧妙利用这些工具,能让我们的开发工作事半功倍,节省大量时间,下面大连Android开发培训小 ...
- Android Animation学习(二) ApiDemos解析:基本Animatiors使用
Animator类提供了创建动画的基本结构,但是一般使用的是它的子类: ValueAnimator.ObjectAnimator.AnimatorSet ApiDemos中Animation部分是单独 ...
- Android 布局学习之——Layout(布局)具体解释二(常见布局和布局參数)
[Android布局学习系列] 1.Android 布局学习之--Layout(布局)具体解释一 2.Android 布局学习之--Layout(布局)具体解释二(常见布局和布局參数) ...
- 我的Android 4 学习系列之文件、保存状态和首选项
目录 使用Shared Preference 保留简单的应用程序数据 保存回话间的Activity实例数据 管理应用程序首选项和创建Preference Screen 保存并加载文件以及管理本地文件系 ...
- Android开发学习之路--网络编程之xml、json
一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载 ...
- Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)
[Android布局学习系列] 1.Android 布局学习之——Layout(布局)详解一 2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数) 3.And ...
- Android JNI学习(二)——实战JNI之“hello world”
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- Android开发学习路线图
Android开发学习方法: Android是一个比较庞大的体系,从底层的Linux内核到上层的应用层,各部分的内容跨度也比较大.因此,一个好的学习方法对我们学习Android开发很重要. 在此建议, ...
随机推荐
- P4语言编程详解
1.源码目录结构 P4项目源码可以在github上直接获取(https://github.com/p4lang).P4项目由很多个单独的模块组成,每个模块就是一个子项目,下面分别简单介绍一下各模块的功 ...
- 【测试技术】websocket-client
Websocket Client 继续上周的议题.因为我不会写go,不会写websocket客户端,导致整个测试过程我是个完美的酱油党.上周我终于把客户端服务写好了. 选择Websokcet框架 现在 ...
- 关于simulink hdlcoder的优化问题
HDL Block Properties中包含有多个优化选项. 1,delay balance 当其他分支优化过后,可能会引入一个或几个周期的delay,这时候需要在与其并行的几条信号路径上也加上de ...
- matplotlib ----- 初步
直接看几段代码即可: # 加载模块的方式 import matplotlib.pyplot as plt import numpy as np # 最简单的单线图 x = np.linspace(0, ...
- conduit 安装试用
备注: 测试安装环境使用docker mac 版本(目前版本已经支持kubernetes了) 1. 基本安装 curl https://run.conduit.io/install | bash 配置 ...
- Python 函数 -range()
range() pytho range() 函数可创建一个整数列表,一般用在 for 循环中. 语法: range(start, stop[, step]) start: 计数从 start 开始.默 ...
- JavaScript模块化-require.js,r.js和打包发布
在JavaScript模块化和闭包和JavaScript-Module-Pattern-In-Depth这两篇文章中,提到了模块化的基本思想,但是在实际项目中模块化和项目人员的分工,组建化开发,打包发 ...
- ubuntu 修改分辨率为自定义分辨率
在ubuntu14.04虚拟机上修改自定义大小的桌面屏幕分辨率,使用的命令:cvt,xrandr 0.首先查看下当前已经提供的分辨率设置:xrandr -q root@xxx:/home/xxx/De ...
- laravel的中间件demo
过滤器已经被废除...刚学才两天,蛋疼 创建一个中间件 ./artisan make:middleware TestMiddleware 大概代码 <?php namespace App\Htt ...
- C#(.Net)中调用Sql sever汉字字符串显示为?问号
利用Sql语言,向数据库中导入‘C语’,结果在检查的时候,发现如上图所示. 网络上,很多人说是编码问题,但是都没给出具体的解决方案,最终用这种方法解决了! 把上图中需要储存汉字字符串的类型变为 nva ...