如果您不想麻烦地实施ContentProvider,或者如果两个应用程序都可能保持安装和使用状态,则有不同的解决方案。
代码和用法
让我们假设有问题的数据在一个类中:
class DataToBeShared() {
// Data etc in here
}
然后,为两个应用添加一个类,如下所示:
public class StoredInfoManager {
public static String codeAppType = "apptype";
public static String codeTimestamp = "timestamp";
public static String codeData = "data";
public static String codeResponseActionString = "arstring";
public static String responseActionString = "com.me.my.app.DATA_RESPONSE";
private static int APP_UNKNOWN = 0;
private static int APP_FREE = 1;
private static int APP_PAID = 2;
private static String freeSharedPrefName = "com.me.my.app.free.data";
private static String paidSharedPrefName = "com.me.my.app.paid.data";
// Use only one pair of the next lines depending on which app this is:
private static String prefName = freeSharedPrefName;
private static int appType = APP_FREE;
//private static String prefName = paidSharedPrefName;
//private static int appType = APP_PAID;
private static String codeActionResponseString = "response";
// Provide access points for the apps to store the data
public static void storeDataToPhone(Context context, DataToBeShared data) {
SharedPreferences settings = context.getSharedPreferences(prefName, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
// Put the data in the shared preferences using standard commends.
// See the android developer page for SharedPreferences.Editor for details.
// Code for that here
// And store it
editor.commit();
}
到目前为止,这是一个相当标准的共享偏好存储系统。现在是乐趣开始的地方。首先,确保有一个私有方法来获取上面存储的数据,以及一个私有方法来广播它。
private static DataToBeshared getData(Context context) {
SharedPreferences settings = context.getSharedPreferences(prefName, Context.MODE_PRIVATE);
DataToBeShared result = new DataToBeShared();
// Your code here to fill out result from Shared preferences.
// See the developer page for SharedPreferences for details.
// And return the result.
return result;
}
private static void broadcastData(Context context, DataToBeShared data, String intentActionName) {
Bundle bundle = new Bundle();
bundle.putInt(codeAppType, appType);
bundle.putParcelable(codeData, data);
Intent intent = new Intext(intentActionString);
intent.putEXtras(bundle);
context.sendBroadcast(intent);
}
创建一个BroadcastReceiver 类来捕获来自其他应用的数据响应以获取我们的数据:
static class CatchData extends BroadcastReceiver {
DataToBeShared data = null;
Long timestamp = 0L;
int versionListeningFor = Version.VERSION_UNKNOWN;
Timeout timeout = null;
// We will need a timeout in case the other app isn't actually there.
class Timeout extends CountDownTimer {
Context _context;
public Timeout(Context context, long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
_context = context;
}
@Override
public void onFinish() {
broadcastAndCloseThisBRdown(_context);
}
@Override
public void onTick(long millisUntilFinished) {}
}
// Constructor for the catching class
// Set the timeout as you see fit, but make sure that
// the tick length is longer than the timeout.
CatchDPupdate(Context context, DataToBeShared dptsKnown, Long timeKnown, int otherVersion) {
data = dptsKnown;
timestamp = timeKnown;
versionListeningFor = otherVersion;
timeout = new Timeout(context, 5000, 1000000);
timeout.start();
}
@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null) return;
// Check it's the data we want
int sendingVersion = extras.getInt(codeAppType, APP_UNKNOWN);
if (sendingVersion != versionListeningFor) return;
// This receiver has served its purpose, so unregister it.
context.unregisterReceiver(this);
// We've got the data we want, so drop the timeout.
if (timeout != null) {
timeout.cancel();
timeout = null;
}
Long tsInc = extras.getLong(codeTimestamp, 0L);
DataToBeShared dataInc = extras.getParcelable(codeData);
// Now, you need to decide which set of data is better.
// You may wish to use a timestamp system incorporated in DataToBeStored.
if (/* Incoming data best*/) {
data = dpInc;
// Make it ours for the future
storeDataToPhone(context, data);
}
// Send the data out
broadcastAndCloseThisBRdown(context);
}
private void broadcastAndCloseThisBRdown(Context context) {
broadcastData(context, data, responseActionString);
}
}
现在,为应用程序提供静态访问功能。请注意,它不会返回任何内容,这是由上面的响应捕获器完成的。
public static void geDataFromPhone(Context context) {
DataToBeStored myData = getData(context);
// See security discussion point 2 for this next line
String internalResponseActionString = "com.me.my.app.blah.hohum." + UUID.randomUUID();
// Instantiate a receiver to catch the response from the other app
int otherAppType = (appType == APP_PAID ? APP_FREE : APP_PAID);
CatchData catchData = new CatchData(context, mydata, otherAppType);
context.registerReceiver(catchData, new IntentFilter(internalResponseActionString));
// Send out a request for the data from the other app.
Bundle bundle = new Bundle();
bundle.putInt(codeAppType, otherAppType);
bundle.putString(codeResponseActionString, internalResponseActionString);
bundle.putString(CatchDataRequest.code_password, CatchDataRequest.getPassword());
Intent intent = new Intent(responseActionString);
context.sendBroadcast(intent);
}
这是它的核心。我们需要另一个类,并对清单进行调整。类(用于捕获来自其他应用的数据请求:
public class CatchDataRequest extends BroadcastReceiver {
// See security discussion point 1 below
public static String code_password = "com.newtsoft.android.groupmessenger.dir.p";
public static String getPassword() {
return calcPassword();
}
private static String calcPassword() {
return "password";
}
private static boolean verifyPassword(String p) {
if (p == null) return false;
if (calcPassword().equals(p)) return true;
return false;
}
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle == null) return;
String passwordSent = bundle.getString(code_password);
if (!verifyPassword(passwordSent)) return;
int versionRequested = bundle.getInt(StoredInfoManager.codeAppType);
String actionStringToRespondWith = bundle.getString(StoredInfoManager.codeResponseActionString);
// Only respond if we can offer what's asked for
if (versionRequested != StoredInfoManager.appType) return;
// Get the data and respond
DataToBrStored data = StoredInfoManager.getData(context);
StoredInfoManager.broadcastData(context, data, actionStringToRespondWith);
}
}
在清单中,请务必将此类声明为 Receiver,其操作名称与 StoredInfoManager.responseActionString 匹配
<receiver android:name="com.me.my.app.CatchDataRequest" android:enabled="true">
<intent-filter>
<action android:name="com.me.my.app.DATA_RESPONSE"/>
</intent-filter>
</receiver>
使用它相对简单。您使用数据的类必须扩展 BroadcastReceiver:
public class MyActivity extends Activity {
// Lots of your activity code ...
// You'll need a class to receive the data:
MyReceiver receiver= new MyReceiver();
class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null) return;
// Do stuff with the data
}
}
// But be sure to add the receiver lines to the following methods:
@Override
public void onPause() {
super.onPause();
this.unregisterReceiver(receiver);
}
@Override
public void onResume() {
super.onResume();
this.registerReceiver(receiver, new IntentFilter(StoredInfoManager.receiver_action_string));
}
}
// To store the data
StoredInfoManager.storeDataToPhone(contextOfApp, data);
// To retrieve the data is a two step process. Ask for the data:
StoredInfoManager.getData(contextOfApp);
// It will arrive in receiver, above.
}
安全
这种方法的弱点是任何人都可以注册一个接收器来捕获两个应用程序之间的通信。上面的代码绕过了这个:
通过使用密码使请求广播难以伪造。这个答案不是讨论如何使密码安全的地方,但重要的是要意识到当您创建密码以稍后检查时您无法存储数据 - 这是一个不同的应用程序将进行检查。
每次都使用唯一的操作代码,使响应更难捕捉。
这些都不是万无一失的。如果您只是传递最喜欢的应用颜色,您可能不需要任何安全措施。如果您要传递更敏感的信息,则两者都需要,并且您需要考虑使密码适当安全。
其他改进