使用之前我们需要了解,IntentService允许在一个非UI线程完成一些后台任务,但是也有其限制:
onHandleIntent()
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
...
}
}
<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/>
1.new一个显式的Intent,把后台任务需要的数据设置进去:
mServiceIntent = new Intent(getActivity(), RSSPullService.class);
mServiceIntent.setData(Uri.parse(dataUrl));
startService()
getActivity().startService(mServiceIntent);
利用LocalBroadcastManager
,反馈执行结果到调用它的Activity,LocalBroadcastManager
,会限制Intent只发送给同一app内的组件。首先new一个包含执行结果,或者过程状态的Intent,其次通过LocalBroadcastManager.sendBroadcast()方法,把intent发送给app内的任意组件。
实例如下:
在IntentService中发送结果:
public final class Constants {
public static final String BROADCAST_ACTION =
"com.example.android.threadsample.BROADCAST";
public static final String EXTENDED_DATA_STATUS =
"com.example.android.threadsample.STATUS";
}
public class RSSPullService extends IntentService {
Intent localIntent =
new Intent(Constants.BROADCAST_ACTION)
.putExtra(Constants.EXTENDED_DATA_STATUS, status);
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
...
}
创建监听结果的广播接收者:
private class ResponseReceiver extends BroadcastReceiver
{
private DownloadStateReceiver() {
}
public void onReceive(Context context, Intent intent) {
/*
* Handle Intents here.
*/
}
}
public class DisplayActivity extends FragmentActivity {
...
public void onCreate(Bundle stateBundle) {
...
super.onCreate(stateBundle);
...
// 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");
...
FragmentActivity
,可以允许使用CursorLoader功能。
public class PhotoThumbnailFragment extends FragmentActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
...
}
LoaderManager.initLoader()
初始化查询可以在onCreate或者onCreateView中初始化
private static final int URL_LOADER_ID = 0;
public View onCreateView(LayoutInflater inflater,ViewGroup viewGroup,Bundle bundle) {
getLoaderManager().initLoader(URL_LOADER_ID, null, this);
...
}
注意:
getLoaderManager()只有在Fragment中可以调用,在FragmentActivity中需要用
getSupportLoaderManager()来获取
LoaderManager
在此方法中需要返回CursorLoader,可以在返回CursorLoader的同时就把查询的参数设定好,也可以先返回一个不带参数的CursorLoader
@Override
public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle)
{
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;
}
}
onCreateLoader方法返回非空的CursorLoader后,后台马上开始查询,当查询结束后,系统会回调onLoadFinished()方法,onLoadFinished()中的一个形参Cursor则包含了查询的结果。
除了onCreateLoader()和 onLoadFinished()
之外,还需要实现接口中的另外一个抽象方法onLoaderReset(),当CursorLoader
发现查询结果与上次相比有变化时此方法会被系统回调,同时重新做查询操作,并通过onLoadFinished()返回查询结果。
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mThemeAdapter.swapCursor(data);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
mAdapter.changeCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mThemeAdapter.swapCursor(null);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.changeCursor(null);
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true">
...
</RelativeLayout>
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
.
WAKE_LOCK
权限:
<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()释放wake lock,防止消耗系统电量。
<receiver android:name=".MyWakefulReceiver"></receiver>
下面的代码,用
startWakefulService()方法启动MyIntentService
,这个方法会让WakefulBroadcastReceiver在Service启动时申请
wake lock:
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);
}
}
当Service完成后台工作时,调用MyWakefulReceiver.completeWakefulIntent()来释放wake lock,行参intent和传入的intent是同一个。
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);
}
}
Alarm机制基于AlarmManager类:
setInexactRepeating()
而不是 setRepeating()
.因为使用setInexactRepeating(),系统会尽量把多个时间相近的alarm集中到一起触发系统,这样可以减少唤醒系统的次数,从而延长电池使用时间。ELAPSED_REALTIME
—基于系统boot后的时间轴,不会唤醒系统,包含系统睡眠时间ELAPSED_REALTIME_WAKEUP
—基于系统boot后的时间轴,会唤醒系统,然后发出pending intentRTC
—基于系统实际时间,不会唤醒系统RTC_WAKEUP
—基于系统实际时间,会先唤醒系统然后再发出pending intentalarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
AlarmManager.INTERVAL_HALF_HOUR,
AlarmManager.INTERVAL_HALF_HOUR, alarmIntent);
一分钟之后如果
系统睡眠就先唤醒系统,然后发出intent,不重复
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);
// 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);
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);
setInexactRepeating()
方法,但是这一方法有个弊端,重复时间必须用系统规定的时间,例如 INTERVAL_FIFTEEN_MINUTES
, INTERVAL_DAY等等。
if (alarmMgr!= null) { alarmMgr.cancel(alarmIntent); }