Если вы не хотите идти на поводу с внедрением 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);
}
}
В манифесте, обязательно объявить этот класс как приемник с именем действия, соответствующего 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.
}
безопасность
Недостатком этого метода является то, что любой может зарегистрировать приемник, чтобы поймать связь между двумя приложениями. Описанный выше код обходит это:
Сделать запрос широковещательной передачи, чтобы подделать с использованием пароля. Этот ответ не подходит для обсуждения того, как вы можете защитить этот пароль, но важно понимать, что вы не можете хранить данные при создании пароля для проверки его позже - это другое приложение, которое будет проверяться.
Сделать реакцию сложнее, если вы используете уникальный код действия каждый раз.
Ни один из них не является доказательством дурака. Если вы просто обходите любимые цвета приложений, вам, вероятно, не нужны никакие меры безопасности. Если вы передаете более конфиденциальную информацию, вам нужны оба, и вам нужно подумать над тем, чтобы сделать пароль надлежащим образом защищенным.
Другое улучшение
Большое спасибо! Действительно аккуратное решение! – l33t
Нельзя ли иметь один и тот же «android: sharedUserId» и каким-то образом прочитать приложение № 1 «SharedPreferences» из приложения № 2? – l33t
Yup, это тоже работает - но если вы выберете (или забыли добавить) другой sharedUserId, вы довольно сильно напортачили - с разрешения вам нужно только сохранить ваши ключи (которые связаны с вашей учетной записью Market в любом случае). – Jens