Android创建Service后台常驻服务并使用Broadcast通信

来源:互联网 发布:好听的淘宝优惠群取名 编辑:程序博客网 时间:2024/06/10 07:30

1、概述

1.1 背景

一直想玩智能小车,考虑了手机作为上位机的角色,所以还是重拾一下Android。花了半天时间搭建环境eclipse + Java + Android SDK,又花了大半天时间查了查资料,最终把这份代码给整理出来,与大家分享一下。

1.2 需求

需求非常简单:用户点击按钮,APP获得事件进行处理并反馈,处理的事件为后续再扩展。

需求分解为:
1)界面启动(Activity),监听用户点击按钮的事件;
2)界面启动后运行后台服务(Service),Service负责处理复杂逻辑事件(后续扩展);
3)后台服务不随界面退出而退出;
4)后台服务主线程执行不耗时的逻辑,子线程执行耗时操作(阻塞的操作)。

2、知识点

2.1 service

概念:Service是Android程序中四大基础组件之一,它和Activity一样都是Context的子类,只不过它没有UI界面,是在后台运行的组件。其它应用的组件可以启动一个服务运行于后台,即使用户切换到另一个应用也会继续运行[1]。

使用方法:Service对象不能自己启动,需要通过某个Activity、Service或者其他Context对象来启动。文章[2] 详细地讲述Service有三种启动方法,以下为摘要:

1) context.startService() 启动流程(后台处理工作)
只能实现启动和停止服务使用Intent进行数据传递,通过服务中的onStartCommand方法进行接受
生命周期:context.startService() -> onCreate() -> onStartCommand() -> Service running ->
    context.stopService() -> onDestroy() -> Service stop
2) context.bindService() 启动流程(在本地同一进程内与Activity交互),单向交互
生命周期:context.bindService() -> onCreate() -> onBind() -> Service running ->
    onUnbind() -> onDestroy() -> Service stop
3) 使用AIDL方式的Service(进行跨进程通信),双向交互
高级:跨进程通信时使用。


2.2 Broadcast

老罗书中提到在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制;这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用
文章[3]给出使用方法 以及Demo:
1)首先在需要发送信息的地方,把要发送的信息和用于过滤的信息(如Action、Category)装入一个Intent对象,然后通过调用 Context.sendBroadcast()、sendOrderBroadcast()或sendStickyBroadcast()方法,把 Intent对象以广播方式发送出去。
2)当Intent发送以后,所有已经注册的BroadcastReceiver会检查注册时的IntentFilter是否与发送的Intent相匹配,若匹配则就会调用BroadcastReceiver的onReceive()方法。
另外,广播注册的方式上又分为静态与动态的方式,下面的例子将使用动态注册的广播完成Activity 与 Service间通信的方法。

3、code

code阶段最大的帮助就是season同学的文章[4],这也是后续智能小车的思路,而回到本次主题《Android创建Service后台常驻服务并使用Broadcast通信》,所以做了一个精简的版本。下面对代码进行关键点的说明。

3.1 Service

