나는 몇 가지 추가 조사를했으며 만족스러운 해결책을 찾을 수있었습니다. 여기에 오는 내용은 다음과 같습니다.
라이브러리는이를 통합하는 각 응용 프로그램이 알려진 조치로 방송 수신기를 게시하는 방식으로 개발되어야합니다. com.mylib.ACTION_DETECT.
라이브러리에는 라이브러리의 현재 인스턴스를 활성화 할 수있는 경우 결정을 내리는 데 도움이되는 AIDL 인터페이스를 게시하는 추가 서비스가 있어야합니다. AIDL은 getVersion(), isActive(), getUUID()와 같은 유용한 메소드를 가질 수 있습니다.
결정을 내리는 패턴은 현재 인스턴스에 더 높은 버전 번호가 있으면 다른 하나는 활성화됩니다. 현재 인스턴스의 버전이 낮 으면 - 자체를 비활성화하거나 이미 비활성화 된 경우 비활성화 상태를 유지합니다. 현재 인스턴스의 버전이 다른 인스턴스와 같으면 다른 인스턴스가 활성화되어 있지 않고 다른 라이브러리의 uuid가 (compareTo 메소드를 통해) 낮 으면 - 자체를 활성화합니다. 다른 조건에서는 - 자체를 비활성화합니다. 이 교차 검사는 각 라이브러리가 자체적으로 결정을 내릴 수 있도록합니다. 모호한 경우는 없습니다. 왜냐하면 각 라이브러리는 게시 된 AIDL에서 지원하는 다른 응용 프로그램의 libary 인스턴스에 대한 서비스에서 필요한 데이터를 가져올 것이기 때문입니다.
다음 단계는 새 패키지가 제거되거나 추가 될 때마다 시작되거나 라이브러리가있는 응용 프로그램이 처음으로 시작될 때 IntentService를 준비하는 것입니다. IntentService는 모든 패키지에 com.mylib.ACTION_DETECT를 구현하는 브로드 캐스트 리시버를 쿼리합니다. 그런 다음 감지 된 패키지를 반복 (자체 패키지 거부)하고 서로 다른 인스턴스의 AIDL 지원 서비스에 바인딩합니다 (AIDL 서비스의 클래스 이름은 항상 동일하며 응용 프로그램 패키지 만 다릅니다). 바인딩을 완료 한 후 명확한 상황이 나타납니다. 적용된 패턴 결과가 "긍정적"(우리 인스턴스의 버전이 UUID보다 높거나 이미 활성화되었거나 이미 활성화 된 경우), 다른 인스턴스가 자신을 "음수"로 파악하고 스스로 비활성화됩니다. . 물론 각 묶인 AIDL 서비스에 패턴을 적용해야합니다.
나쁜 영어로 사과드립니다.
작동 코드 ConfictAvoidance 솔루션 : 바인딩을 지원하는 IntentService 클래스는 위에서 언급 한 AIDL 백업 서비스이기도합니다. 충돌 확인을 시작하는 BroadcastReceiver도 있습니다.
public class ConflictAvoidance extends IntentService
{
private static final String TAG = ConflictAvoidance.class.getSimpleName();
private static final String PREFERENCES = "mylib_sdk_prefs";
private static final int VERSION = 1;
private static final String KEY_BOOLEAN_PRIME_CHECK_DONE = "key_bool_prime_check_done";
private static final String KEY_BOOLEAN_ACTIVE = "key_bool_active";
private static final String KEY_LONG_MUUID = "key_long_muuid";
private static final String KEY_LONG_LUUID = "key_long_luuid";
private WakeLock mWakeLock;
private SharedPreferences mPrefs;
public ConflictAvoidance()
{
super(TAG);
}
private final IRemoteSDK.Stub mBinder = new IRemoteSDK.Stub()
{
@Override
public boolean isActive() throws RemoteException
{
return mPrefs.getBoolean(KEY_BOOLEAN_ACTIVE, false);
}
@Override
public long[] getUUID() throws RemoteException
{
return getLongUUID();
}
@Override
public int getSdkVersion() throws RemoteException
{
return 1;
}
};
@Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
@Override
public void onCreate()
{
//#ifdef DEBUG
Log.i(TAG, "onCreate()");
//#endif
mWakeLock = ((PowerManager) getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.acquire();
mPrefs = getSharedPreferences(PREFERENCES, MODE_PRIVATE);
super.onCreate();
}
@Override
public void onDestroy()
{
//#ifdef DEBUG
Log.i(TAG, "onDestroy()");
//#endif
mWakeLock.release();
super.onDestroy();
}
@Override
protected void onHandleIntent(Intent arg)
{
//#ifdef DEBUG
Log.d(TAG, "Conflict check");
//#endif
final String packageName = getPackageName();
//#ifdef DEBUG
Log.v(TAG, "Current package name: %s", packageName);
//#endif
final ArrayList<String> packages = new ArrayList<String>(20);
final PackageManager man = getPackageManager();
//#ifdef DEBUG
Log.v(TAG, "Querying receivers: com.mylib.android.sdk.ACTION_DETECT_LIB");
//#endif
final List<ResolveInfo> receivers = man.queryBroadcastReceivers(new Intent("com.mylib.android.sdk.ACTION_DETECT_LIB"), 0);
for (ResolveInfo receiver : receivers)
{
if (receiver.activityInfo != null)
{
final String otherPackageName = receiver.activityInfo.packageName;
//#ifdef DEBUG
Log.v(TAG, "Checking package: %s", otherPackageName);
//#endif
if (!packageName.equals(otherPackageName))
{
packages.add(otherPackageName);
}
}
}
if (packages.isEmpty())
{
//#ifdef DEBUG
Log.i(TAG, "No other libraries found");
//#endif
setup(true);
}
else
{
//#ifdef DEBUG
Log.v(TAG, "Querying other packages");
//#endif
final UUID uuid = getUUID();
for (String pkg : packages)
{
final Intent intent = new Intent();
intent.setClassName(pkg, "com.mylib.android.sdk.utils.ConflictAvoidance");
final RemoteConnection conn = new RemoteConnection(uuid);
try
{
if (bindService(intent, conn, BIND_AUTO_CREATE))
{
if (!conn.canActivateItself())
{
setup(false);
return;
}
}
}
finally
{
unbindService(conn);
}
}
setup(true);
}
}
private UUID getUUID()
{
final long[] uuid = getLongUUID();
return new UUID(uuid[0], uuid[1]);
}
private synchronized long[] getLongUUID()
{
if (mPrefs.contains(KEY_LONG_LUUID) && mPrefs.contains(KEY_LONG_MUUID))
{
return new long[] { mPrefs.getLong(KEY_LONG_MUUID, 0), mPrefs.getLong(KEY_LONG_LUUID, 0) };
}
else
{
final long[] uuid = new long[2];
final UUID ruuid = UUID.randomUUID();
uuid[0] = ruuid.getMostSignificantBits();
uuid[1] = ruuid.getLeastSignificantBits();
mPrefs.edit().putLong(KEY_LONG_MUUID, uuid[0]).putLong(KEY_LONG_LUUID, uuid[1]).commit();
return uuid;
}
}
private void setup(boolean active)
{
//#ifdef DEBUG
Log.v(TAG, "setup(active:%b)", active);
//#endif
mPrefs.edit().putBoolean(KEY_BOOLEAN_ACTIVE, active).putBoolean(KEY_BOOLEAN_PRIME_CHECK_DONE, true).commit();
}
public static StatusInfo getStatusInfo(Context context)
{
final SharedPreferences prefs = context.getSharedPreferences(PREFERENCES, MODE_PRIVATE);
return new StatusInfo(prefs.getBoolean(KEY_BOOLEAN_ACTIVE, false), prefs.getBoolean(KEY_BOOLEAN_PRIME_CHECK_DONE, false));
}
public static class DetectionReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
context.startService(new Intent(context, ConflictAvoidance.class));
}
}
public static class StatusInfo
{
public final boolean isActive;
public final boolean primeCheckDone;
public StatusInfo(boolean isActive, boolean primeCheckDone)
{
this.isActive = isActive;
this.primeCheckDone = primeCheckDone;
}
}
protected static class RemoteConnection implements ServiceConnection
{
private final ConditionVariable var = new ConditionVariable(false);
private final UUID mUuid;
private final AtomicReference<IRemoteSDK> mSdk = new AtomicReference<IRemoteSDK>();
public RemoteConnection(UUID uuid)
{
super();
this.mUuid = uuid;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
//#ifdef DEBUG
Log.v(TAG, "RemoteConnection.onServiceConnected(%s)", name.getPackageName());
//#endif
mSdk.set(IRemoteSDK.Stub.asInterface(service));
var.open();
}
@Override
public void onServiceDisconnected(ComponentName name)
{
//#ifdef DEBUG
Log.w(TAG, "RemoteConnection.onServiceDisconnected(%s)", name);
//#endif
var.open();
}
public boolean canActivateItself()
{
//#ifdef DEBUG
Log.v(TAG, "RemoteConnection.canActivateItself()");
//#endif
var.block(30000);
final IRemoteSDK sdk = mSdk.get();
if (sdk != null)
{
try
{
final int version = sdk.getSdkVersion();
final boolean active = sdk.isActive();
final UUID uuid;
{
final long[] luuid = sdk.getUUID();
uuid = new UUID(luuid[0], luuid[1]);
}
//#ifdef DEBUG
Log.v(TAG, "Other library: ver: %d, active: %b, uuid: %s", version, active, uuid);
//#endif
if (VERSION > version)
{
return true;
}
else if (VERSION < version)
{
return false;
}
else
{
if (active)
{
return false;
}
else
{
return mUuid.compareTo(uuid) == 1;
}
}
}
catch (Exception e)
{
return false;
}
}
else
{
return false;
}
}
}
}
AIDL 파일 :
package com.mylib.android.sdk;
interface IRemoteSDK
{
boolean isActive();
long[] getUUID();
int getSdkVersion();
}
샘플 매니페스트 :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mylib.android.sdk"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="4"
android:targetSdkVersion="4" />
<service
android:name="com.mylib.android.sdk.utils.ConflictAvoidance"
android:exported="true" />
<receiver android:name="com.mylib.android.sdk.utils.ConflictAvoidance$DetectionReceiver" >
<intent-filter>
<action android:name="com.mylib.android.sdk.ACTION_DETECT_LIB" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
</application>
</manifest>
작업 :
<action android:name="com.mylib.android.sdk.ACTION_DETECT_LIB" />
이것은 라이브러리와 함께 다른 앱을 검색하는 데 사용되는 일반적인 작업입니다.
로그 사용이 이상하게 보일 수 있지만 디버깅 할 때 StringBuffers 오버 헤드를 줄이기 위해 서식을 지원하는 사용자 지정 래퍼를 사용합니다.
연구를 위해 시간을 보내고 작업 코드 샘플로 답변을 게시했습니다. 유용하다고 생각되면 의견을 남겨주세요. –