26.11 挑战练习:在 Lollipop 设备上使用 JobService
请创建另一个 PollService 实现版本。新的 PollService 应该继承 JobService 并使用
JobScheduler 来运行。在 PollService 启动代码中,检查系统版本是否为Lollipop:如果是,就
使用 JobScheduler 计划运行 JobService ;如果不是,依然使用 AlarmManager 实现。

JobService类:

 package com.bignerdranch.android.photogallery;

 import android.app.Notification;
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.util.Log; import java.util.List; /**
* Created by Leo on 2017/7/22.
*/ public class JobService extends android.app.job.JobService { private static final String TAG = "PollService"; private PollTask mCurrentTask; @Override
public boolean onStartJob(JobParameters parms){ //服务启动后执行
mCurrentTask = new PollTask();
mCurrentTask.execute(parms); //开启AsyncTask
return true;
} @Override
public boolean onStopJob(JobParameters params) {
if(mCurrentTask != null){
mCurrentTask.cancel(true);
}
return false;
} public static void setServiceAlarm(Context context,Boolean shouldStartAlarm){ //Fragment中启动服务需要调用的方法.用于在Fragment启动JobService
final int JOB_ID = 1;
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
if(shouldStartAlarm){
scheduler.cancel(JOB_ID);
}else{
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, new ComponentName(context, JobService.class))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setPeriodic(1000 * 60)
.setPersisted(true)
.build();
scheduler.schedule(jobInfo);
} } //判断是否已经计划好了任务
public static boolean isHasBeenScheduled(Context context){ final int JOB_ID = 1; JobScheduler scheduler = (JobScheduler)context.getSystemService(JOB_SCHEDULER_SERVICE); boolean hasBeenScheduled = false; for (JobInfo jobInfo : scheduler.getAllPendingJobs()) {
if (jobInfo.getId() == JOB_ID) {
hasBeenScheduled = true;
}
} return hasBeenScheduled;
} public JobService(){
super();
} //用于执行
private class PollTask extends AsyncTask<JobParameters,Void ,List<GalleryItem>> {
//在doInBackground()方法中获取到最新的结果集。
@Override
protected List<GalleryItem> doInBackground(JobParameters... params) {
JobParameters jobParams = params[0]; //不需要手动判断网络连接状态了 String query = QueryPreferences.getStoredQuery(JobService.this); //获取查询值 List<GalleryItem> items; //获取最新结果集
if(query == null){
items = new FlickrFetcher().fetchRecentPhotos();
}else{
items = new FlickrFetcher().searchPhotos(query);
} jobFinished(jobParams,false); return items; //将items返回出去, onPostExcute()方法会接受到
} @Override
protected void onPostExecute(List<GalleryItem> items) {
String lastResultId = QueryPreferences.getLastResultId(JobService.this);
//获取第一条结果
String resultId = items.get(0).getId();
//确认是否不同于上一次结果ID ,不同的话弹出Notification。
if(resultId.equals(lastResultId)){
Log.i(TAG, "Got a old result" + resultId);
}else{
Log.i(TAG, "Got a new result" + resultId); Resources resources = getResources();
Intent i = PhotoGalleryActivity.newIntent(JobService.this);
PendingIntent pi = PendingIntent.getActivity(JobService.this,0,i,0); Notification notification = new NotificationCompat.Builder(JobService.this)
.setTicker(resources.getString(R.string.new_pictures_title))
.setSmallIcon(android.R.drawable.ic_menu_report_image)
.setContentTitle(resources.getString(R.string.new_pictures_title))
.setContentText(resources.getString(R.string.new_pictures_text))
.setContentIntent(pi)
.setAutoCancel(true)
.build(); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(JobService.this);
notificationManager.notify(0,notification);
}
//将第一条结果存入SharedPreferences
QueryPreferences.setLastResultId(JobService.this,resultId);
}
}
}
PhotoGalleryFragment类:
onOptionsSelected()方法:
@Override
public boolean onOptionsItemSelected(MenuItem item){
switch (item.getItemId()){
case R.id.menu_item_clear:
QueryPreferences.setStoredQuery(getActivity(),null);
updateItems();
return true;
case R.id.menu_item_toggle_polling:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
boolean shouldStartAlarm = JobService.isHasBeenScheduled(getActivity()); //启动服务
JobService.setServiceAlarm(getActivity(),shouldStartAlarm);
getActivity().invalidateOptionsMenu();
}else{
boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
PollService.setServiceAlarm(getActivity(),shouldStartAlarm);
getActivity().invalidateOptionsMenu(); //刷新菜单项 }
return true;
default:
return super.onOptionsItemSelected(item);
}

onCreateOptionsMenu()方法:


public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater){
super.onCreateOptionsMenu(menu,menuInflater);
menuInflater.inflate(R.menu.fragment_photo_gallery,menu); MenuItem searchItem = menu.findItem(R.id.menu_item_search); //将MenuItem取出并保存在searchItem变量中。
mSearchView = (SearchView)searchItem.getActionView(); //取出SearchView对象。 //设置监听器
mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {//提交搜索查询时。这个方法执行。
Log.d(TAG, "onQueryTextSubmit: "+ s);
QueryPreferences.setStoredQuery(getActivity(),s); //存储用户提交的查询信息。
updateItems();
hintSoftInputAndSearchField(); //隐藏键盘的方法
return true;
} @Override
public boolean onQueryTextChange(String s) { //搜索框的文字有变化时,这个方法执行。
Log.d(TAG, "onQueryTextChange: " + s);
return false;
}
}); MenuItem toggleItem = menu.findItem(R.id.menu_item_toggle_polling);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
if(JobService.isHasBeenScheduled(getActivity())){
toggleItem.setTitle(R.string.stop_polling);
}else {
toggleItem.setTitle(R.string.start_polling);
}
}else{
if (PollService.isServiceAlarmOn(getActivity())) {
toggleItem.setTitle(R.string.stop_polling);
} else {
toggleItem.setTitle(R.string.start_polling);
}
} mSearchView.setOnSearchClickListener(new View.OnClickListener() { //设置点击按钮时,显示之前搜索过的值
@Override
public void onClick(View v) {
String query = QueryPreferences.getStoredQuery(getActivity());
mSearchView.setQuery(query,false);
}
}); }

 