使用的是startService的方式,回顾一下生命周期: context.startService() -> onCreate() -> onStartCommand() -> Service running ->context.stopService() -> onDestroy() -> Service stop
首先在onStartCommand方法里面动态注册广播,广播将用于接收Activity发送过来的命令。
/* Called when Activity startService */@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {cmdReceiver = new CommandReceiver();IntentFilter filter = new IntentFilter();filter.addAction("android.intent.action.cmdservice");
registerReceiver(cmdReceiver, filter);myStartService();return super.onStartCommand(intent, flags, startId);}
然后进入myStartService()准备开启子线程,随后的耗时操作将放置到子线程中,如串口的数据读取。
/* * Called by onStartCommand, initialize and start runtime thread  */private void myStartService() {//TODO initialize devicethreadFlag = true;mThread = new MyThread();mThread.start();}
线程类,threadFlag控制enable,现暂时放置的“耗时操作”由showToast消息提示客串呵呵。
/* * Thread runtime */public class MyThread extends Thread {        @Overridepublic void run() {super.run();// TODO runtimewhile( threadFlag ) {Log.d(TAG, "Thread pulse");showToast("Service Thread pulse");try{Thread.sleep(10000);}catch(Exception e){e.printStackTrace();}}}}
showToast其实就是发送广播给Activity,告知其在界面上显示消息内容
/* * Tell Activity to show message on screen */public void showToast(String str) {Intent intent = new Intent();intent.putExtra("cmd", CMD_SHOW_TOAST);intent.putExtra("str", str);intent.setAction("android.intent.action.cmdactivity");sendBroadcast(intent); }
同时考虑一下,Service在主线程中也是得需要接收来着Activity的命令,这时就看看CommandReceiver这个类了。
广播数据中定义了两个变量,cmd 和 value, cmd表示系统类型, value则是具体操作的数据,这时myHandlerData就可以自己发挥了,如发送串口命令到下位机...
/* * BroadcastReceiver for Activity */private class CommandReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {if ( intent.getAction().equals("android.intent.action.cmdservice") ){int cmd = intent.getIntExtra("cmd", -1);int value = intent.getIntExtra("value", -1);if ( cmd == CMD_STOP_SERVICE ) {myStopService();}else if ( cmd == CMD_SEND_DATA ) {myHandlerData(value);}}}}
然后就是Service生命周期的结束,取消广播的注册,结束进程。
其实这里也有疑问点:如何才能触发onDestroy?Activity显然是不能关闭他了。
@Overridepublic void onDestroy() {Log.i(TAG, "onDestroy()");super.onDestroy();this.unregisterReceiver(cmdReceiver);threadFlag = false;boolean retry = true;while ( retry ) {try {retry = false;mThread.join();} catch (Exception e) {e.printStackTrace();}}}

3.2 Activity

主进程通过startService(mIntent) 来开启服务,当然也定义了4个按钮来测试发送命令
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBtn1 = (Button)findViewById(R.id.button1);mBtn1.setTag(1);mBtn1.setOnClickListener(new mButtonSendClickListener());mBtn2 = (Button)findViewById(R.id.button2);mBtn2.setTag(2);mBtn2.setOnClickListener(new mButtonSendClickListener());mBtn3 = (Button)findViewById(R.id.button3);mBtn3.setTag(3);mBtn3.setOnClickListener(new mButtonSendClickListener());mBtn4 = (Button)findViewById(R.id.button4);mBtn4.setTag(4);mBtn4.setOnClickListener(new mButtonSendClickListener());mIntent = new Intent(MainActivity.this, MyService.class); startService(mIntent);Log.i(TAG, "startService");}
然后咱们先来看看发送命令到Service这一部分,先是监听点击事件,然后对应不同按钮的tag值发送不同的广播内容。
到这里仍然不清楚广播用法的话你可以参照一下文章[4]的Demo。
这里面用多个按钮共用一个监听进行处理的方式[6]。
/* * Send message to service */public void mSendBroadcast(int cmd, int value) {Intent intent = new Intent();intent.setAction("android.intent.action.cmdservice");intent.putExtra("cmd", cmd);intent.putExtra("value", value); sendBroadcast(intent);Log.d(TAG, "sendBroadcast: " + CMD_SEND_DATA + " " + value);}/* * Handle click event */public class mButtonSendClickListener implements OnClickListener{@Overridepublic void onClick(View v) {// send broadcastint cmd = CMD_SEND_DATA;int value = (Integer) v.getTag();mSendBroadcast(cmd, value);}}
/* * Send message to service */public void mSendBroadcast(int cmd, int value) {<span style="white-space:pre"></span>Intent intent = new Intent();<span style="white-space:pre"></span>intent.setAction("android.intent.action.cmdservice");<span style="white-space:pre"></span>intent.putExtra("cmd", cmd);<span style="white-space:pre"></span>intent.putExtra("value", value); <span style="white-space:pre"></span>sendBroadcast(intent);<span style="white-space:pre"></span>Log.d(TAG, "sendBroadcast: " + CMD_SEND_DATA + " " + value);}
另外不要忘了等待Service的消息回馈,先注册一个广播
@Overrideprotected void onResume() {super.onResume();Log.i(TAG, "onResume");mReceiver = new MyReceiver();IntentFilter mFilter=new IntentFilter();mFilter.addAction("android.intent.action.cmdactivity");MainActivity.this.registerReceiver(mReceiver, mFilter);}
监听方法,主要就是对接收到Service的字符串进行显示
/* * BroadcastReceiver for Service */public class MyReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {if(intent.getAction().equals("android.intent.action.cmdactivity")){Bundle bundle = intent.getExtras();int cmd = bundle.getInt("cmd");if(cmd == CMD_SHOW_TOAST){<span style="white-space:pre"></span>String str = bundle.getString("str");<span style="white-space:pre"></span>myShowToast(str);}else if(cmd == CMD_SYSTEM_EXIT){System.exit(0);}}}}
/* * Show message on screen */public void myShowToast(String str) {<span style="white-space:pre"></span>Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show();<span style="white-space:pre"></span>}

4、结束

界面的效果图如下,这次先简单地将框架准备好,方便下次进行扩展。


Broadcast主要就是将数据插入到Intent对象中进行发送、接收。
这次使用startService将启动在后台服务一直执行,后续检查是否会造成CPU浪费,是否要改回Bind的方式。

工程下载地址:http://download.csdn.net/detail/stayneckwind2/8610921

参考文章:
[1] Android Service 详解, http://www.cnblogs.com/mengdd/archive/2013/03/24/2979944.html
[2] 浅谈Service, http://www.2cto.com/kf/201504/390385.html
[3] Android之Broadcast, BroadcastReceiver , http://www.cnblogs.com/playing/archive/2011/03/23/1992030.html
[4] Android 与Arduino蓝牙串口的通信设计, http://blog.csdn.net/cen616899547/article/details/6728040
[5] Broadcast简单实例, http://blog.csdn.net/xyylchq/article/details/6824992
[6] Android多个按钮的处理方法,  http://blog.csdn.net/woshixuye/article/details/8331335
0 0
原创粉丝点击