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 的应用尝试在不允许其创建后台服务的情况 ...
随机推荐
- ArcEngine C++ 10 程序的运行环境,ArcEngine RT的授权
以前我一直以为 必须安装 Arcgis Desktop才可以授权,发现我错了,原来是这个样子的. 一.安装License manager,并授权许可server.txt 当然这个license也可以安 ...
- Chart:ECharts
ylbtech-Chart:ECharts ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome, ...
- 【虚拟化实战】Cluster设计之一资源池
作者:范军 (Frank Fan) 新浪微博:@frankfan7 资源池是Cluster设计中的一个重要概念,本文介绍了为什么用资源池,怎么用好资源池,以及澄清了一些常见的误区. 一概念 每个ESX ...
- 首个threejs项目-前端填坑指南【转】
http://www.cnblogs.com/pursues/p/5226807.html 第一次使用threejs到实际项目中,开始的时候心情有点小激动,毕竟是第一次嘛,然而做着做着就感受到这玩意水 ...
- 7个提高效率的JavaScript调试工具
现在的JavaScript事实上已然成为了流行的web语言,即使它并不完美.很多程序员不喜欢用JavaScript写代码,是因为写到后来总会出现各种莫名其妙的bug,而且在开发大型应用程序的过程中很容 ...
- Socket请求和Http请求的各自特点、区别及适用场景 (转)
http://blog.csdn.net/hexinli/article/details/50500316 Socket实现服务器与客户端之间的物理连接,并进行数据传输.主要有TCP/UDP两个协议. ...
- (转)Unity3D研究院之将场景导出XML或JSON或二进制并且解析还原场景
自:http://www.xuanyusong.com/archives/1919 导出Unity场景的所有游戏对象信息,一种是XML一种是JSON.本篇文章我们把游戏场景中游戏对象的.旋转.缩放.平 ...
- eclipse中的项目鼠标右键卡死
1.错误描写叙述 在eclipse中部署了Java Web项目,想在WebContent目录下新建一个目录,鼠标右键时出现eclipse卡死的想象 2.错误原因 (1)插件安装过多 (2)导入的项目过 ...
- 什么是哈希码(HashCode)
什么是哈希码(HashCode) 在Java中,哈希码代表对象的特征. 例如对象 String str1 = “aa”, str1.hashCode= 3104 String str2 = “bb”, ...
- [Functional Programming] Create Reusable Functions with Partial Application in JavaScript
This lesson teaches you how arguments passed to a curried function allow us to store data in closure ...