a.创建IntentService,我们创建它的子类:
<span style="font-size:14px;">public class RSSPullService extends IntentService { @Override protected void onHandleIntent(Intent workIntent) { // Gets data from the incoming Intent String dataString = workIntent.getDataString(); ... // Do work here, based on the contents of dataString ... } }</span>还有其他IntentService中的回调方法,如onStartCommand()等是被系统自动回调的,我们不要再重写这些方法。
b.在Manifest中注册:
<span style="font-size:14px;"><application android:icon="@drawable/icon" android:label="@string/app_name"> ... <!-- Because android:exported is set to "false", the service is only available to this app. --> <service android:name=".RSSPullService" android:exported="false"/> ... <application/></span>注意:这里的service的启动是通过显示intent启动的,所以不要添加 intent filter
c.启动IntentService:可以在activity或者fragment中来启动
<span style="font-size:14px;">/* * Creates a new Intent to start the RSSPullService * IntentService. Passes a URI in the * Intent's "data" field. */ mServiceIntent = new Intent(getActivity(), RSSPullService.class); mServiceIntent.setData(Uri.parse(dataUrl)); // Starts the IntentService getActivity().startService(mServiceIntent); </span>d.回调结果给启动该服务的activity或者fragment:
<span style="font-size:14px;">public final class Constants { ... // Defines a custom Intent action public static final String BROADCAST_ACTION = "com.example.android.threadsample.BROADCAST"; ... // Defines the key for the status "extra" in an Intent public static final String EXTENDED_DATA_STATUS = "com.example.android.threadsample.STATUS"; ... } public class RSSPullService extends IntentService { ... /* * Creates a new Intent containing a Uri object * BROADCAST_ACTION is a custom Intent action */ Intent localIntent = new Intent(Constants.BROADCAST_ACTION) // Puts the status into the Intent .putExtra(Constants.EXTENDED_DATA_STATUS, status); // Broadcasts the Intent to receivers in this app. LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent); ... }</span>
<span style="font-size:14px;">// Broadcast receiver for receiving status updates from the IntentService private class DownloadStateReceiver extends BroadcastReceiver { // Prevents instantiation private DownloadStateReceiver() { } // Called when the BroadcastReceiver gets an Intent it's registered to receive @ public void onReceive(Context context, Intent intent) { ... /* * Handle Intents here. */ ... } }</span>
<span style="font-size:14px;"> // The filter's action is BROADCAST_ACTION IntentFilter mStatusIntentFilter = new IntentFilter( Constants.BROADCAST_ACTION); // Adds a data filter for the HTTP scheme mStatusIntentFilter.addDataScheme("http"); // Instantiates a new DownloadStateReceiver DownloadStateReceiver mDownloadStateReceiver = new DownloadStateReceiver(); // Registers the DownloadStateReceiver and its intent filters LocalBroadcastManager.getInstance(this).registerReceiver( mDownloadStateReceiver, mStatusIntentFilter);</span>
<span style="font-size:14px;">/* * Initializes the CursorLoader. The URL_LOADER value is eventually passed * to onCreateLoader(). */ getLoaderManager().initLoader(URL_LOADER, null, this);</span>b.重写一些回调方法
<span style="font-size:14px;"> /* * Callback that's invoked when the system has initialized the Loader and * is ready to start the query. This usually happens when initLoader() is * called. The loaderID argument contains the ID value passed to the * initLoader() call. */ @Override public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle) { /* * Takes action based on the ID of the Loader that's being created */ switch (loaderID) { case URL_LOADER: // Returns a new CursorLoader return new CursorLoader( getActivity(), // Parent activity context mDataUrl, // Table to query mProjection, // Projection to return null, // No selection clause null, // No selection arguments null // Default sort order ); default: // An invalid id was passed in return null; } } /* * Defines the callback that CursorLoader calls * when it's finished its query */ @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { ... /* * Moves the query results into the adapter, causing the * ListView fronting this adapter to re-display */ mAdapter.changeCursor(cursor); }</span>
当手机处于闲置,然后熄灭屏幕后,系统会基本上关闭CPU,来防止电池的电量被快速耗尽。但是如果我们在这种情况下需要依然进行一些操作,例如在播放movies时保证屏幕一直处于亮的状态;例如不必要一定屏幕一直亮着,但是要保证cpu一直工作。
a.保证屏幕亮屏:
在activity中调用 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
或者在activity的布局文件的根布局中使用 android:keepScreenOn="true"
一般情况下,你不必程序调用来清除该标记。
如果你有这个需求的话,例如间隔多长时间后清除该标记getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON).
b.保证cpu一直工作:PowerManager
我们只有在极端情况下才这么做,而且保证不要持有它们太长时间,因为这会非常影响电池的使用寿命。
需要权限:<uses-permission android:name="android.permission.WAKE_LOCK" />
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MyWakelockTag");
wakeLock.acquire();
在你的后台任务执行完后,尽快的wakelock.release().
或者使用WakefulBroadcastReceiver,
使用这个广播启动一个后台服务,在服务结束后 MyWakefulReceiver.completeWakefulIntent(intent);释放CPU
public class MyWakefulReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Start the service, keeping the device awake while the service is
// launching. This is the Intent to deliver to the service.
Intent service = new Intent(context, MyIntentService.class);
startWakefulService(context, service);
}
}
public class MyIntentService extends IntentService {
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public MyIntentService() {
super("MyIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
Bundle extras = intent.getExtras();
// Do the work that requires your app to keep the CPU running.
// ...
// Release the wake lock provided by the WakefulBroadcastReceiver.
MyWakefulReceiver.completeWakefulIntent(intent);
}
}
4.使用AlarmManager:进行后台定时循环操作
可以让你的app独立于application之外。你可以使用它来实现例如每天都要执行一个服务获取天气。
闹钟有如下特点:
可以让你使用广播启动服务或其他操作、
可以让你的操作独立于application,甚至即使手机是休眠的。
可以让你不必运行后台service,减少app的资源消耗的同时安排任务。
我们一般有这个需求,在后台每隔一段时间与服务器同步数据。下面是一些建议
a.随机触发网络请求,不要在某一个具体的时间来请求,减少服务器压力
b.保证alarm的频率中等
c.非必要下不要唤醒设备,选择alarm type
选择闹钟类型:
有2个通常使用的闹钟,elapsed real time基于手机开机的时间
eal time clock uses 基于UTC,实时时间,与时区有关
一般推荐使用elapsed real time
闹钟有这些类型:
ELAPSED_REALTIME,基于时间的逃逸数量,以手机启动开始,当手机休眠时也可以,但是不会唤醒手机
ELAPSED_REALTIME_WAKEUP,与上一个类似,但是会唤醒手机
RTC,在指定时刻,不会唤醒手机
RTC_WAKEUP,与上一个类似,会唤醒手机
下面列举少许例子说明:
<span style="font-size:14px;"> ELAPSED_REALTIME_WAKEUP说明 30分钟后执行,之后每隔30分钟执行一次、 // Hopefully your alarm will have a lower frequency than this! alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, AlarmManager.INTERVAL_HALF_HOUR, AlarmManager.INTERVAL_HALF_HOUR, alarmIntent); 只执行一次 private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmReceiver.class); alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0); alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 60 * 1000, alarmIntent); RTC_WAKEUP. 说明 // Set the alarm to start at approximately 2:00 p.m. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 14); // With setInexactRepeating(), you have to use one of the AlarmManager interval // constants--in this case, AlarmManager.INTERVAL_DAY. alarmMgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, alarmIntent); //Wake up the device to fire the alarm at precisely 8:30 a.m., and every 20 minutes thereafter: private AlarmManager alarmMgr; private PendingIntent alarmIntent; ... alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, AlarmReceiver.class); alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0); // Set the alarm to start at 8:30 a.m. Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY, 8); calendar.set(Calendar.MINUTE, 30); // setRepeating() lets you specify a precise custom interval--in this case, // 20 minutes. alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60 * 20, alarmIntent); </span>
<span style="font-size:14px;">取消闹钟: // If the alarm has been set, cancel it. if (alarmMgr!= null) { alarmMgr.cancel(alarmIntent); }</span>
<span style="font-size:14px;">使用步骤: <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> public class SampleBootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) { // Set the alarm here. } } } <receiver android:name=".SampleBootReceiver" android:enabled="false"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED"></action> </intent-filter> </receiver> 这个广播有些特别,一旦你启动了它,即使手机重启了,也还会存在的。 我们使能开启: ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); 在需要的时候关闭: ComponentName receiver = new ComponentName(context, SampleBootReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);</span>