安卓权威编程指南-笔记(第26章 服务的作用)

1. IntentService

IntentService也是一个context(Service是Context的子类),并能够响应intent。

一个最基本的IntentService实例如下:

public class PollService extends IntentService {
    private static final String TAG = "PollService";
    public static Intent newIntent(Context context) {
    return new Intent(context, PollService.class);
}
public PollService() {
    super(TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
    Log.i(TAG, "Received an intent: " + intent);
    }
}

服务的intent又称作命令,每一个命令都要求服务完成某项具体的任务,服务种类不同,其执行命令的方式也不尽相同。

接收到首个命令时, IntentService 完成启动,并触发一个后台线程,然后将命令放入队列。随后, IntentService 继续按顺序执行每一条命令,并针对每一条命令在后台线程上调用onHandleIntent(Intent) 方法。新进命令总是放置在队列尾部。最后,执行完队列中的全部命令后,服务也随即停止并被销毁。

2.检查网络的可用性

  

private boolean isNetworkAvailableAndConnected() {
    ConnectivityManager cm =(ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
    boolean isNetworkAvailable = cm.getActiveNetworkInfo() != null;
    boolean isNetworkConnected = isNetworkAvailable &&
    cm.getActiveNetworkInfo().isConnected();
    return isNetworkConnected;
}//获取网络状态需要增加权限<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

3.Notification

 1  protected void onCreate(Bundle savedInstanceState) {
 2         super.onCreate(savedInstanceState);
 3         setContentView(R.layout.activity_main);
 4
 5         final Button send_notice = (Button)findViewById(R.id.send_notice);
 6         send_notice.setOnClickListener(new View.OnClickListener() {
 7             @Override
 8             public void onClick(View v) {
 9                 //1.获取NotificationManager对象
10                 NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
11                 //设置意图
12                 Intent intent = new Intent(MainActivity.this,NotificationActivity.class);
13                 //通过PengdingIntent.getActivity得到PendingIntent对象
14                 PendingIntent pendingIntent = PendingIntent.getActivity(MainActivity.this,0,intent,0);
15                 Notification notification = new Notification.Builder(MainActivity.this)
16                         .setContentTitle("1")
17                         .setContentText("?")
18                         .setContentIntent(pendingIntent) //设置ContentIntent 传入PendingIntent 设置点击事件
19                         .setWhen(System.currentTimeMillis())
20                         .setSmallIcon(R.mipmap.ic_launcher)
21                         .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
22
23                         .setAutoCancel(true)//当点击了这个通知的时候 通知会自动取消掉 取消通知的第二种方法 调用得到的Manager对象的cancle方法 cancle方法中传入创建通知时给定的Id
24
25                         .setSound(Uri.fromFile(new File("/system/media/audio/ringtones/Luna.ogg")))
26
27                         //设置手机静止和震动的时长,以毫秒为单位,以0开始 奇数为震动的时长 偶数为静止的时长 需要声明权限
28                         .setVibrate(new long[]{0,1000,1000,1000})
29
30                         /*
31                         *控制手机Led灯
32                         * 第一个参数用于指定Led灯的颜色 第二个参数用于指定LED灯亮起的时长,以毫秒为单位,第三个参数用于指定LED灯暗去的时长
33                          */
34                         .setLights(Color.RED,1000,1000)
35
36                         //使用通知的默认效果 会根据当前手机环境来决定播放什么铃声以及如何震动
37 //                        .setDefaults(NotificationCompat.DEFAULT_ALL)
38
39                         /*
40                         *通过setStyle方法在通知中显示一段长文字
41                         *创建一个NotificationCompat.BigTextStyle对象,调用它的bigText方法将文字传入即可
42                          */
43                         .setStyle(new Notification.BigTextStyle().bigText("Learn NotificaionLearn NotificaionLearn NotificaionLearn Notific" +
44                                 "aionLearn NotificaionLearn NotificaionLearn NotificaionLearn NotificaionLearn NotificaionLear" +
45                                 "n NotificaionLearn Notificaion Learn Notificaion"))
46
47                         //显示一张大图片
48                         .setStyle(new Notification.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)))
49
50                         //设置优先级
51                         .setPriority(NotificationCompat.PRIORITY_MAX)
52
53
54                         .build();
55                 //调用notify 方法将Notification显示出来
56                 manager.notify(1,notification);
57
58
59             }
60         });

4.服务

4.1 服务的能与不能

  与activity一样,服务是一个有生命周期回调方法的应用组件,这些回调方法同样也会在主线程上运行。

