Activity和Service的交互方式
来源:互联网 发布:为啥女生干java反应慢 编辑:程序博客网 时间:2024/05/22 00:10
在开发过程中,经常会遇到Activity和Service进行相互通信、交换数据的需要,最常见的比如音乐播放器,使用Service在后台进行音乐播放,前台使用Activity显示界面,点击前台控件后需要告知Service,控制音乐的播放、暂停、切换下一首等,后台Service再将数据传给Activity来改变界面显示
Activity和Service的交互方式主要有以下几种
- 通过广播进行交互
- 通过共享文件
- Messenger
- AIDL
下面分别使用几种交互方式来实现一个计时器的程序,程序界面只有一个Button,Button上显示一个数字,点击Button后开始计时,每隔1秒,Button上数据加1,使用Service来实现计时的功能
布局文件很简单
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" /></RelativeLayout>
1、通过广播交互
在Activity中点击Button后启动Service
public void onClick(View v) { Intent intent = new Intent(this, CounterService.class); intent.putExtra("counter", counter); //counter用来计数 startService(intent); }
CounterService.java
public class CounterService extends Service { int counter; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub counter = intent.getIntExtra("counter", 0); new Timer().schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub Intent counterIntent = new Intent(); counterIntent.putExtra("counter", counter); counterIntent.setAction("com.example.counter.COUNTER_ACTION"); sendBroadcast(counterIntent); counter++; } }, 0, 1000); return START_STICKY; }}
在Service的onStartCommand()中启动一个定时器,每隔1秒钟counter计数加1,通过广播发送将counter发送出去,在Activity中收到广播后取出counter,将counter设置到Button上
广播接收器,定义在Activity中
class CounterReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub counter = intent.getIntExtra("counter", 0); start.setText(counter + ""); } }
运行程序,点击按钮开始计时
在程序运行过程遇到一个问题,在这里说明一下,广播类是在Activity里定义的,是Activity的内部类,这个内部类在使用静态注册的时候,会发生程序运行崩溃,原因是内部广播类如果使用静态注册,必须是静态内部类,但是如果是静态内部类,只能访问外部类的静态成员变量,所以内部广播类推荐使用动态注册方式,而且这类广播一般只在程序内部使用,没有必须在进程结束以后继续接收广播
通过广播实现Activity和Service的交互简单容易实现,缺点是发送不广播受系统制约,系统会优先发送系统级的广播,自定义的广播接收器可能会有延迟,在广播里也不能有耗时操作,否则会导致程序无响应
2、通过共享文件
共享文件就是通过读写同一个文件来进行通信,使用这种方式通信时,同一时间只能一方写,一方读,不能两方同时写,这里使用SharedPreferences来实现Activity和Service的交互
客户端点击Button启动Service
public void onClick(View v) { Intent intent = new Intent(this, CounterService.class); intent.putExtra("counter", counter); //counter用来计数 startService(intent); }
CounterService.java
public class CounterService extends Service { int counter; SharedPreferences sharedPreferences; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return null; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub counter = intent.getIntExtra("counter", 0); sharedPreferences = getSharedPreferences("counter_preferences", Context.MODE_PRIVATE); new Timer().schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub sharedPreferences.edit().putInt("counter", counter).commit(); counter++; } }, 0, 1000); return START_STICKY; }}
在Service中同样启动一个定时器,每秒将计数加1,然后写入到SharedPreferences中
在Activity中也需要启动一个定时任务,从SharedPreferences中读取计数
sharedPreferences = getSharedPreferences("counter", Context.MODE_PRIVATE); new Timer().schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub counter = sharedPreferences.getInt("counter", 0); handler.sendEmptyMessage(0x123); } }, 0, 1000);
在Activity的onCreate()中启动定时器,每隔1秒读取一次数据,由于在子线程中是无法更新UI的,所以通过handler发送一条消息到主线程中更新
Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { if(msg.what == 0x123) { start.setText(counter + ""); } }; };
程序运行结果和上图相同
使用SharedPreferences进行数据共享对文件格式没有要求,只要读写双方约定好数据格式即可。但是也有局限性,在面对高并发的读写时,这种方式就变得不可靠,很可能会导致读写的数据不一致,所以不建议使用这种方式来进行通信
3、Messenger
Messenger的意思可以译为信使,通过它可以在不同进程间传递Meesage对象,Messenger是一种轻量级的IPC方案,底层是用AIDL实现的
使用Messenger在Activity和Service之间进行数据传输的步骤如下;
1、在Service端创建信使对象
创建Messenger需要传入一个Handler对象,所以首先要新建一个Handler,利用Handler来创建信使
@Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); mMessenger = new Messenger(handler); }
2、Service端的onBind()方法使用mMessenger.getBinder()返回一个binder对象
@Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return mMessenger.getBinder(); }
3、客户端绑定到Service,在onServiceConnected()方法中使用Service返回的IBinder对象创建Messenger对象,通过这个Messenger对象就可以向Service发送消息了。
ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName name, android.os.IBinder service) { rMessenger = new Messenger(service); }; public void onServiceDisconnected(ComponentName name) { }; };
这样只是实现了客户端向Service发送消息,如果需要Service可以将相应客户端,同样的需要在客户端使用Handler来创建Messenger对象,通过Message将这个Messenger传到Service中,Service获取到客户端的Messenger对象后,也可以向客户端发送消息。
通过Messenger来实现上面的功能
Service端的代码CounterService.java
public class CounterService extends Service { int counter; Messenger mMessenger, cMessenger; //Service的信使对象和客户端的信使对象 Handler handler = new Handler() { public void handleMessage(Message msg) { cMessenger = msg.replyTo; //获取Message中的客户端信使对象 counter = msg.arg1; //获取Message中的计数 new Timer().schedule(new TimerTask() { @Override public void run() { // TODO Auto-generated method stub Message message = Message.obtain(); message.arg1 = counter; try { cMessenger.send(message); //通过客户端的信使对象向客户端发送消息,消息中保存counter } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } counter++; } }, 0, 1000); }; }; @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return mMessenger.getBinder(); } @Override public void onCreate() { // TODO Auto-generated method stub super.onCreate(); mMessenger = new Messenger(handler); //初始化Service信使 }}
客户端代码MainActivity.java
public class MainActivity extends Activity { Button start; int counter = 0; Messenger rMessenger, mMessenger; //远程Service端的信使对象和客户端本地的信使对象 Handler handler = new Handler() { public void handleMessage(Message msg) { counter = msg.arg1; //获取Service消息中的计数 start.setText(counter + ""); }; }; ServiceConnection connection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { rMessenger = new Messenger(service); //使用Service返回的IBinder对象初始化Service端信使对象 mMessenger = new Messenger(handler); //初始化本地客户端信使对象 Message message = Message.obtain(); message.replyTo = mMessenger; //将客户端的信使对象保存到message中,通过Service端的信使对象发送给Service message.arg1 = counter; try { rMessenger.send(message); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } }; public void onServiceDisconnected(ComponentName name) { rMessenger = null; }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); start = (Button)findViewById(R.id.start); start.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Intent intent = new Intent(MainActivity.this, CounterService.class); bindService(intent, connection, BIND_AUTO_CREATE); } }); }}
代码中的注释很详细,点击按钮后,客户端绑定到Service,通过Service中返回的IBinder对象创建Service端的信使对象,然后将客户端本地的信使对象和计数变量通过Message发送到Service中。
在Service中获取到客户端发送过来的消息后,取出信息中的Messenger,这样Service中就有了客户端的信使对象,就可以向客户端发送消息,这样就实现了双向通信。
程序运行效果和前面两个程序一样
4、使用AIDL进行通信
AIDL属于Android的IPC机制,常用于跨进程通信,主要实现原理基于底层Binder机制,使用AIDL Service实现进程间通信在另一篇博客http://blog.csdn.net/zh175578809/article/details/71915238中有详细介绍,这里就不再阐述
- Activity和Service的交互方式
- activity和service的交互
- activity和service的交互
- Activity和Service的交互
- android service与Activity的交互方式
- Activity和Service交互
- andorid service activity交互方式
- android activity和service的交互介绍
- Activity和Service之间的交互
- Service和Activity之间的交互
- Service和Activity之间的交互
- android activity和service的交互介绍
- Android中Activity与Service的交互方式
- Activity和服务交互的方式
- activity和fragment的交互方式
- Service Activity三种交互方式
- android Service Activity三种交互方式
- android Service Activity三种交互方式
- linux对于特定文件夹删除
- <hr>分割线样式
- GitHub 使用markdown格式的三个坑
- 关于在Centos中设置jdk、maven等参数不生效的解决方法
- cvc-complex-type.2.3: 元素 'beans' 必须不含字符 [子级], 因为该类型的内容类型为“仅元素”。
- Activity和Service的交互方式
- Python学习笔记(1) -- Python内建函数文档
- 数据库范式笔记
- ul li 居中排列
- <fieldset>的样式
- 静态代码块,构造代码块,构造方法的执行顺序
- 170604 逆向-CrackMe之013
- SVG 绘制可交互的中国地图
- windows下解压版MYSQL的配置