1. 使用Bundle
- 由于Bundle实现了Parcelable接口,所以在四大组件中的三大组件(Activity, Service, Receiver)都支持在Intent中传递Bundle.就可以在Bundle中附加我们需要的信息通过Intent发送出去. 当然传递的类型必须是能够被序列化的,
- 这个地方有一个疑问,intent也可以传输数据啊,为啥还要用bundle呢?
- 解释是:bundle的内部实现是一个hashMap.一个容器将要传输的数据装起来然后做传输,如果使用intent则需要一个一个传输,如果此数据需要从A传到B然后传入C,那么用bundle就很方便了,而对于intent则还需要在B取出,然后在转入..
- 由于Bundle使用很简单就不展示代码了;
2. 使用文件共享
- 原理:两个进程通过读/写同一个文件来交换数据. 例如进程A把数据写入文件中,而进程B从文件中读取出来数据.
- 需要注意的点:文件共享适合在对数据同步要求不高的进程之间进行通信,并且要妥善的处理并发读写的问题.
- SharePreferencess是Android提供的一个轻量级方案,通过键值对存储数据,底层采用XML文件来进行存储. 存储路径/data/data/package name/shared_prefs目录下. 也属于文件的一种,但是由于系统对SP的读写存在一定的缓存策略,内存中会有一份缓存,所以多进程下,系统对它的读写也就变得不可靠.
- 文件共享,使用sp就是比较好的例子,sp的使用也比较简单就不列出了
3. 使用Messenger
Messenger(信使). 不同的进程中可以传递Message对象, 在Message中放入我们需要传递的数据,就可实现进程间传递. Messenger是一种轻量级的IPC方案,它的底层实现AIDL.
-
具体实现,共分为四部:
-
客户端进程创建两个 Messenger,一个 Sender ,一个 Receiver;
private Messenger mGetReplyMessenger = new Messenger(new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 0: Log.d(TAG, "handleMessage: 这里是客户端:::"+msg.getData().getString("reply")); break; default: super.handleMessage(msg); } } }); private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mMessenger = new Messenger(iBinder); Message message = Message.obtain(null, 1); Bundle bundle = new Bundle(); bundle.putString("msg","wo shi 客户端"); message.setData(bundle); message.replyTo= mGetReplyMessenger; try { mMessenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } };
-
绑定服务,由于在不同的应用中所以采用隐士启动方式:
Intent intent = new Intent(); intent.setAction("com.lemon.service"); intent.setComponent(new ComponentName("com.lemon.messgagertest","com.lemon.messgagertest.MessagerService")); bindService(intent, mConnection,BIND_AUTO_CREATE);
注意:采用隐士启动的时候,需要设置 intent.setComponent(),不然会报错,找不到;
-
服务端,接受信息,然后处理,然后返回:
private Messenger mMessenger = new Messenger(new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1: Log.e(TAG,"receive msg from client "+msg.getData().getString("msg")); Messenger client = msg.replyTo; Message message = Message.obtain(null, 0); Bundle bundle = new Bundle(); bundle.putString("reply","消息已收到,稍后回复"); message.setData(bundle); try { client.send(message); } catch (RemoteException e) { e.printStackTrace(); } break; default: super.handleMessage(msg); } } });
-
配置xml文件:
<service android:name=".MessagerService" android:process=":remote"> <intent-filter> <action android:name="com.lemon.service"></action> </intent-filter> </service>
-
Messenger使用很简单,但是有局限性,因为Messenger对AIDL进行了封装,使得在使用时更加简单,并且它的处理方式是一次处理一个请求,因此服务器端不用考虑线程同步因为服务端不存在并发执行的情形.
在使用Messenger进行数据传递必须将数据放入到Message中. 而Messenger和Message都实现了序列化接口. 所以可以在进程间通信.
4. 使用AIDL
虽然Messenger使用方便, 但是要清楚它是以串行的方式处理客户端发来的消息,如果有大量并发的请求. 或者需求是跨进程调用服务端的方法时. 就无法使用Messenger. 这个时候就该AIDL
-
对于使用AIDL的流程简单梳理一遍
- 服务端
- 服务端创建一个Service用来监听客户端的连接请求, 然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口并在onBind()返回即可.
- 客户端
- 绑定服务端的Service,绑定成功后,将服务端返回来的Binder对象转成AIDL接口所属的类型,接着就可以直接调用AIDL中的方法了.
- 服务端
-
具体的实现
-
AIDL 文件
Book.aidl
package com.lemon.bookaidl; parcelable Book;
IBookManger.aidl
package com.lemon.bookaidl; // Declare any non-default types here with import statements import com.lemon.bookaidl.book; import com.lemon.bookaidl.IOnBookArrivedListener; interface IBookManger { List<Book> getBookList(); void addBook(in Book book); void registerListener(IOnBookArrivedListener listener); void unregisterListener(IOnBookArrivedListener listener); }
IOnBookArrivedListener.aidl
package com.lemon.bookaidl; // Declare any non-default types here with import statements import com.lemon.bookaidl.book; interface IOnBookArrivedListener { void IOnBookArriverListener(in Book book); }
-
服务端:
public class BookMangerService extends Service { private static final String TAG = "BookMangerService"; private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false); private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>(); private RemoteCallbackList<IOnBookArrivedListener> mListeners = new RemoteCallbackList<>(); private Binder mBinder = new IBookManger.Stub() { @Override public List<Book> getBookList() throws RemoteException { return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); } @Override public void registerListener(IOnBookArrivedListener listener) throws RemoteException { mListeners.register(listener); } @Override public void unregisterListener(IOnBookArrivedListener listener) throws RemoteException { mListeners.unregister(listener); } }; @Nullable @Override public IBinder onBind(Intent intent) { return mBinder; } @Override public void onCreate() { super.onCreate(); mBookList.add(new Book(1,"Android")); mBookList.add(new Book(2, "Ios")); new Thread(new serviceWork()).start(); } private class serviceWork implements Runnable { @Override public void run() { while (!mIsServiceDestoryed.get()){ try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } int bookId = mBookList.size()+1; Book newBook = new Book(bookId,"#new Book"+bookId); try { onNewBookArrived(newBook); } catch (RemoteException e) { e.printStackTrace(); } } } } private void onNewBookArrived(Book newBook) throws RemoteException { mBookList.add(newBook); final int N = mListeners.beginBroadcast(); Log.e("onNewBookArrived","registener listener size:" + N); for (int i = 0 ;i<N ;i++){ IOnBookArrivedListener l = mListeners.getBroadcastItem(i); if (l!=null){ l.IOnBookArriverListener(newBook); } } mListeners.finishBroadcast(); } @Override public void onDestroy() { mIsServiceDestoryed.set(true); super.onDestroy(); } }
-
-
客户端
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int MESSAGE_NEW_BOOK_ARRIVED = 1; private IBookManger mIBookManger; private android.os.Handler mHandler = new android.os.Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case MESSAGE_NEW_BOOK_ARRIVED: Log.e(TAG, "received new book:" + msg.obj); break; default: super.handleMessage(msg); } } }; private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { mIBookManger = IBookManger.Stub.asInterface(iBinder); try { List<Book> bookList = mIBookManger.getBookList(); Log.e(TAG, "query book list,list type:" + bookList.getClass().getCanonicalName()); Log.e(TAG, "query book list:" + bookList.toString()); Book newBook = new Book(3, "android进阶"); mIBookManger.addBook(newBook); Log.e(TAG, "add book:" + newBook); List<Book> newList = mIBookManger.getBookList(); Log.e(TAG, "query book list:" + newList.toString()); mIBookManger.registerListener(mIOnBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { } }; private IOnBookArrivedListener mIOnBookArrivedListener = new IOnBookArrivedListener.Stub() { @Override public void IOnBookArriverListener(Book book) throws RemoteException { mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,book).sendToTarget(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindService(); } private void bindService() { Intent intent = new Intent(); intent.setAction("com.lemon.bookservice"); intent.setComponent(new ComponentName("com.lemon.bookaidl","com.lemon.bookaidl.BookMangerService")); bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { if (mIBookManger != null && mIBookManger.asBinder().isBinderAlive()){ Log.e(TAG, "unregister listener:" + mIOnBookArrivedListener); try { mIBookManger.unregisterListener(mIOnBookArrivedListener); } catch (RemoteException e) { e.printStackTrace(); } } unbindService(mServiceConnection); super.onDestroy(); } }
-
几个点得理解:
- CopyOnWriteArrayList:支持并发的读写,这里我们使用它来进行自动的线程同步;
- RemoteCallBackList:是系统专门提供的用于删除跨进程listener的接口:它的工作原理其实很简单:在它的内部有一个Map结构专门用来保存所有AIDL回调3. 这个例子涉及到的东西比较多,如果刚接触可以参考点简单的例子,就实现个远程服务进行计算,而client获取到结果的这种,实现特别简单,主要看你怎么理解
5. 使用ContentProvider
- ContentProvider是Android提供专门用于不用应用进行数据共享的方式. 它的底层同样也是Binder. 因为系统封装, 所以它的使用比起AIDL要简单很多.
- 要实现一个内容提供者, 只需要写一个类继承ContentProvider,并复写六个抽象方法. 其中有四个是CURD操作方法. 一个onCreate()用来做初始化. 一个getType()用来返回一个Uri请求所对应的MIME类型,比如图片还是视频等. 如果我们不关心那么可是直接返回NULL或者
6. 使用Socket
- Socket也称为套接字. 是网络通信中的概念, 它分为流式套接字和用户数据包套接字两种. 分别对应于网络的传输控制层中TCP和UDP协议.
客户端:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG="MainActivity";
private Button mSend;
private EditText mInput;
private TextView mMessage;
private Socket mClientSocket;
private PrintWriter mPrintWriter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
mMessage = (TextView) findViewById(R.id.tv_message);
mInput = (EditText) findViewById(R.id.et_input);
mSend = (Button) findViewById(R.id.bt_send);
mSend.setOnClickListener(this);
Intent service = new Intent(this, SocketService.class);
startService(service);
new Thread(){
@Override
public void run() {
connectTCPserver();
}
}.start();
}
private void connectTCPserver() {
Socket socket=null;
while (socket==null) {
try {
socket = new Socket("localhost", 8888);
mClientSocket = socket;
mPrintWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
} catch (IOException e) {
SystemClock.sleep(1000);
}
}
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (!isFinishing()){
String msg = br.readLine();
if (msg!=null){
String time = formateDateTime(System.currentTimeMillis());
Log.e(TAG,"客户端收到信息:"+msg+" at:"+time);
}
}
mPrintWriter.close();
br.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String formateDateTime(long l) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(l));
}
@Override
public void onClick(View view) {
if (view== mSend) {
final String msg = mInput.getText().toString();
if (!TextUtils.isEmpty(msg) && mPrintWriter != null) {
//像服务器发送信息
Log.e(TAG,"client has send '" + msg + "' at " + formateDateTime(System.currentTimeMillis()));
mPrintWriter.println(msg);
mInput.setText("");
}
}
}
@Override
protected void onDestroy() {
if (mClientSocket != null) {
try {
mClientSocket.shutdownInput();
mClientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
服务端:
public class SocketService extends Service {
private static final String TAG= "SocketService";
private boolean mIsSocketServiceDestory=false;
@Override
public void onCreate() {
new Thread(new TcpServer()).start();
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
mIsSocketServiceDestory=true;
super.onDestroy();
}
private class TcpServer implements Runnable{
@Override
public void run() {
ServerSocket mServerSocket;
try {
mServerSocket = new ServerSocket(8888);
} catch (IOException e) {
e.printStackTrace();
return;
}
while (!mIsSocketServiceDestory){
try {
final Socket accept = mServerSocket.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
replyClient(accept);
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void replyClient(Socket accept) throws IOException {
BufferedReader input = new BufferedReader(new InputStreamReader(accept.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(accept.getOutputStream())),true);
while (!mIsSocketServiceDestory){
String line = input.readLine();
if (line==null)break;
Log.e(TAG,"## reveive msg :"+line);
String message = "server has received your message:"+line;
out.println(message);
}
out.close();
input.close();
accept.close();
}
}
7. sharedUserId
sharedUserId的作用是让两个应用程序共享一个user id,我们都知道Linux进程给每一个应用程序分配了一个独立的user id,所以如果两个或多个应用程序的签名相同并且设置了一样的sharedUserId,他们将会共享一个user id,相同user id的应用程序可以访问对方的数据(也就是说如果应用程序中的一个文件的权限是600,相同uid可以直接访问,反之则无法访问),并且设置成一个Android:process就能够运行在一个进程中了。
- sharedUserId方式主要就是使用createPackageContext (String packageName, int flags)函数,该函数用来返回指定包名应用的上下文,注意是application的context。
- 注意里面的两个参数,尤其是第二个:这个flag,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY两个选项,CONTEXT_INCLUDE_CODE的作用就是在调用者的进程执行该应用的代码,也就是说可以使用getClassLoader()函数来初始化该application的相关类,如果需要加载的application不能被安全的加载进进程的话,将会抛出一个SecurityException,如果这个标示没有被设置,那么将不会在被加载的类上面施加任何约束,getClassLoader()将会返回默认的系统类加载器;CONTEXT_IGNORE_SECURITY的意思是忽略任何安全警告,和CONTEXT_INCLUDE_CODE标识一起使用可能会将不安全的代码加载进进程,所以谨慎使用。
1. 获取client应用的context
context=createPackageContext("com.lemon.shareuidclient",CONTEXT_INCLUDE_CODE|CONTEXT_IGNORE_SECURITY);
tv_context.setText(context.toString());
2. 获取client应用的drawable图片
int id = context.getResources().getIdentifier("android_boot","drawable","com.lemon.shareuidclient");
iv_pic.setImageDrawable(ContextCompat.getDrawable(context,id));
3. 获取client应用的string中得值
int ie = context.getResources().getIdentifier("lemon","string","com.lemon.shareuidclient");
tv_string.setText(context.getString(ie));
4. 打开client应用的activity
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.lemon.shareuidclient","com.lemon.shareuidclient.MainActivity"));
startActivity(intent);
5. 调用client中得方法
try {
Class clazz = context.getClassLoader().loadClass("com.lemon.shareuidclient.Method");
Object instance = clazz.newInstance();
int [] arr= new int []{3,4,5,6};
int sum = (int) clazz.getMethod("add",int[].class).invoke(instance,arr);
tv_sum.setText(" sum: ="+sum);
} catch (Exception e) {
e.printStackTrace();
}
6. 获得client中得sharepreference
SharedPreferences lemon = context.getSharedPreferences("lemon", MODE_MULTI_PROCESS);
String string = lemon.getString("adress", "china");
tv_shared_preference.setText(string );
由于SharedPreferences是有缓存机制的,所以如果在B应用中修改了该SharedPreferences文件,接着A应用去读取该文件中修改的那个值,这时你会发现还是修改前的值,这就是缓存机制导致的问题,不过有一个flag可以解决这个问题:MODE_MULTI_PROCESS,但是非常不幸的是api23已经将该标识deprecated了,原因是在一些版本上不可靠,有兴趣的可以去了解一下;