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 的应用尝试在不允许其创建后台服务的情况 ...
随机推荐
- JAVA中String.format的用法 转16进制,还可以补0
1.对整数进行格式化:%[index$][标识][最小宽度]转换方式 我们可以看到,格式化字符串由4部分组成,其中%[index$]的含义我们上面已经讲过,[最小宽度]的含义也很好理解, ...
- 计算机中的概念: 视图 VS 镜像
这两个概念还是不太一样的.下面来说说个人的理解,记录一下. 1. 镜像 镜像可以理解为一份完全一样的拷贝.也就是"深度拷贝",一个复制品. 比如 iso映像文件,ubuntu-12 ...
- iOS开发-Bug锦囊
duplicate symbols for architecture armv7 今天修改了自己项目的部分代码,发现XCode编译的时候报错:duplicate symbols for archite ...
- DIV+CSS规范命名集合
我们开发CSS+DIV网页(Xhtml)时候,比较困惑和纠结的事就是CSS命名,特别是新手不知道什么地方该如何命名,怎样命名才是好的方法. 命名规则说明: 1).所有的命名最好都小写 2).属性的值一 ...
- Android -- TypedArray
当我们自定义View的时候,在给View赋值一些长度宽度的时候,一般都是在layout布局文件中进行的.,比如android:layout_height="wrap_content" ...
- POJ 2280 Amphiphilic Carbon Molecules 极角排序 + 扫描线
从TLE的暴力枚举 到 13313MS的扫描线 再到 1297MS的简化后的扫描线,简直感觉要爽翻啦.然后满怀欣喜的去HDU交了一下,直接又回到了TLE.....泪流满面 虽说HDU的时限是2000 ...
- Hibernate中得fetch
fetch ,可以设置fetch = "select" 和 fetch = "join" 用一对多来举例:fetch = "select"是 ...
- MogileFS与FastDFS的个人见解
MogileFS与FastDFS的个人见解 六月 9, 2013 1 条评论 MogileFS & FastDFS 为两个开源分布式文件系统,都主要适用于互联网文件共享,上传,下载等功能,主要 ...
- 【linux】ubuntu中上下左右键变为^[[A^[[B^[[D^[[C问题处理
问题现象: 使用上下左右键时,结果为 ^[[A^[[B^[[D^[[C,如图: 原因在于ubuntu系统自带的 vi 不完整导致. 解决方法:安装完整的vi $ sudo apt-get instal ...
- VDP
Today VMware announced a new version on their backup product vSphere Data Protection. They gave it t ...