Service使用AlarmManager实现后台定时任务

在Android 4.0.3版本中, 使用AlarmManager实现后台定时任务是比较好的方案, 其实现机制, 是利用Service的 onStartCommand() 方法, 在每次被AlarmManager唤醒后, 执行任务并注册下一次唤醒到AlarmManager. 涉及的代码

1. 新建DaemonService, 实现 onStartCommand() 方法, 在这个方法中新开线程执行任务, 并再次将AlarmReceiver注册到AlarmManager. 注: 同样的注册多次调用时, 不会注册多个, 而是会进行更新. 这个方法会在Activity中调用 startService(intent); 方法时被调用.

    @Override
public int onStartCommand(Intent intent, int flags, int startId) {
new DaemonThread().start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 5 * 1000; Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
return super.onStartCommand(intent, flags, startId);
}

2. onDestroy() 方法在调用 Activity stopService(intent) 时会被调用, 此时需要将AlarmReceiver从AlarmManager中cancel掉.

    @Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
manager.cancel(pi);
super.onDestroy();
}

3. 新建Receiver, 用来注册到AlarmManager, 用于将来响应Alarm消息. 在内部的onReceive方法中, 启动DaemonService

public class AlarmReceiver extends BroadcastReceiver {

    @Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, DaemonService.class);
context.startService(i);
}
}

4. 在AndroidManifest.xml中添加Service和Receiver

    <application
<!--... -->
<service android:name=".DaemonService" ></service>
<receiver android:name=".AlarmReceiver" ></receiver>
</application>

5. 在Activity中, 对应控件的点击响应中添加service的启动, 停止代码

    @Override
public boolean onOptionsItemSelected(MenuItem item) {
// ... if (id == R.id.action_start) {
Intent intent = new Intent(this, DaemonService.class);
startService(intent);
return true;
} if (id == R.id.action_stop) {
Intent intent = new Intent(this, DaemonService.class);
stopService(intent);
return true;
} // ... return super.onOptionsItemSelected(item);
}

定时任务中使用 AsyncTask和 httpUrlConnection访问网址, 使用Callback进行结果回调

1. 新建HttpAsyncCallback接口, 要接收AsyncTask返回数据的, 都要实现这个接口

public interface HttpAsyncCallback {
// This function will be called from inside of your AsyncTask when you are ready to callback to
// your controllers (like a fragment, for example) The object in the completionHandler will be
// whatever it is that you need to send to your controllers
void completionHandler(Boolean success, int type, Object object);
}

2. 新建HttpAsyncTask类, 进行实际的HTTP访问

public class HttpAsyncTask extends AsyncTask<String, Void, Void> {
public static final int METHOD_GET = 0;
public static final int METHOD_POST = 1; private static final String TAG = HttpAsyncTask.class.getSimpleName(); private String postData;
private int method;
private int connectTimeout;
private int readTimeout;
private String encoding; private int type;
private HttpAsyncCallback callback; public HttpAsyncTask(int method, String encoding) {
this(null, method, encoding, 10000, 10000, 0, null);
} public HttpAsyncTask(int method, String encoding, int type, HttpAsyncCallback callback) {
this(null, method, encoding, 10000, 10000, type, callback);
} public HttpAsyncTask(String postData, int method, String encoding, int type, HttpAsyncCallback callback) {
this(postData, method, encoding, 10000, 10000, type, callback);
} public HttpAsyncTask(String postData, int method, String encoding, int connectTimeout, int readTimeout, int type, HttpAsyncCallback callback) {
this.postData = postData;
this.method = method;
this.encoding = encoding;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout; this.type = type;
this.callback = callback;
} @Override
protected Void doInBackground(String... strings) {
Log.d(TAG, "Timestamp:" + System.currentTimeMillis());
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL(strings[0]).openConnection();
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
if (method == METHOD_GET) {
connection.setRequestMethod("GET");
} else {
// get请求的话默认就行了,post请求需要setDoOutput(true),这个默认是false的。
connection.setDoOutput(true);
connection.setRequestMethod("POST");
if (this.postData != null) {
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write(postData);
writer.flush();
}
} int statusCode = connection.getResponseCode();
if (statusCode == 200) {
InputStream in = connection.getInputStream();
byte[] bytes = getBytesByInputStream(in);
String response = new String(bytes, encoding);
Log.d(TAG, response);
// From here you can convert the string to JSON with whatever JSON parser you like to use
// After converting the string to JSON, I call my custom callback. You can follow this
// process too, or you can implement the onPostExecute(Result) method
// Use the response to create the object you need
if (callback != null) {
callback.completionHandler(true, type, "Timestamp:" + System.currentTimeMillis() + ", " + response);
}
} else {
Log.d(TAG, statusCode+"");
if (callback != null) {
callback.completionHandler(false, type, statusCode);
}
}
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
} finally {
if (connection != null){
connection.disconnect();
}
}
return null;
} private byte[] getBytesByInputStream(InputStream is) {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
try {
while ((length = is.read(buffer)) != -1) {
bos.write(buffer, 0, length);
}
} catch (IOException e) {
Log.e(TAG, e.getMessage());
} finally {
try {
bos.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
}
return bos.toByteArray();
} public static String formDataToString(Map<String, String> data, String encoding) {
StringBuilder sb = new StringBuilder();
String con = "";
for (String key : data.keySet()) {
String value = data.get(key);
try {
key = URLEncoder.encode(key, encoding);
value = URLEncoder.encode(value, encoding);
sb.append(con).append(key).append("=").append(value);
con = "&";
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "UnsupportedEncodingException " + encoding + " in processing:" + key);
}
}
return sb.toString();
} public static String formDataToJson(Map<String, String> data, String encoding) {
if (data != null) {
JSONObject jsonObject = new JSONObject(data);
return jsonObject.toString();
}
return null;
}
}

