本文目录 ⌄
第二十八章 后台服务
有关IntentService
IntentService服务能够按照顺序执行命令队列里面的命令,这些命令就是IntentService服务需要响应(onHandleIntent)的intent。需要声明这个元素节点:
<service android:name=".PollService"/>有关后台网络连接安全
在后台连接网络时,需要使用ConnectivityManager确认网络连接是否可用,首先获取权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />然后根据网络服务是否可用(connectivityManager.getActiveNetworkInfo())、网络是否连得上(connectivityManager.getActiveNetworkInfo().isConnected())来确定是否应该启用服务:
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (!isNetworkAvailableAndConnected())
{
return;
}
...
}
private boolean isNetworkAvailableAndConnected()
{
ConnectivityManager connectivityManager=(ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE);
boolean isNetWorkAvailable=connectivityManager.getActiveNetworkInfo()!=null;
boolean isNetworkConnected=isNetWorkAvailable&&connectivityManager.getActiveNetworkInfo().isConnected();
return isNetworkConnected;
}有关使用AlarmManager延迟运行服务
PendingIntent打包
PendingIntent.getService()方法打包了一个Context.startService(Intent)方法的调用:
Intent i=PollService.newIntent(context);PendingIntent pendingIntent=PendingIntent.getService(context,0,i,0);这个方法有四个参数,第一个是用来发送intent的context,第二个是区分PendingIntent来源的请求码,第三个是待发送的Intent对象,第四个是如何创建PendingIntent的标识符。第四个参数的一个例子如下:
Intent intent=PollService.newIntent(context);PendingIntent pendingIntent=PendingIntent.getService(context,0,intent, PendingIntent.FLAG_NO_CREATE);PendingIntent.FLAG_NO_CREATE实际上并不做的发送这个任务,它只是用以判断定时器的启停状态。
AlarmManager定时
AlarmManager.setRepeating()方法是设置定时器,这个方法也有四个参数,第一个是描述定时器事件基准的常量,第二个是定时器启动的时间,第三个是定时器循环的时间间隔,第四个是要发送的PendingIntent。
AlarmManager alarmManager=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(),POLL_INTERVAL_MS,pendingIntent);取消定时器是AlarmManager.cancel(),参数就是要取消的PendingIntent:
alarmManager.cancel(pendingIntent);有关通知信息
想要发送通知消息,首先要创建Notification对象,可以用构造器来创建,完整的Notification需要有:tick text、图标、通知信息本身,待触发的PendingIntent。
Notification notification=new NotificationCompat.Builder(this) .setTicker(resources.getString(R.string.new_pictures_titles)) .setSmallIcon(android.R.drawable.ic_menu_report_image) .setContentTitle(resources.getString(R.string.new_pictures_titles)) .setContentText(resources.getString(R.string.new_picture_text)) .setContentIntent(pi) .setAutoCancel(true) .build();发送通知信息需要调用NotificationManager的notify方法,构造NotificationManager的方式是从当前的context中取出有关NotificationManagerCompat实例:
Notification notification=new NotificationCompat.Builder(this) .setTicker(resources.getString(R.string.new_pictures_titles)) .setSmallIcon(android.R.drawable.ic_menu_report_image) .setContentTitle(resources.getString(R.string.new_pictures_titles)) .setContentText(resources.getString(R.string.new_picture_text)) .setContentIntent(pi) .setAutoCancel(true) .build();NotificationManagerCompat notificationManager= NotificationManagerCompat.from(this);notificationManager.notify(0,notification);有关挑战练习 在Lollipop设备上使用JobService
我原先误解了这个题设的意思,我以为是在onStartJob方法中选择启动的方式,导致我卡了很长的时间都没有解决方案。参考了网上的解法之后,我发现挑战练习之前的介绍就是实现的这一题的过程。
public class PollJobService extends JobService:
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public class PollJobService extends JobService { private PollTask mCurrentTask; private static final String TAG="PollJobService"; @Override public boolean onStartJob(JobParameters jobParameters) { mCurrentTask=new PollTask(); mCurrentTask.execute(jobParameters); return true; } private class PollTask extends AsyncTask<JobParameters,Void,Void> { @Override protected Void doInBackground(JobParameters... jobParameters) { JobParameters jobParams=jobParameters[0]; PollFilckr(); jobFinished(jobParams,false); return null; } } @Override public boolean onStopJob(JobParameters jobParameters) { if (mCurrentTask!=null) { mCurrentTask.cancel(true); } return true; } public static void setServiceAlarm(Context context, boolean shouldStartAlarm) { 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*15) .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; } private void PollFilckr() { String query=QueryPreferences.getStoredQuery(PollJobService.this); String lastResultId=QueryPreferences.getPrefLastResultId(PollJobService.this); List<GalleryItem> items; if (query==null) { items=new FlickrFetchr().fetchRecentPhotos(1); } else { items=new FlickrFetchr().searchPhotos(query); } if (items.size()==0) { return; } String resultId=items.get(0).getID(); if (resultId.equals(lastResultId)) { Log.i(TAG,"Got an old result: "+resultId); } else { Log.i(TAG,"Got a new result: "+resultId); Resources resources=getResources(); Intent i=PhototGalleryActivity.newIntent(PollJobService.this);//就是一个context对象 PendingIntent pi=PendingIntent.getActivity(PollJobService.this,0,i,0); Notification notification=new NotificationCompat.Builder(PollJobService.this) .setTicker(resources.getString(R.string.new_pictures_titles)) .setSmallIcon(android.R.drawable.ic_menu_report_image) .setContentTitle(resources.getString(R.string.new_pictures_titles)) .setContentText(resources.getString(R.string.new_picture_text)) .setContentIntent(pi) .setAutoCancel(true) .build(); NotificationManagerCompat notificationManager= NotificationManagerCompat.from(PollJobService.this); notificationManager.notify(0,notification); } QueryPreferences.setPrefLastResultId(PollJobService.this,resultId); }}在fragment中的菜单项目中选择使用的服务:
@Overridepublic void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.fragment_photo_gallery,menu); MenuItem searchItem=menu.findItem(R.id.menu_item_search); final SearchView searchView=(SearchView)searchItem.getActionView(); searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { Log.i(TAG,"QueryTextSumbit: "+s); QueryPreferences.setStoredQuery(getActivity(),s); updateItems(); searchView.onActionViewCollapsed(); return true; } @Override public boolean onQueryTextChange(String s) { Log.i(TAG,"QueryTextChange: "+s); return false; } }); searchView.setOnSearchClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String query=QueryPreferences.getStoredQuery(getActivity()); searchView.setQuery(query,false); } }); MenuItem toggleItem =menu.findItem(R.id.menu_item_toggle_polling); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ if(PollJobService.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); } }}@Overridepublic boolean onOptionsItemSelected(@NonNull 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 = PollJobService.isHasBeenScheduled(getActivity()); //启动服务 PollJobService.setServiceAlarm(getActivity(),shouldStartAlarm); getActivity().invalidateOptionsMenu(); }else{ boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity()); PollService.setServiceAlarm(getActivity(),shouldStartAlarm); getActivity().invalidateOptionsMenu(); //刷新菜单项 } return true; default: return super.onOptionsItemSelected(item); }}