Android 跨进程数据共享
Android 开发过程中,基于功能隔离、进程安全、进程保活等等考虑,我们经常需要为应用划分进程,然后不得不面临跨进程通信和跨进程共享数据的挑战。
跨进程通信
相对来说,跨进程通信比较简单,常用的方式有:
1.全局广播
广播是最简洁的跨进程通信方式,发送——接收广播即可完成异步通信。
2.AIDL
使用AIDL进行跨进程调用、通信是不错的选择,能够支持更复杂的接口调用,通信是同步完成的。但是实现上需要与其他进程的Service建立连接,然后通过AIDL定义的接口进行调用,实现上稍显复杂。
笔者经常使用的是这两种方式,具体使用哪种看场景决定,没有最好的方案,只有最适合的方案。如果小伙伴们有更多方式,欢迎留言交流,让我也学习学习。
跨进程共享数据
跨进程共享数据也是很常见的需求,一份应用数据,各个进程都需要读取、更新使用,我们要做的就是,在各进程都可以访问到这份数据的前提下,保证数据的同步。
常用的方式有:
1.SharedPreferences
使用存储模式为MODE_MULTI_PROCESS的SharedPreferences来实现数据存储,各个进程都需要建立自己的SharedPreference实例,通过它来访问数据,系统机制保证数据的同步。不过这种方式不完全可靠,已经被官方弃用,新的Android 版本已经不再支持。
2.ContentProvider
ContentProvider是官方推荐应用间共享数据的方式,也是被大家最广泛使用的方式,由系统来保证进程间数据同步的安全性和可靠性,稳定可靠。ContentProvider提供了增删改查的接口,与数据库结合,相当于为其他进程提供了一个远程数据库,功能强大,只是实现上相当于定义了一套远程访问数据库的接口协议,稍显复杂。
3.第三方框架
常见的有github上的Tray,作为SharedPreferences跨进程版本的替代方案,使用者的反馈是不错的,不过我还没有使用研究过它:D
同样的,没有最好的方案,只有最适合的方案,使用哪一种方案,需要根据实际场景,分析各自的优点和局限性,作出合理的选择。就如同在选择使用Sqlite还是SharedPreferences的时候,对于需要复杂操作的数据,比如大量且需要复杂的增删改查的数据,应该使用Sqlite等数据库实现来存储,跨进程时就用数据库作支持的ContentProvider,对于简单的只需要简单查询,读写的数据,用SharedPreferences足矣,跨进程时,使用SharedPreferences作支持的ContentProvider足矣。
在跨进程共享简单数据,如配置、用户信息等等的时候,我常用的做法就是利用ContentProvider的跨进程安全特性,以SharedPreferences作为支撑,实现跨进程的SharedPreferences:
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils; import java.util.Iterator; /**
* Created by Irwin on 2017/8/29.
*/ public class GlobalProvider extends ContentProvider { public static final Uri AUTHORITY_URI = Uri.parse("content://[YOUR PACKAGE NAME]");
public static final Uri CONTENT_URI = AUTHORITY_URI; public static final String PARAM_KEY = "key"; public static final String PARAM_VALUE = "value"; private final String DB_NAME = "global.sp";
private SharedPreferences mStore; public static Cursor query(Context context, String... keys) {
return context.getContentResolver().query(CONTENT_URI, keys, null, null, null);
} public static String getString(Context context, String key) {
return getString(context, key, null);
} public static String getString(Context context, String key, String defValue) {
Cursor cursor = query(context, key);
String ret = defValue;
if (cursor.moveToNext()) {
ret = cursor.getString(0);
if (TextUtils.isEmpty(ret)) {
ret = defValue;
}
}
cursor.close();
return ret;
} public static int getInt(Context context, String key, int defValue) {
Cursor cursor = query(context, key);
int ret = defValue;
if (cursor.moveToNext()) {
try {
ret = cursor.getInt(0);
} catch (Exception e) { }
}
cursor.close();
return ret;
} public static Uri save(Context context, ContentValues values) {
return context.getContentResolver().insert(GlobalProvider.CONTENT_URI, values);
} public static Uri save(Context context, String key, String value) {
ContentValues values = new ContentValues(1);
values.put(key, value);
return save(context, values);
} public static Uri remove(Context context, String key) {
return save(context, key, null);
} @Override
public boolean onCreate() {
mStore = getContext().getSharedPreferences(DB_NAME, Context.MODE_PRIVATE);
return true;
} @Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
int size = projection == null ? 0 : projection.length;
if (size > 0) {
String[] values = new String[size];
for (int i = 0; i < size; i++) {
values[i] = getValue(projection[i], null);
}
return createCursor(projection, values);
}
String key = uri.getQueryParameter(PARAM_KEY);
String value = null;
if (!TextUtils.isEmpty(key)) {
value = getValue(key, null);
}
return createSingleCursor(key, value);
} protected Cursor createSingleCursor(String key, String value) {
MatrixCursor cursor = new MatrixCursor(new String[]{key}, 1);
if (!TextUtils.isEmpty(value)) {
cursor.addRow(new Object[]{value});
}
return cursor;
} protected Cursor createCursor(String[] keys, String[] values) {
MatrixCursor cursor = new MatrixCursor(keys, 1);
cursor.addRow(values);
return cursor;
} @Nullable
@Override
public String getType(@NonNull Uri uri) {
return "";
} @Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
if (values != null && values.size() > 0) {
save(values);
} else {
String key = uri.getQueryParameter(PARAM_KEY);
String value = uri.getQueryParameter(PARAM_VALUE);
if (!TextUtils.isEmpty(key)) {
save(key, value);
}
}
return null;
} @Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
String key = selection == null ? selection : uri.getQueryParameter(PARAM_KEY);
if (!TextUtils.isEmpty(key)) {
remove(key);
return 1;
}
return 0;
} @Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
if (values != null && values.size() > 0) {
save(values);
return values.size();
}
String key = uri.getQueryParameter(PARAM_KEY);
String value = uri.getQueryParameter(PARAM_VALUE);
if (!TextUtils.isEmpty(key)) {
save(key, value);
return 1;
}
return 0;
} protected String getValue(String key, String defValue) {
return mStore.getString(key, defValue);
} protected void save(ContentValues values) {
String key;
String value;
Iterator<String> iterator = values.keySet().iterator();
SharedPreferences.Editor editor = mStore.edit();
while (iterator.hasNext()) {
key = iterator.next();
value = values.getAsString(key);
if (!TextUtils.isEmpty(key)) {
if (value != null) {
editor.putString(key, value);
} else {
editor.remove(key);
}
}
}
editor.commit();
} protected void save(String key, String value) {
SharedPreferences.Editor editor = mStore.edit();
if (value != null) {
editor.putString(key, value);
} else {
editor.remove(key);
}
editor.commit();
} protected void remove(String key) {
SharedPreferences.Editor editor = mStore.edit();
editor.remove(key);
editor.commit();
} }
根据需要添加快捷访问的方法,使用起来简单方便:
//读取共享
GlobalProvider.getInt(context,PARAMETER_KEY,DEFAULT_VALUE_WHILE_NULL); //写入共享
GlobalProvider.save(context, PARAMETER_KEY, PARAMETER_VALUE);
当然,对于需要复杂操作的共享数据,还是乖乖滴基于数据库根据自己的业务需求实现完整的ContentProvider吧!
Android 跨进程数据共享的更多相关文章
- 【朝花夕拾】Android性能篇之(七)Android跨进程通信篇
前言 只要是面试高级工程师岗位,Android跨进程通信就是最受面试官青睐的知识点之一.Android系统的运行由大量相互独立的进程相互协助来完成的,所以Android进程间通信问题,是做好Andro ...
- Android 跨进程启动Activity黑屏(白屏)的三种解决方案
原文链接:http://www.cnblogs.com/feidu/p/8057012.html 当Android跨进程启动Activity时,过程界面很黑屏(白屏)短暂时间(几百毫秒?).当然从桌面 ...
- Android跨进程通信的四种方式
由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于andro ...
- android 跨进程通信
转自:http://www.androidsdn.com/article/show/137 由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一 ...
- 【Android跨进程】IPC总结
前言 IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程.两个进程可以是两个独立的app也可以是一个app的两个 ...
- android跨进程通信(IPC)——AIDL
转载请标明出处: http://blog.csdn.net/sinat_15877283/article/details/51026711: 本文出自: [温利东的博客] 近期在看 @任玉刚 大神编写 ...
- Android跨进程訪问(AIDL服务)
我将AndroidAIDL的学习知识总结一下和大家共享 在Android开发中,AIDL主要是用来跨进程訪问. Android系统中的进程之间不能共享内存,因此,须要提供一些机制在不同进程之间进行数据 ...
- android 跨进程通讯 AIDL
跨进程如何通讯?两个进程无法直接通讯,通过Android系统底层间接通讯.基于service的aidl实现跨进程通讯. 什么叫AIDL? Android interface definition la ...
- Android跨进程通信AIDL服务
服务(Service)是android系统中非常重要的组件.Service可以脱离应用程序运行.也就是说,应用程序只起到一个启动Service的作用.一但Service被启动,就算应用程序关闭,Ser ...
随机推荐
- asp.net core 通过ajax调用后台方法(非api)
1. 在Startup.cs文件中添加: services.AddMvc(); services.AddAntiforgery(o => o.Heade ...
- vue-i18n国际化在data中切换不起作用
vue-i18n是一个针对于vue的国际化插件,使用非常简单,具体使用方式看我细细道来. 实现方式 1. 下载包 npm install vue-i18n 2. 配置 在main.js文件中加入如下配 ...
- RIDE的下载及安装
1.本机配置 windows8.1 python3.6.5,已配置环境变量 2.安装RIDE前需要安装的依赖包(使用pip就可以直接安装) 首先必须有robotframework 例如:pip ins ...
- An overview of network penetration testing
1. an SQLi vulnerability will allow you to do the following query the database using select statem ...
- Jmeter性能测试之Monitor监控(SSHMon Samples Collector)
前面写的一篇Monitor监控有缺陷, 这篇文章使用Jmeter4.0+的版本, 使用插件SSHMon Samples Collector来做资源监控 1. 官网下载插件: plugins-manag ...
- day14.生成器迭代器作业
1.写生成器,从文件中读取内容,再每一行读取的内容前加上 ‘***’之后返回给用户 def func(filename): word = input('输入你想找的内容:') with open(fi ...
- Codeforces 628F 最大流转最小割
感觉和昨天写了的题一模一样... 这种题也能用hall定理取check, 感觉更最小割差不多. #include<bits/stdc++.h> #define LL long long # ...
- k-近邻算法概述
2.1 k-近邻算法概述 k-近邻算法采用测量不同特征值之间的距离方法进行分类. 优点:精度高.对异常值不敏感.无数据输入假定. 确定:计算复杂度高.空间复杂度高. 适用数据范围:数值型和标称型. 工 ...
- Redis常见面试题
介绍:Redis 是一个开源的使用 ANSI C 语言编写.遵守 BSD 协议.支持网络.可基于内存亦可持久化的日志型.Key-Value 数据库,并提供多种语言的 API的非关系型数据库. 传统数据 ...
- RPC远程调用——Dubbo
1.安装Zookeeper a.下载Zookeeper后解压 b.进入根目录下的conf文件夹,将zoo_sample.cfg改成bak文件,并复制一个修改为zoo.cfg,修改相关配置 # The ...