  初始创建的服务不会在后台线程上运行任何代码,这也是我们推荐使用IntentService的最主要原因,大多数重要服务都需要某种后台进程,而IntentService已提供了一套标准实现代码。

4.2 服务的生命周期

  如果是startService(Intent)方法启动的服务,其生命周期很简单,并具有三种周期回调方法。  

  •   onCreate方法:服务创建时调用。
  •   onStartCommand(Intent, int, int )方法:每次组件通过startService(intent)方法启动时调用一次。它有两个整数参数,一个是标识符集,一个是启动ID。标识符集用来表示当前intent发送究竟是一次重新发送,还是一次从没成功的发送,每次调用onStartCommand(Intent , int ,int )方法,启动ID都会不同,因此,启动ID也可用于区分不同的命令。
  •   onDestroy()方法:服务不再需要时调用,通常是在服务停止后。

  服务停止时会调用onDestroy()方法,服务停止的方式取决于服务的类型。服务的类型由onStartCommand()方法的返回值确定,可能的服务类型有Service.START_NOT_STICKY、START_READLIVER_INTENT和START_STICKY.

4.3 non-sticky 服务

  IntentService是一种non-sticky服务。non-sticky服务在服务自己认为自己已完成任务时停止。为获得non-sticky服务,应返回START_NOT_STICKY或START_REDELIVER_INTENT.

  通过调用stopSelf()或stopSelf(int)方法,我们告诉Android任务已完成。stopself()是个无条件方法。不管onStartCommand()方法调用多少次。该方法总会成功停止服务。

  stopSelf(int) 是个有条件的方法。该方法需要来自于 onStartCommand(...) 方法的启动ID。只有在接收到最新启动ID后,该方法才会停止服务。(这也是 IntentService 的后台工作原理。)

    返回 START_NOT_STICKY 和 START_REDELIVER_INTENT 有什么不同呢?区别就在于,如果系统需要在服务完成任务之前关闭它,则服务的具体表现会有所不同。 START_NOT_STICKY 型服务说消亡就消亡了;而 START_REDELIVER_INTENT 型服务则会在资源不再吃紧时,尝试再次启动服务。    

   选择 START_NOT_STICKY 还是 START_REDELIVER_INTENT ,这要看服务对应用有多重要了。如果不重要,就选择 START_NOT_STICKY 。

4.4 sticky 服务

  sticky服务会持续运行,直到外部组件调用Context.stopService(Intent)方法让它停止。为获得sticky服务,应返回START_STICKY。

    Sticky服务启动后会持续运行,除非某个组件调用Context.stopService(Intent)方法停止它。

    可传入一个null intent 给 onStartCommand()方法,实现服务的重启。

  sticky服务适用与长时间运行的服务。

4.5 绑定服务

除以上各类服务外,也可使用 bindService(Intent,ServiceConnection, int) 方法绑定一个服务,以此获得直接调用绑定服务方法的能力。 ServiceConnection 是代表服务绑定的一个对象。它负责接收全部绑定回调方法。

fragment中,绑定代码示例代码如下:

private ServiceConnection mServiceConnection = new ServiceConnection() {
     public void onServiceConnected(ComponentName className,IBinder service) {
    // Used to communicate with the service
    MyBinder binder = (MyBinder)service;
    }
    public void onServiceDisconnected(ComponentName className) {
    }
 };
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent i = new Intent(getActivity(), MyService.class);
        getActivity().bindService(i, mServiceConnection, 0);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        getActivity().unbindService(mServiceConnection);
    }
}

对服务来说,绑定引入了另外两个生命周期回调方法。
? onBind(Intent) 方法:每次绑定服务时调用,返回来自 ServiceConnection.onService
Connected(ComponentName,IBinder) 方法的 IBinder 对象。
? onUnbind(Intent) 方法:服务绑定终止时调用。

4.5.1. 本地服务绑定
MyBinder 是怎样一种对象呢?如果服务是个本地服务, MyBinde 很可能就是本地进程中的一
个简单Java对象。通常, MyBinde 用于提供一个句柄,以便直接调用服务方法:

private class MyBinder extends IBinder {
    public MyService getService() {
    return MyService.this;
    }
}
@Override
public void onBind(Intent intent) {
    return new MyBinder();
}

这种模式看上去让人激动。这是Android系统中唯一一处支持组件间直接对话的地方。不过,
我们并不推荐此种模式。服务是种高效的单例,与仅使用一个单例相比,使用此种模式显现不出
优势。

4.5.2. 远程服务绑定
绑定更适用于远程服务,因为它们赋予了其他进程中应用调用服务方法的能力。

