Android(4.0.3+): Service, AsyncTask, 定时任务和UI通信
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通信的更多相关文章
- Android中Activity、Service和线程之间的通信
Activity.Service和线程应该是Android编程中最常见的几种类了,几乎大多数应用程序都会涉及到这几个类的编程,自然而然的,也就会涉及到三者之间的相互通信,本文就试图简单地介绍一下这三者 ...
- Android 7.0 中 ContentProvider 实现原理
欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者:汪毅雄 导语: 本文描述了ContentProvider发布者和调用者这两在Framework层是如何实现的. 作为Android的四大 ...
- Android 5.0 行为变更
Android 5.0 除了提供诸多新特性和功能外,还对系统和 API 行为做出了各种变更.本文重点介绍您应该了解并在开发应用时加以考虑的一些主要变更. 如果您之前发布过 Android 应用,请注意 ...
- [Android分享] 如何解决Android 5.0中出现的警告:Service Intent must be explicit
Android 5.0程序运行报Service Intent must be explicit错误,原因是5.0的service必须显式调用 改成 Intent intent = new Intent ...
- 如何解决Android 5.0中出现的警告:Service Intent must be explicit
有些时候我们使用Service的时需要采用隐私启动的方式,但是Android 5.0一出来后,其中有个特性就是Service Intent must be explitict,也就是说从Lollip ...
- 解决Android 5.0中出现的警告:Service Intent must be explicit
extends:http://www.eoeandroid.com/thread-568853-1-1.html 本帖最后由 469874851 于 2015-3-11 18:15 编辑 有些时候我们 ...
- 我的Android进阶之旅------>如何解决Android 5.0中出现的警告: Service Intent must be explicit:
我的Android进阶之旅-->如何解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...
- 我的Android进阶之旅------>怎样解决Android 5.0中出现的警告: Service Intent must be explicit:
我的Android进阶之旅-->怎样解决Android 5.0中出现的警告: java.lang.IllegalArgumentException: Service Intent must be ...
- Android 8.0 启动后台service 出错 IllegalStateException: Not allowed to start service Intent
错误原因: Android 8.0 不再允许后台service直接通过startService方式去启动, 具体行为变更如下: 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况 ...
随机推荐
- linux驱动编写(电源管理驱动)
对于嵌入式设备来说,合适的电源管理,不仅可以延长电池的寿命,而且可以省电,延长设备运行时间,在提高用户体验方面有很大的好处.所以,各个soc厂家在这方面花了很多的功夫.下面,我们可以看看linux是如 ...
- 在XP系统中,如何让添加新管理员帐户和原来的管理员帐户同时存在
一.有新账户后administrator账户会自动隐藏的,如果你要用administrator账户登录的话,就机器启动到选账户那里用ctrl+alt+del软启动,然后就可以输入账户名administ ...
- 如何mount一个Isilon的NFS的file share?
命令如下: mount -o vers=3,proto=tcp 172.16.200.41:/ifs/nfsshare1 /mnt/localfolder1 简单版的 mount 192.168.1. ...
- mysql分布式数据库中间件对比
目前数据库中间件有很多,基本这些中间件在下都有了解和使用,各种中间件优缺点及使用场景也都有些心的.所以总结一个关于中间件比较的系列,希望可以对大家有帮助. 1. 什么是中间件 传统的架构模式就是 应用 ...
- 解决Android中图片圆角——.9图
目录: 一.问题概述 二..9图介绍 三..9图制作 1.开发工具 2.打开图片 3.制作图片 4.保存图片 一.问题概述 在html开发中,可以通过设置css的border-radius来设置圆角 ...
- android api doc 一
Manifest.permission 用于记录相应权限类 ACCESS_CHECKIN_PROPERTIES 允许在登入数据库的时候读写其中的属性表,并上传改变的值 ACCESS_COARSE_LO ...
- php随机生成汉字实现方法
GB 2312-80 是中国国家标准简体中文字符集,全称<信息交换用汉字编码字符集·基本集>,由中国国家标准总局发布,1981年5月1日实施.GB2312 编码通行于中国大陆:新加坡等地也 ...
- Java7,AutoClosable
Java 7的try-with-resource语法的目的是提高Java开发人员的效率,使得他们不需要在编写代码时考虑资源释放问题,大多数的这类"清理"问题是由于异常发生时清理方法 ...
- ICTCLAS中的HMM人名识别
http://www.hankcs.com/nlp/segment/ictclas-the-hmm-name-recognition.html 本文主要从代码的角度分析标注过程中的细节,理论谁都能说, ...
- Flask调试
1.AttributeError: 'NoneType' object has no attribute 'app' 原因:直接在py中调用视图函数,但没有上下文,导致出错 2.不转换html代码 { ...