使用 Callback和BroadcastReceiver实现消息通信

1. 在DaemonService中实现HttpAsyncCallback接口, 用于接收HttpAsyncTask任务执行结果

public class DaemonService extends Service implements HttpAsyncCallback {
private static final String TAG = DaemonService.class.getSimpleName(); // ... @Override
public void completionHandler(Boolean success, int type, Object object) {
Log.d(TAG, "completionHandler");
}
}

2. 在DaemonService的onStartCommand()方法中, 将自己做为参数传给HttpAsyncTask. 1和2是为了将AsyncTask的结果传回Service

    @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Map<String, String> map = new HashMap<>();
map.put("phone", "13800138000");
HttpAsyncTask task = new HttpAsyncTask(HttpAsyncTask.formDataToString(map, "UTF-8"), HttpAsyncTask.METHOD_POST, "UTF-8", 0, this);
task.execute("https://www.toutiao.com/api/pc/realtime_news/");
//new DaemonThread("").start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
long triggerAtTime = SystemClock.elapsedRealtime() + 5 * 1000; Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
return super.onStartCommand(intent, flags, startId);
}

3. 为了从Service将结果传回Fragment, 需要在Fragment中注册一个BroadcastReceiver, 实现onReceive方法, 在这个方法中将结果更新到TextView, 在onCreateView中初始化这个receiver, 在onStart和onStop方法中进行注册和取消. 注意: 从fragment中获取TextView时, 需要在onActivityCreated方法中才行, 在其他的事件方法(onCreateView, onAttach中, findViewById拿到的是null

public class MainActivityFragment extends Fragment {
private static final String TAG = MainActivityFragment.class.getSimpleName();
private BroadcastReceiver receiver;
private TextView tv; ... @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String s = intent.getStringExtra("msg");
tv.setText(s);
}
};
return inflater.inflate(R.layout.fragment_main, container, false);
} @Override
public void onAttach(Context context) {
Log.d(TAG, "onAttach");
super.onAttach(context);
} @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated");
super.onActivityCreated(savedInstanceState);
tv = getActivity().findViewById(R.id.sample_text);
} @Override
public void onStart() {
Log.d(TAG, "onStart");
super.onStart();
if (receiver != null) {
IntentFilter intentFilter = new IntentFilter(MainActivityFragment.class.getName() + ".TextView");
getActivity().registerReceiver(receiver, intentFilter);
}
} @Override
public void onStop() {
Log.d(TAG, "onStop");
if (receiver != null) {
getActivity().unregisterReceiver(receiver);
}
super.onStop();
}
}

代码在 https://github.com/MiltonLai/android-service-example

