一、概述
由于应用程序之间不能共享内存。在不同应用程序之间交互数据(跨进程通讯),在Android SDK中提供了4种用于跨进程通讯的方式。
这4种方式正好对应于android系统中4种应用程序组件:Activity、Content Provider、Broadcast和Service。其中Activity可以跨进程调用其他应用程序的Activity;Content Provider可以跨进程访问其他应用程序中的数据(以Cursor对象形式返回),当然,也可以对其他应用程序的数据进行增、删、改操 作;Broadcast可以向android系统中所有应用程序发送广播,而需要跨进程通讯的应用程序可以监听这些广播;Service和Content Provider类似,也可以访问其他应用程序中的数据,但不同的是,Content Provider返回的是Cursor对象,而Service返回的是Java对象,这种可以跨进程通讯的服务叫AIDL服务。
Activity
Activity的跨进程访问与进程内访问略有不同。虽然它们都需要Intent对象,但跨进程访问并不需要指定Context对象和Activity的 Class对象,而需要指定的是要访问的Activity所对应的Action(一个字符串)。有些Activity还需要指定一个Uri(通过 Intent构造方法的第2个参数指定)。
在android系统中有很多应用程序提供了可以跨进程访问的Activity,例如,下面的代码可以直接调用拨打电话的Activity。
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:12345678" ); startActivity(callIntent);
Content Provider
Android应用程序可以使用文件或SqlLite数据库来存储数据。Content Provider提供了一种在多个应用程序之间数据共享的方式(跨进程共享数据)。应用程序可以利用Content Provider完成下面的工作
1. 查询数据
2. 修改数据
3. 添加数据
4. 删除数据
虽然Content Provider也可以在同一个应用程序中被访问,但这么做并没有什么意义。Content Provider存在的目的向其他应用程序共享数据和允许其他应用程序对数据进行增、删、改操作。
Android系统本身提供了很多Content Provider,例如,音频、视频、联系人信息等等。我们可以通过这些Content Provider获得相关信息的列表。这些列表数据将以Cursor对象返回。因此,从Content Provider返回的数据是二维表的形式。
广播(Broadcast)
广播是一种被动跨进程通讯的方式。当某个程序向系统发送广播时,其他的应用程序只能被动地接收广播数据。这就象电台进行广播一样,听众只能被动地收听,而不能主动与电台进行沟通。
在应用程序中发送广播比较简单。只需要调用sendBroadcast方法即可。该方法需要一个Intent对象。通过Intent对象可以发送需要广播的数据。
Service
1.利用AIDL Service实现跨进程通信
这是我个人比较推崇的方式,因为它相比Broadcast而言,虽然实现上稍微麻烦了一点,但是它的优势就是不会像广播那样在手机中的广播较多时会有明显的时延,甚至有广播发送不成功的情况出现。
注意普通的Service并不能实现跨进程操作,实际上普通的Service和它所在的应用处于同一个进程中,而且它也不会专门开一条新的线程,因此如果在普通的Service中实现在耗时的任务,需要新开线程。
要实现跨进程通信,需要借助AIDL(Android Interface Definition Language)。Android中的跨进程服务其实是采用C/S的架构,因而AIDL的目的就是实现通信接口。
首先举一个简单的栗子。
服务端代码如下:
首先是aidl的代码:
package com.android.service; interface IData { int getRoomNum(); }
RoomService的代码如下:
package com.android.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class RoomService extends Service{ private IData.Stub mBinder=new IData.Stub() { @Override public int getRoomNum() throws RemoteException { return 3008; } }; @Override public IBinder onBind(Intent intent) { return mBinder; } }
AndroidManifest如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.aidlsampleservice" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" <service android:name="com.android.service.RoomService"> <intent-filter> <action android:name="com.aidl.service.room"/> </intent-filter> </service> </application> </manifest>
然后运行该Service所在的Project即可。
客户端代码如下:
注意客户端也要有aidl文件,所以最简单的办法就是将Service端中aidl所在的包直接复制过去。另外要注意的是在onDestroy中要解除和Service的绑定。
MainActivity.java的代码如下:
package com.example.aidlsampleclient; import com.android.service.IData; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG="MainActivity"; private static final String ROOM_SERVICE_ACTION="com.aidl.service.room"; private Button bindServiceButton; private Button getServiceButton; IData mData; private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG,"----------------onServiceConnected--------"); mData=IData.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG,"----------------onServiceDisconnected-------------"); mData=null; } }; private void initView() { bindServiceButton=(Button)findViewById(R.id.bindServiceButton); getServiceButton=(Button)findViewById(R.id.getServiceButton); bindServiceButton.setOnClickListener(this); getServiceButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { switch(v.getId()) { case R.id.bindServiceButton: bindService(); break; case R.id.getServiceButton: getService(); break; default: break; } } private void bindService() { Intent intent=new Intent(); intent.setAction(ROOM_SERVICE_ACTION); bindService(intent,conn,BIND_AUTO_CREATE); } private void getService() { try { if(mData!=null) { int roomNum=mData.getRoomNum(); showLongToast("RoomNum:"+roomNum); } } catch(RemoteException ex) { ex.printStackTrace(); } } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); } }
activity_main.xml的代码如下:
<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" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <Button android:id="@+id/bindServiceButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="BindService" /> <Button android:id="@+id/getServiceButton" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="GetService" android:layout_below="@id/bindServiceButton" /> </RelativeLayout>
运行结果如下:
然后举一个稍微复杂一点的栗子,注意如果*.aidl文件中含有自定义的对象,那么该对象的类要实现Parcelable接口,并且要新建一个该类的aidl文件,否则会出现could not find import for class com.android.service.XX的错误,其中XX为类名。还是上面的栗子,但是aidl文件中添加了一些新的方法。仍以上面的RoomService为例,
Service端的代码如下:
Room类的代码为:
package com.android.service; import android.os.Parcel; import android.os.Parcelable; public class Room implements Parcelable{ //房间号 private int roomNum; //房间大小 private float roomSpace; //是否有空调 private boolean hasAirConditioner; //是否有Wifi private boolean hasWifi; //房间内的装饰风格 private String decorativeStyle; public static final Parcelable.Creator<Room>CREATOR=new Parcelable.Creator<Room>() { @Override public Room createFromParcel(Parcel source) { return new Room(source); } @Override public Room[] newArray(int size) { return null; } }; public Room(int roomNum,float roomSpace,boolean hasAirConditioner,boolean hasWifi,String decorativeStyle) { this.roomNum=roomNum; this.roomSpace=roomSpace; this.hasAirConditioner=hasAirConditioner; this.hasWifi=hasWifi; this.decorativeStyle=decorativeStyle; } private Room(Parcel source) { roomNum=source.readInt(); roomSpace=source.readFloat(); boolean[]tempArray=new boolean[2]; source.readBooleanArray(tempArray); hasAirConditioner=tempArray[0]; hasWifi=tempArray[1]; decorativeStyle=source.readString(); } @Override public String toString() { StringBuilder sb=new StringBuilder(); sb.append("Basic info of room is as follows:\n"); sb.append("RoomNum:"+roomNum+"\n"); sb.append("RoomSpace:"+roomSpace+"\n"); sb.append("HasAirConditioner:"+hasAirConditioner+"\n"); sb.append("HasWifi:"+hasWifi+"\n"); sb.append("Decorative Style:"+decorativeStyle); return sb.toString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest,int flags) { dest.writeInt(roomNum); dest.writeFloat(roomSpace); dest.writeBooleanArray(new boolean[]{hasAirConditioner,hasWifi}); dest.writeString(decorativeStyle); } }
Room的声明为:
package com.android.service; parcelable Room;
IRoom.aidl的代码为:
package com.android.service; import com.android.service.Room; interface IRoom { Room getRoom(); }
RoomService的代码为:
package com.android.service; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class RoomService extends Service{ private IRoom.Stub mBinder=new IRoom.Stub() { @Override public Room getRoom() throws RemoteException { Room room=new Room(3008,23.5f,true,true,"IKEA"); return room; } }; @Override public IBinder onBind(Intent intent) { return mBinder; } }
由于AndroidManifest.xml的代码不变,因而此处不再贴出。下面是客户端的代码:
package com.example.aidlsampleclient; import com.android.service.IRoom; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG="MainActivity"; //private static final String SERVICE_ACTION="com.aidl.service.data"; private static final String ROOM_SERVICE_ACTION="com.aidl.service.room"; private Button bindServiceButton; private Button getServiceButton; IRoom mRoom; private ServiceConnection conn=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i(TAG,"----------------onServiceConnected--------"); showLongToast("onServiceConnected"); mRoom=IRoom.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.i(TAG,"----------------onServiceDisconnected-------------"); mRoom=null; } }; private void initView() { bindServiceButton=(Button)findViewById(R.id.bindServiceButton); getServiceButton=(Button)findViewById(R.id.getServiceButton); bindServiceButton.setOnClickListener(this); getServiceButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { switch(v.getId()) { case R.id.bindServiceButton: bindService(); break; case R.id.getServiceButton: getService(); break; default: break; } } private void bindService() { Intent intent=new Intent(); intent.setAction(ROOM_SERVICE_ACTION); bindService(intent,conn,BIND_AUTO_CREATE); } private void getService() { if(mRoom!=null) { try { showLongToast(mRoom.getRoom().toString()); } catch (RemoteException e) { e.printStackTrace(); } } } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { resent. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); } }
注意首先仍然是要将Room,IRoom的代码复制过去,否则会出错。
运行结果如下:
显然,客户端已经成功读取到服务信息。
注意,上面的所举的栗子其实不只是跨进程,还是跨应用。要注意的是,跨应用一定跨进程,但是跨进程不一定是跨应用。对于跨应用的情况,利用AIDL基本上是较好的解决了问题,但也只是“较好”而已,实际上并不完美,比如,如果要增加一个服务,如果利用AIDL的话,那么又要改写aidl文件,如果是涉及自定义对象,则还要增加自定义对象的声明,而且这种改变不只是Service端的改变,客户端也要跟着改变,显然这种解决方案不够优雅。
那么,有没有更优雅的方法呢?
当然有,那就是利用Service的onStartCommand(Intent intent, int flags, int startId)方法。
服务端代码如下:
package com.android.service; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.widget.Toast; public class RoomService extends Service{ private static final String TAG="RoomService"; private static final int CLEAN_SERVICE=0x1; private static final int ORDER_SERVICE=0x2; private static final int PACKAGE_SERVICE=0x3; private static final String SERVICE_KEY="ServiceName"; @Override public void onStart(Intent intent, int startId) { showLog("onStart"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //String action=intent.getAction(); Log.i(TAG,"onStartCommand"); int actionFlag=intent.getIntExtra(SERVICE_KEY, -1); switch(actionFlag) { case CLEAN_SERVICE: showShortToast("Start Clean Service Right Now"); break; case ORDER_SERVICE: showShortToast("Start Order Service Right Now"); break; case PACKAGE_SERVICE: showShortToast("Start Package Service Right Now"); break; default: break; } return super.onStartCommand(intent, flags, startId); } private void showLog(String info) { Log.i(TAG,info); } private void showShortToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show(); } @Override public void onDestroy() { showLog("onDestroy"); super.onDestroy(); } @Override public void onCreate() { showLog("onCreate"); super.onCreate(); } @Override public IBinder onBind(Intent intent) { showLog("onBind"); return null; } @Override public boolean onUnbind(Intent intent) { showLog("onUnbind"); return super.onUnbind(intent); } }
客户端代码如下:
package com.example.aidlsampleclient; import com.android.service.IRoom; import com.android.service.RoomService; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.util.Log; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG="MainActivity"; private static final String ROOM_SERVICE_ACTION="com.aidl.service.room"; private static final int CLEAN_SERVICE=0x1; private static final int ORDER_SERVICE=0x2; private static final int PACKAGE_SERVICE=0x3; private static final String SERVICE_KEY="ServiceName"; private Button cleanButton; private Button orderButton; private Button packageButton; private void initView() { cleanButton=(Button)findViewById(R.id.cleanButton); orderButton=(Button)findViewById(R.id.orderButton); packageButton=(Button)findViewById(R.id.packageButton); cleanButton.setOnClickListener(this); orderButton.setOnClickListener(this); packageButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { switch(v.getId()) { case R.id.cleanButton: cleanAction(); break; case R.id.orderButton: orderAction(); break; case R.id.packageButton: packageAction(); break; default: break; } } private void cleanAction() { startAction(ROOM_SERVICE_ACTION,CLEAN_SERVICE); } private void orderAction() { startAction(ROOM_SERVICE_ACTION,ORDER_SERVICE); } private void packageAction() { startAction(ROOM_SERVICE_ACTION,PACKAGE_SERVICE); } private void startAction(String actionName,int serviceFlag) { //Intent intent=new Intent(this,RoomService.class); Intent intent=new Intent(); intent.setAction(actionName); intent.putExtra(SERVICE_KEY, serviceFlag); this.startService(intent); } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { resent. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { super.onDestroy(); } }
运行结果如下:
显然,此时客户端顺利获取了服务。
上面举的是跨应用的例子,如果是在同一个应用的不同进程的话,则有更简单的实现方法。
RoomService的代码如下:
package com.android.service; import com.android.actions.Actions; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import android.widget.Toast; public class RoomService extends Service{ private static final String TAG="RoomService"; @Override public void onStart(Intent intent, int startId) { showLog("onStart"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { //String action=intent.getAction(); Log.i(TAG,"onStartCommand"); String action=intent.getAction(); if(Actions.CLEAN_ACTION.equals(action)) { showShortToast("Start Clean Service Right Now"); } else if(Actions.ORDER_ACTION.equals(action)) { showShortToast("Start Order Service Right Now"); } else if(Actions.PACKAGE_ACTION.equals(action)) { showShortToast("Start Package Service Right Now"); } else { showShortToast("Wrong action"); } return super.onStartCommand(intent, flags, startId); } private void showLog(String info) { Log.i(TAG,info); } private void showShortToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_SHORT).show(); } @Override public void onDestroy() { showLog("onDestroy"); super.onDestroy(); } @Override public void onCreate() { showLog("onCreate"); super.onCreate(); } @Override public IBinder onBind(Intent intent) { showLog("onBind"); return null; } @Override public boolean onUnbind(Intent intent) { showLog("onUnbind"); return super.onUnbind(intent); } }
MainActivity的代码如下:
package com.android.activity; import com.android.activity.R; import com.android.service.RoomService; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.widget.Button; import android.widget.Toast; import android.view.View; import com.android.actions.Actions; public class MainActivity extends Activity implements View.OnClickListener{ private static final String TAG="MainActivity"; private static final String SERVICE_KEY="ServiceName"; private Button cleanButton; private Button orderButton; private Button packageButton; private void initView() { cleanButton=(Button)findViewById(R.id.cleanButton); orderButton=(Button)findViewById(R.id.orderButton); packageButton=(Button)findViewById(R.id.packageButton); cleanButton.setOnClickListener(this); orderButton.setOnClickListener(this); packageButton.setOnClickListener(this); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } @Override public void onClick(View v) { switch(v.getId()) { case R.id.cleanButton: cleanAction(); break; case R.id.orderButton: orderAction(); break; case R.id.packageButton: packageAction(); break; default: break; } } private void cleanAction() { startAction(Actions.CLEAN_ACTION); } private void orderAction() { startAction(Actions.ORDER_ACTION); } private void packageAction() { startAction(Actions.PACKAGE_ACTION); } private void startAction(String actionName) { Intent intent=new Intent(this,RoomService.class); intent.setAction(actionName); this.startService(intent); } private void showLongToast(String info) { Toast.makeText(getBaseContext(), info, Toast.LENGTH_LONG).show(); } @Override public boolean onCreateOptionsMenu(Menu menu) { resent. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override protected void onDestroy() { super.onDestroy(); } }
从打印的log可看出,客户端每调用一次Context.startService(Intent),Service就会重新执行一次onStartCommand---->onStart;但是使用AIDL的话,绑定服务之后,不会重复执行onStart,显然后者的代价更小。
Service:前台Service,像我们经常用的天气、音乐其实都利用了前台Service来实现
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Android 进程间通信AIDL使用详解,包括了Android 进程间通信AIDL使用详解的使用技巧和注意事项,需要的朋友参考一下 一、概述 AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一
本文向大家介绍详解Android中点击事件的几种实现方式,包括了详解Android中点击事件的几种实现方式的使用技巧和注意事项,需要的朋友参考一下 在之前博文中多次使用了点击事件的处理实现,有朋友就问了,发现了很多按钮的点击实现,但有很多博文中使用的实现方式有都不一样,到底是怎么回事。今天我们就汇总一下点击事件的实现方式。 点击事件的实现大致分为以下三种: (1)Activity 实现接口方式实现
一、使用 Intent Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle 实现了 Parcelable 接口,可以在不同的进程间进行传输。 在一个进程中启动了另一个进程的 Activity,Service 和 Receiver ,可以在 Bundle 中附加要传递的数据通过 Intent 发送出去。 二、使用文件共享 Wind
本文向大家介绍详解iOS 计步器的几种实现方式,包括了详解iOS 计步器的几种实现方式的使用技巧和注意事项,需要的朋友参考一下 这篇文章介绍两种可以获取计步数据的方法,一种是采用CMPedometer获取手机计步器数据,另一种是采用HealthKit框架从手机健康App中获取计步数据。另外玩了一下写入数据到健康App。有描述不当之处,望指点。 花絮(用HealthKit框架构建app,写入数据到苹
本文向大家介绍Java多线程通信实现方式详解,包括了Java多线程通信实现方式详解的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了Java多线程通信实现方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 线程通信的方式: 1、共享变量 线程间通信可以通过发送信号,发送信号的一个简单方式是在共享对象的变量里设置信号值。线程A在一个同
本文向大家介绍Python通过4种方式实现进程数据通信,包括了Python通过4种方式实现进程数据通信的使用技巧和注意事项,需要的朋友参考一下 python提供了4种方式来满足进程间的数据通信 1. 使用multiprocessing.Queue可以在进程间通信,但不能在Pool池创建的进程间进行通信 2. 使用multiprocessing.Manager.Queue可以在Pool进程池创建的进