安卓权威编程指南 挑战练习(第26章 在 Lollipop 设备上使用 JobService)的更多相关文章

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

    你可能已经注意到了,提交搜索时, RecyclerView 要等好一会才能刷新显示搜索结果.请接受挑战,让搜索过程更流畅一些.用户一提交搜索,就隐藏软键盘,收起 SearchView 视图(回到只显示 ...

  2. 安卓权威编程指南 挑战练习 13.8 用于RecyclerView的空视图

    当前,CriminalIntent应用启动后,会显示一个空白列表.从用户体验上来讲,即使crime列表 是空的,也应展示提示或解释类信息. 请设置空视图展示类似“没有crime记录可以显示”的信息.再 ...

  3. 安卓权威编程指南 挑战练习:实现高效RecyclerView刷新

    Adapter的notifyDataSetChanged方法会通知RecyclerView刷新全部的可见列表项. 在CriminalIntent应用里,这个方法不够高效,我们知道,返回CrimeLis ...

  4. 安卓权威编程指南-笔记(第23章 HTTP与后台任务)

    1. 网络连接基本 //通过指定URL获取原始数据,并返回一个字节流数组. public byte[] getUrlBytes(String urlSpec)throws IOException{ / ...

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

    在Andorid的世界里,凡事要在屏幕上绘制的东西都可以叫drawable,比如抽象图形,Drawable的子类,位图图形等,我们之前用来封装图片的BitmapDrawable就是一种drawable ...

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

    本章需求:首先,让应用轮询新结果并在有所发现时及时通知用户,即使用户重启设备后还没有打开过应用.其次,保证用户在使用应用时不出现新结果通知. 1. 一般intent和broadcast intent ...

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

    AsyncTask是执行后台线程的最简单方式,但它不适用于那些重复且长时间运行的任务. 1. Looper Android中,线程拥有一个消息队列(message queue),使用消息队列的线程叫做 ...

  8. 安卓权威编程指南-笔记(第22章 深入学习intent和任务)

    本章,我们会使用隐式intent创建一个替换android默认启动器的应用.名为NerdLauncher. NerdLauncher应用能列出设备上的其他应用,点选任意列表项会启动相应应用. 1. 解 ...

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

    学习安卓编程权威指南第五章的时候自己写了个简单的Demo来加深理解两个Activity互相传递数据的问题,然后将自己的学习笔记贴上来,如有错误还请指正. IntentActivityDemo学习笔记 ...

随机推荐

  1. Linux inode 理解

    inode 硬盘的最小存储单位叫做"扇区"(Sector).每个扇区储存512字节(相当于0.5KB).操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性读取一 ...

  2. mysql查看变量

    在MySQL客户端执行如下命令查看MySQL的数据存放位置: show global variables like "%datadir%"; 查看端口号 show global v ...

  3. Python语言学习前提:循环语句

    一.循环语句 1.循环语句:允许执行下一个语句或语句组多次 2. 循环类型 3. 循环控制语句 4. while 循环语句 a.while循环语句:在某个条件下,循环执行某段程序,以处理需要重复处理的 ...

  4. Flume(二) —— 自定义拦截器、Source、Sink

    自定义拦截器 自定义Source 自定义Sink 引入依赖 <dependency> <groupId>org.apache.flume</groupId> < ...

  5. Hibernate一级缓存Session和对象的状态

    建议看原文:https://blog.csdn.net/qq_42402854/article/details/81461496 一.session简介        首先,SessionFactor ...

  6. crontab不执行service命令

    我这里的需求是每30分钟重启一次 写成下面的格式就可以正确执行了,后面执行的命令写绝对路径 */30 * * * * /usr/bin/supervisorctl restart all

  7. 使用okhttp连接网络,再把数据储存进Sqlite

    这次会把所有之前学过的东西应用在一起,写一个登入的功能. 1. Activity调用CONFIG,获得URL后 2. Activity再调用Okhttp,从服务器返回JSON 3. Activity调 ...

  8. 序列化模块Serializer

    序列化组件: 知识点:Serializer(偏底层).ModelSerializer(重点).ListModelSerializer(辅助群改) Serializer 序列化准备: 模型层:model ...

  9. dao层单元测试报错CONDITIONS EVALUATION REPORT

    0 环境 系统:win10 编辑器:IDEA 1 正文 1.1 起因 在controller层测试 测试url时没问题的 但是我单元测试就报错 1.2 排查 因为controller层 springb ...

  10. LeetCode No.94,95,96

    No.94 InorderTraversal 二叉树的中序遍历 题目 给定一个二叉树,返回它的中序 遍历. 示例 输入: [1,null,2,3] 1 \ 2 / 3 输出: [1,3,2] 进阶:递 ...