5. JobScheduler和JobService

使用AlarmManager、IntentService和PendingIntent相互配合,创走周期性的后台任务,实现一个完全可用的后台服务还需要手动执行以下操作。

?  计划一个周期性任务

?  检查周期性任务的运行状态

?  检查网络是否可用

在实际场景下,还有更多想法需要实现,例如请求失败,是否还需要稍后重试机制。或者是只允许应用使用不限量的网络连接...

在系统控制方面,本章实现的后台服务也存在一些问题,它无法在某种情况停下来,除非手动停止。

JobScheduler:除了实现常规后台任务之外,JobScheduler还支持按场景、按条件运行后台服务。

JobScheduler的使用:

创建一个类继承JobService,并覆盖onStartJob(JobParameters parms)方法和onStopJob(JobParameters params)。

public class PollService extends JobService {

    @Override
    public boolean onStartJob(JobParameters params) {
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

Android准备好执行任务时,服务就会启动,此时会在主线程上收到onStartJob()方法调用。该方法返回false结果表示:"交代的任务我已全力去做,现在做完了。”返回 true 结果则表示:“任务收到,正在做,但是还没有做完。”

JobService在执行的时候需要单开线程,可以使用AsyncTask按如下方式创建新线程。

    private PollTask mCurrentTask;

@Override
    public boolean onStartJob(JobParameters parms){
        mCurrentTask = new PollTask();
        mCurrentTask.execute(parms);
        return true;
    }    

private class PollTask extends AsyncTask<JobParameters,Void,Void> {
    @Override
    protected Void doInBackground(JobParameters... params) {
        JobParameters jobParams = params[0];
        //执行任务的逻辑
        jobFinished(jobParams, false);
        return null;
    }
}    

任务执行完毕后,就可以调用jobFinished(JobParameters, boolean)方法通知结果,如果该方法的第二个参数传入true的话,就等于说:“事情这次做不完了,请计划在下次某个时间继续吧。”

onStopJob(JobParameters)方法适合在中断任务时调用,用户通常需要服务在有WIFI连接时才运行,如果在调用JobFinished()之前(任务完成之前),手机就没了Wifi,onStopJob(...) 方法就会被调用,也就是说,一切任务就立即停止了。

@Override
public boolean onStopJob(JobParameters params) {
    if (mCurrentTask != null) {
    mCurrentTask.cancel(true);
    }
    return true;
}

调用 onStopJob(...) 方法就是表明,服务马上就要停掉了。不要抱有幻想,请立即停止手头上的一切事情。这里,返回 true 表示:“任务应该计划在下次继续。”返回 false 表示:“不管怎样,事情就到此结束吧,不要计划下次了。”

使用JobService,必须在Manifest配置清单中添加权限:

<service
android:name=".PollService"
android:permission="android.permission.BIND_JOB_SERVICE"  //添加的权限控制只有JobScheduler才能运行它。
android:exported="true"/>

通过JobScheduler检查是否已计划好了任务:

final int JOB_ID = 1;
JobScheduler scheduler = (JobScheduler)
    context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
boolean hasBeenScheduled = false;
for (JobInfo jobInfo : scheduler.getAllPendingJobs()) {
    if (jobInfo.getId() == JOB_ID) {
    hasBeenScheduled = true;
    }
}

PoolService的运行:

final int JOB_ID = 1;

JobScheduler scheduler = (JobScheduler)
    context.getSystemService(Context.JOB_SCHEDULER_SERVICE);

    JobInfo jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(context, PollService.class))
    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
    .setPeriodic(1000 * 60 * 15)
    .setPersisted(true)
    .build();
scheduler.schedule(jobInfo);

上述代码计划任务每15分钟运行一次,但前提条件是有Wi-Fi或有可用的不限流量网络。调用 setPersisted(true) 方法可保证服务在设备重启后也能按计划运行。

时间: 2024-11-07 04:57:42

安卓权威编程指南-笔记(第26章 服务的作用)的相关文章

安卓权威编程指南-笔记(第21章 XML drawable)

在Andorid的世界里,凡事要在屏幕上绘制的东西都可以叫drawable,比如抽象图形,Drawable的子类,位图图形等,我们之前用来封装图片的BitmapDrawable就是一种drawable. 本章我们还会看到更多的drawable:state list drawable.shape drawable和layer list drawable. 这三个drawable都定义在XML文件中,可以归为一类,统称为XML drawable. shape drawable 使用ShapeDraw

安卓权威编程指南-笔记(第27章 broadcast intent)

本章需求:首先,让应用轮询新结果并在有所发现时及时通知用户,即使用户重启设备后还没有打开过应用.其次,保证用户在使用应用时不出现新结果通知. 1. 一般intent和broadcast intent 许多系统组件需要知道某些事件的发生(WIFI信号时有时无,电话的呼入等),为满足这样的需求,Andorid提供了broadcast intent 组件. broadcast intent的工作原理类似于之前学过的intent,但不同的是broadcast intent可以被多个叫做broadcast

安卓权威编程指南-笔记(第25章 搜索)

1. SearchView SearchView是个操作视图,所谓操作视图,就是可以内置在工具栏中的视图.SearchView可以让整个搜索界面完全内置在应用的工具栏中. 1.1 SearchView的配置 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:ap

安卓权威编程指南-笔记 (第29章定制视图与触摸事件)

1.定制视图 Android自带众多优秀的标准视图与组件,但有时为追求独特的应用视觉效果,我们仍需创建定制视图. 定制视图分为两大类别: 简单视图: 简单视图内部也可以很复杂,之所以归为简单类别,是因为简单视图不包括子视图,而且简单视图几乎总是会执行定制绘制. 聚合视图:聚合视图由其他视图对象组成,聚合视图通常管理着子视图,但不负责执行定制绘制,图形绘制任务都委托给了各个子视图. 创建定制视图的所需的三大步骤: 选择超类.对于简单定制视图而言,View是个空白画布,因此它作为超类最常见,对于聚合

安卓权威编程指南-笔记(第24章 Looper Handler 和 HandlerThread)

AsyncTask是执行后台线程的最简单方式,但它不适用于那些重复且长时间运行的任务. 1. Looper Android中,线程拥有一个消息队列(message queue),使用消息队列的线程叫做消息循环(message loop).消息循环会循环检查队列上是否有新消息. 消息循环由线程和looper组成,Looper对象管理着线程的消息队列. 主线程就是个消息循环,因此也拥有Looper,主线程的所有工作都是由其looper完成的,looper不断的从消息队列中抓去消息,然后完成消息指定的

安卓权威编程指南 挑战练习 22章 应用图标

本章使用了 ResolveInfo.loadLabel(...) 方法,在启动器应用中显示了各个activity的名 称. ResolveInfo 类还提供了另一个名为 loadIcon() 的方法.可以使用该方法为每个应用加载 显示图标.你要接受的挑战就是,为NerdLauncher应用中显示的所有应用添加对应的图标. 首先增加一个RecyclerView的条目布局,代码如下: 1 <?xml version="1.0" encoding="utf-8"?&

安卓权威编程指南 挑战练习 24章 预加载以及缓存

24.7 挑战练习:预加载以及缓存 应用中并非所有任务都能即时完成,对此,大多用户表示理解.不过,即使是这样,开发者们也一直在努力做到最好.为了让应用反应更快,大多数现实应用都通过以下两种方式增强自己的代码:? 增加缓存层 ? 预加载图片 缓存指存储一定数目 Bitmap 对象的地方.这样,即使不再使用这些对象,它们也依然存储在那里. 缓存的存储空间有限,因此,在缓存空间用完的情况下,需要某种策略对保存的对象做一定的取舍.许多缓存机制使用一种叫作LRU(least recently used,最

安卓权威编程指南 挑战练习 25章 深度优化 PhotoGallery 应用

你可能已经注意到了,提交搜索时, RecyclerView 要等好一会才能刷新显示搜索结果.请接受挑战,让搜索过程更流畅一些.用户一提交搜索,就隐藏软键盘,收起 SearchView 视图(回到只显示搜索按钮的初始状态).再来个挑战.用户一提交搜索,就初始化 RecyclerView ,显示一个搜索结果加载状态界面(使用状态指示器).下载到JSON数据之后,就删除状态指示器.也就是说,一旦开始下载图片, 就不应显示加载状态了 1.提交搜索,隐藏软键盘,收起SearchView: 将SearchV

安卓权威编程指南 - 第五章学习笔记(两个Activity)

学习安卓编程权威指南第五章的时候自己写了个简单的Demo来加深理解两个Activity互相传递数据的问题,然后将自己的学习笔记贴上来,如有错误还请指正. IntentActivityDemo学习笔记 题目:ActivityA登录界面(用户名.密码.登陆按钮),ActivityB(Edit,返回按键:SubmitButton).A界面输入用户名和密码传到B中,B验证用户输入的用户名和密码,如果错误就返回A,并用Toast 显示用户名和密码错误:如果正确,就在第二个 activity中显示一个Edi