Android(4.0.3+): Service, AsyncTask, 定时任务和UI通信的更多相关文章

  1. Android中Activity、Service和线程之间的通信

    Activity.Service和线程应该是Android编程中最常见的几种类了,几乎大多数应用程序都会涉及到这几个类的编程,自然而然的,也就会涉及到三者之间的相互通信,本文就试图简单地介绍一下这三者 ...

  2. Android 7.0 中 ContentProvider 实现原理

    欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者:汪毅雄 导语: 本文描述了ContentProvider发布者和调用者这两在Framework层是如何实现的. 作为Android的四大 ...

  3. Android 5.0 行为变更

    Android 5.0 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更.本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更. 如果您之前发布过 Android 应用,请注意 ...

  4. [Android分享] 如何解决Android 5.0中出现的警告:Service Intent must be explicit

    Android 5.0程序运行报Service Intent must be explicit错误,原因是5.0的service必须显式调用 改成 Intent intent = new Intent ...

  5. 如何解决Android 5.0中出现的警告:Service Intent must be explicit

    有些时候我们使用Service的时需要采用隐私启动的方式,但是Android 5.0一出来后,其中有个特性就是Service Intent  must be explitict,也就是说从Lollip ...

  6. 解决Android 5.0中出现的警告:Service Intent must be explicit

    extends:http://www.eoeandroid.com/thread-568853-1-1.html 本帖最后由 469874851 于 2015-3-11 18:15 编辑 有些时候我们 ...

  7. 我的Android进阶之旅------>如何解决Android 5.0中出现的警告: Service Intent must be explicit:

    我的Android进阶之旅-->如何解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...

  8. 我的Android进阶之旅------&gt;怎样解决Android 5.0中出现的警告: Service Intent must be explicit:

    我的Android进阶之旅-->怎样解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...

  9. Android 8.0 启动后台service 出错 IllegalStateException: Not allowed to start service Intent

    错误原因: Android 8.0 不再允许后台service直接通过startService方式去启动, 具体行为变更如下: 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况 ...

随机推荐

  1. swift3.0:NSURLSession的使用

    一.说明 NSURLSession是OC中的会话类,在Swift中变成URLSession类,它们的实现方式是一样的,下面的示例就Swift语法进行讲解和介绍. 二.介绍: URLSession 类支 ...

  2. ESP32那些事儿(六):功能开发之蓝牙及WiFi功能

    蓝牙和WIFI是ESP32的核心功能,蓝牙和wifi的共存也是esp32的独门武功,但时候后续也会发现共存的时候会有很多问题.不管怎样,蓝牙和wifi在esp-idf中有很多的例子,大家都可以参考.本 ...

  3. [leetcode]Word Search @ Python

    原题地址:https://oj.leetcode.com/problems/word-search/ 题意: Given a 2D board and a word, find if the word ...

  4. pytorch之dataloader深入剖析

    PyTorch学习笔记(6)——DataLoader源代码剖析 - dataloader本质是一个可迭代对象,使用iter()访问,不能使用next()访问: - 使用iter(dataloader) ...

  5. 构建配置 defaultConfig signingConfigs buildTypes productFlavors dependencies

    测试项目位置:https://github.com/baiqiantao/FragmentTest.git 项目结构: project 目录的 build.gradle 文件 // Top-level ...

  6. 在Asp.Net中使用SmtpMail发送邮件的方法

    在ASP中,就可以通过调用CDONTS组件发送简单邮件,在ASP.Net中,自然也可以.不同的是,.Net Framework中,将这一组件封装到了System.Web.Mail命名空间中. 一个典型 ...

  7. Laravel5.5 Jwt 1.0 beta 配置

    https://github.com/tymondesigns/jwt-auth/issues/860 1 下载开发者版本   image.png 修改composer.json,添加 "t ...

  8. Java中夏令时带来的Date不一致问题 (转)

    http://www.cnblogs.com/snake-hand/archive/2013/06/10/3131157.html 最近同事W发现使用Java Date创建日期,在不同的机器上执行,得 ...

  9. IIS 7.5: HOW TO ENABLE TLS 1.1 AND TLS 1.2

    In IIS 7.5, which is installed on Windows 2008 R2 servers, only SSL 3.0 and TLS 1.0 are enabled for ...

  10. A Complete ActiveX Web Control Tutorial

    A Complete ActiveX Web Control Tutorial From: https://www.codeproject.com/Articles/14533/A-Complete- ...