Wakelock、AlarmManager、JobScheduler实例分析
后台定时唤醒App做指定任务实例
public class MainActivity extends Activity implements View.OnClickListener {
private Button btnStartalarm;
private SyncDataRecevier mSyncDataRecevier = new SyncDataRecevier();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStartalarm = (Button)findViewById(R.id.btn_startalarm);
btnStartalarm.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_startalarm:
mSyncDataRecevier.setAlarm(this);
break;
}
}
public void stopAlarm(View view){
mSyncDataRecevier.cancelAlarm(this);
}
}
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.SystemClock;
import android.support.v4.content.WakefulBroadcastReceiver;
import chenxiaojian.backgroundtask.service.SyncDataService;
public class SyncDataRecevier extends WakefulBroadcastReceiver {
private AlarmManager mAlarmManager;
private PendingIntent mPendingAlarmIntent;
@Override
public void onReceive(Context context, Intent intent) {
// ComponentName comp = new ComponentName(context.getPackageName(),
// SyncDataRecevier.class.getName());
// startWakefulService(context,intent.setComponent(comp));
Intent service = new Intent(context, SyncDataService.class);
startWakefulService(context, service);
}
public void setAlarm(Context context) {
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, SyncDataRecevier.class);
mPendingAlarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
//五分钟后,每隔五分钟fired一次。
// mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, AlarmManager
// .INTERVAL_FIFTEEN_MINUTES, AlarmManager.INTERVAL_FIFTEEN_MINUTES,
// mPendingAlarmIntent);
//30秒后,每隔30s就启动一次
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
.elapsedRealtime() + 1 * 1000, 10 * 1000, mPendingAlarmIntent);
//系统重启后依然要重新启动Alarm
ComponentName componentName = new ComponentName(context,BootRecevier.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
public void cancelAlarm(Context context){
if (mAlarmManager != null){
mAlarmManager.cancel(mPendingAlarmIntent);
}
// Disable {@code SampleBootReceiver} so that it doesn't automatically restart the
// alarm when the device is rebooted.
ComponentName receiver = new ComponentName(context, BootRecevier.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
import android.app.IntentService;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import chenxiaojian.backgroundtask.MainActivity;
import chenxiaojian.backgroundtask.R;
import chenxiaojian.backgroundtask.recevier.SyncDataRecevier;
public class SyncDataService extends IntentService {
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
// An ID used to post the notification.
public static final int NOTIFICATION_ID = 1;
public SyncDataService(){
super("SyncDataService");
}
@Override
protected void onHandleIntent(Intent intent) {
//耗时操作
loadNetData();
//发送Notifaction通知
sendNotifation("哈哈来一发");
//通过这个广播接收器释放锁
SyncDataRecevier.completeWakefulIntent(intent);
}
private void loadNetData() {
}
private void sendNotifation(String msg){
mNotificationManager = (NotificationManager) getSystemService(Context
.NOTIFICATION_SERVICE);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
MainActivity.class), 0);
builder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Alarm定时通知")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
builder.setContentIntent(pendingIntent);
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
}
}
public class BootRecevier extends BroadcastReceiver {
private SyncDataRecevier mSyncDataRecevier = new SyncDataRecevier();
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
mSyncDataRecevier.setAlarm(context);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="chenxiaojian.backgroundtask">
<uses-sdk android:minSdkVersion="11" android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service android:name=".service.SyncDataService" android:exported="false"/>
<receiver android:name=".recevier.SyncDataRecevier" android:exported="false"></receiver>
<receiver android:name=".recevier.BootRecevier" android:exported="false"></receiver>
</application>
</manifest>
上面的这个实例是一个经典的用AlarmManager的过程,可以看到我们甚至可以在应用休眠的时间执行代码,唤醒CPU,那说明它一定用到了WakeLock锁。可以注意到我们用了WakefulBroadcastReceiver这个新的广播接收者。
public static ComponentName startWakefulService(Context context, Intent intent) {
synchronized (mActiveWakeLocks) {
int id = mNextId;
mNextId++;
if (mNextId <= 0) {
mNextId = 1;
}
intent.putExtra(EXTRA_WAKE_LOCK_ID, id);
ComponentName comp = context.startService(intent);
if (comp == null) {
return null;
}
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"wake:" + comp.flattenToShortString());
wl.setReferenceCounted(false);
wl.acquire(60*1000);
mActiveWakeLocks.put(id, wl);
return comp;
}
}
分析代码可以知道这个这个Recevier里面有PowerManager.WakeLock,也就是说当AlarmManager被激活后,它会唤起一个PendingIntent。而这个PendingIntent会唤醒这个SyncDataRecevier,这个SyncDataRecevier会拥有CPU锁60s,也就是说,我们的任务应该在60s内完成,为什么非要加这个SyncDataRecevier,虽然我们有AlarmManager可以被执行,但是如果此时AP处理器正在休眠,它依然无法执行代码,也就是说,我们BP是一直是活着的,到一个点之后,它想办法唤醒AP才能执行代码。
Intent intent = new Intent(context, SyncDataRecevier.class);
mPendingAlarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock
.elapsedRealtime() + 1 * 1000, 10 * 1000, mPendingAlarmIntent);
JobScheduler在指定规则下执行任务
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private ComponentName serviceComponent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceComponent = new ComponentName(this, JobSchedulerService.class);
}
public void startJobScheduler(View view) {
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
for (int i = 0; i < 10; i++) {
JobInfo.Builder builder = new JobInfo.Builder(i, serviceComponent)
.setMinimumLatency(5000)//5秒 最小延时、
.setOverrideDeadline(60000)//maximum最多执行时间
// .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
// 免费的网络---wifi 蓝牙 USB
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) //任意网络---
/**
设置重试/退避策略,当一个任务调度失败的时候执行什么样的测量采取重试。
initialBackoffMillis:第一次尝试重试的等待时间间隔ms
*backoffPolicy:对应的退避策略。比如等待的间隔呈指数增长。
*/
// .setBackoffCriteria(long initialBackoffMillis, int backoffPolicy)
// .setPeriodic(long intervalMillis)//设置执行周期,每隔一段时间间隔任务最多可以执行一次。
// .setPeriodic(long intervalMillis,long flexMillis)
// 在周期执行的末端有一个flexMiliis长度的窗口期,任务就可以在这个窗口期执行。
//设置设备重启后,这个任务是否还要保留。需要权限:RECEIVE_BOOT_COMPLETED //ctrl+shift+y/u x
// .setPersisted(boolean isPersisted);
// .setRequiresCharging(boolean )//是否需要充电
// .setRequiresDeviceIdle(boolean)//是否需要等设备出于空闲状态的时候
// .addTriggerContentUri(uri)//监听uri对应的数据发生改变,就会触发任务的执行。
// .setTriggerContentMaxDelay(long duration)//设置Content发生变化一直到任务被执行中间的最大延迟时间
//设置Content发生变化一直到任务被执行中间的延迟。如果在这个延迟时间内content发生了改变,延迟时间会重写计算。
// .setTriggerContentUpdateDelay(long durationMilimms)
.setBackoffCriteria(JobInfo.MAX_BACKOFF_DELAY_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR);
JobInfo jobinfo = builder.build();
jobScheduler.schedule(jobinfo);
}
}
}
import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
public class JobSchedulerService extends JobService {
private static final String LOG_TAG = "MyJobService";
@Override
public void onCreate() {
super.onCreate();
Log.i(LOG_TAG, "MyJobService created");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(LOG_TAG, "MyJobService destroyed");
}
// 当任务开始时会执行onStartJob(JobParameters params)方法,因为这是系统用来触发已经被执行的任务。
// 正如你所看到的,这个方法返回一个boolean值。如果返回值是false,系统假设这个方法返回时任务已经执行完毕。
// 如果返回值是true,那么系统假定这个任务正要被执行,执行任务的重担就落在了你的肩上。
// 当任务执行完毕时你需要调用jobFinished(JobParameters params, boolean needsRescheduled)来通知系统。
@Override
public boolean onStartJob(JobParameters params) {
// This is where you would implement all of the logic for your job. Note that this runs
// on the main thread, so you will want to use a separate thread for asynchronous work
// (as we demonstrate below to establish a network connection).
// If you use a separate thread, return true to indicate that you need a "reschedule" to
// return to the job at some point in the future to finish processing the work. Otherwise,
// return false when finished.
//在这里可以写你所需要的所有逻辑。因为它是运行在主线程中的,所以你可能需要使用一个单独的线程作一些异步的
//任务。如果你使用一个单独的线程,返回true来表明你这个工作可能需要在以后某个时间点才能完成,如果你知道它
//现在就完成了,就直接返回false就行了。
Log.i(LOG_TAG, "Totally and completely working on job " + params.getJobId());
// First, check the network, and then attempt to connect.
if (isNetworkConnected()) {
new SimpleDownloadTask() .execute(params);
return true;
} else {
Log.i(LOG_TAG, "No connection on job " + params.getJobId() + "; sad face");
}
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
// Called if the job must be stopped before jobFinished() has been called. This may
// happen if the requirements are no longer being met, such as the user no longer
// connecting to WiFi, or the device no longer being idle. Use this callback to resolve
// anything that may cause your application to misbehave from the job being halted.
// Return true if the job should be rescheduled based on the retry criteria specified
// when the job was created or return false to drop the job. Regardless of the value
// returned, your job must stop executing.
Log.i(LOG_TAG, "Whelp, something changed, so I'm calling it on job " + params.getJobId());
return false;
}
/**
* Determines if the device is currently online.
*/
private boolean isNetworkConnected() {
ConnectivityManager connectivityManager =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
/**
* Uses AsyncTask to create a task away from the main UI thread. This task creates a
* HTTPUrlConnection, and then downloads the contents of the webpage as an InputStream.
* The InputStream is then converted to a String, which is logged by the
* onPostExecute() method.
*/
private class SimpleDownloadTask extends AsyncTask<JobParameters, Void, String> {
protected JobParameters mJobParam;
@Override
protected String doInBackground(JobParameters... params) {
// cache system provided job requirements
mJobParam = params[0];
try {
InputStream is = null;
// Only display the first 50 characters of the retrieved web page content.
int len = 50;
URL url = new URL("https://www.baidu.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(10000); //10sec
conn.setConnectTimeout(15000); //15sec
conn.setRequestMethod("GET");
//Starts the query
conn.connect();
int response = conn.getResponseCode();
Log.d(LOG_TAG, "The response is: " + response);
is = conn.getInputStream();
// Convert the input stream to a string
Reader reader = null;
reader = new InputStreamReader(is, "UTF-8");
char[] buffer = new char[len];
reader.read(buffer);
return new String(buffer);
} catch (IOException e) {
return "Unable to retrieve web page.";
}
}
@Override
protected void onPostExecute(String result) {
jobFinished(mJobParam, false);
Log.i(LOG_TAG, result);
}
}
}
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service
android:name=".JobSchedulerService"
android:permission="android.permission.BIND_JOB_SERVICE"></service>
</application>
Last updated