下面列出了怎么用androidx.annotation.AnyThread的API类实例代码及写法,或者点击链接到github查看源代码。
@AnyThread
synchronized @NonNull LiveRecipient getLive(@NonNull RecipientId id) {
if (id.isUnknown()) return unknown;
LiveRecipient live = recipients.get(id);
if (live == null) {
final LiveRecipient newLive = new LiveRecipient(context, new MutableLiveData<>(), new Recipient(id));
recipients.put(id, newLive);
MissingRecipientException prettyStackTraceError = new MissingRecipientException(newLive.getId());
SignalExecutors.BOUNDED.execute(() -> {
try {
newLive.resolve();
} catch (MissingRecipientException e) {
throw prettyStackTraceError;
}
});
live = newLive;
}
return live;
}
@AnyThread
@NonNull
private AuthState readState() {
mPrefsLock.lock();
try {
String currentState = mPrefs.getString(KEY_STATE, null);
if (currentState == null) {
return new AuthState();
}
try {
return AuthState.jsonDeserialize(currentState);
} catch (JSONException ex) {
Log.w(TAG, "Failed to deserialize stored auth state - discarding");
return new AuthState();
}
} finally {
mPrefsLock.unlock();
}
}
@AnyThread
private void writeState(@Nullable AuthState state) {
mPrefsLock.lock();
try {
SharedPreferences.Editor editor = mPrefs.edit();
if (state == null) {
editor.remove(KEY_STATE);
} else {
editor.putString(KEY_STATE, state.jsonSerializeString());
}
if (!editor.commit()) {
throw new IllegalStateException("Failed to write state to shared prefs");
}
} finally {
mPrefsLock.unlock();
}
}
/**
* Marks the generation as done, and updates the list if the generation is the most recent.
*
* @return True if the given generation is the most recent, in which case the given list was
* set. False if the generation is old and the list was ignored.
*/
@AnyThread
private synchronized boolean tryLatchList(@Nullable List<? extends EpoxyModel<?>> newList,
int runGeneration) {
if (generationTracker.finishGeneration(runGeneration)) {
list = newList;
if (newList == null) {
readOnlyList = Collections.emptyList();
} else {
readOnlyList = Collections.unmodifiableList(newList);
}
return true;
}
return false;
}
@AnyThread
@NonNull
private AuthState readState() {
mPrefsLock.lock();
try {
String currentState = mPrefs.getString(KEY_STATE, null);
if (currentState == null) {
return new AuthState();
}
try {
return AuthState.jsonDeserialize(currentState);
} catch (JSONException ex) {
Log.w(TAG, "Failed to deserialize stored auth state - discarding");
return new AuthState();
}
} finally {
mPrefsLock.unlock();
}
}
@AnyThread
private void writeState(@Nullable AuthState state) {
mPrefsLock.lock();
try {
SharedPreferences.Editor editor = mPrefs.edit();
if (state == null) {
editor.remove(KEY_STATE);
} else {
editor.putString(KEY_STATE, state.jsonSerializeString());
}
if (!editor.commit()) {
throw new IllegalStateException("Failed to write state to shared prefs");
}
} finally {
mPrefsLock.unlock();
}
}
@SuppressWarnings("SuspiciousNameCombination")
@AnyThread
private void fileSRect(Rect sRect, Rect target) {
if (getRequiredRotation() == 0) {
target.set(sRect);
} else if (getRequiredRotation() == 90) {
target.set(sRect.top,
sHeight - sRect.right,
sRect.bottom, sHeight - sRect.left);
} else if (getRequiredRotation() == 180) {
target.set(
sWidth - sRect.right,
sHeight - sRect.bottom,
sWidth - sRect.left,
sHeight - sRect.top);
} else {
target.set(sWidth - sRect.bottom, sRect.left, sWidth - sRect.top, sRect.right);
}
}
@AnyThread
@Override
public void scheduleDeferredRelease(Releasable releasable) {
if (!isOnUiThread()) {
releasable.release();
return;
}
boolean shouldSchedule;
synchronized (mLock) {
if (mPendingReleasables.contains(releasable)) {
return;
}
mPendingReleasables.add(releasable);
shouldSchedule = mPendingReleasables.size() == 1;
}
// Posting to the UI queue is an O(n) operation, so we only do it once.
// The one runnable does all the releases.
if (shouldSchedule) {
mUiHandler.post(releaseRunnable);
}
}
/**
* Marks a record as no-longer-needed. Will be removed from the adapter the next time the database
* changes.
*/
@AnyThread
void releaseFastRecord(long id) {
synchronized (releasedFastRecords) {
releasedFastRecords.add(id);
}
}
/**
* @return A reader that lets you read from an immutable snapshot of the store, ensuring that data
* is consistent between reads. If you're only reading a single value, it is more
* efficient to use the various get* methods instead.
*/
@AnyThread
synchronized @NonNull KeyValueReader beginRead() {
initializeIfNecessary();
KeyValueDataSet copy = new KeyValueDataSet();
copy.putAll(dataSet);
return copy;
}
/**
* Ensures that any pending writes (such as those made via {@link Writer#apply()}) are finished.
*/
@AnyThread
synchronized void blockUntilAllWritesFinished() {
CountDownLatch latch = new CountDownLatch(1);
executor.execute(latch::countDown);
try {
latch.await();
} catch (InterruptedException e) {
Log.w(TAG, "Failed to wait for all writes.");
}
}
@AnyThread
void apply() {
for (String key : removes) {
if (dataSet.containsKey(key)) {
throw new IllegalStateException("Tried to remove a key while also setting it!");
}
}
write(dataSet, removes);
}
/**
* Marks any megaphones a new user shouldn't see as "finished".
*/
@AnyThread
public void onFirstEverAppLaunch() {
executor.execute(() -> {
database.markFinished(Event.REACTIONS);
database.markFinished(Event.MESSAGE_REQUESTS);
resetDatabaseCache();
});
}
@AnyThread
public void getNextMegaphone(@NonNull Callback<Megaphone> callback) {
executor.execute(() -> {
if (enabled) {
init();
callback.onResult(Megaphones.getNextMegaphone(context, databaseCache));
} else {
callback.onResult(null);
}
});
}
@AnyThread
public void markVisible(@NonNull Megaphones.Event event) {
long time = System.currentTimeMillis();
executor.execute(() -> {
if (getRecord(event).getFirstVisible() == 0) {
database.markFirstVisible(event, time);
resetDatabaseCache();
}
});
}
@AnyThread
public void markSeen(@NonNull Event event) {
long lastSeen = System.currentTimeMillis();
executor.execute(() -> {
MegaphoneRecord record = getRecord(event);
database.markSeen(event, record.getSeenCount() + 1, lastSeen);
enabled = false;
resetDatabaseCache();
});
}
@AnyThread
public void markFinished(@NonNull Event event) {
executor.execute(() -> {
database.markFinished(event);
resetDatabaseCache();
});
}
@Override
@AnyThread
public void onStateChanged(@NonNull Job job, @NonNull JobTracker.JobState jobState) {
synchronized (queues) {
queues.add(job.getParameters().getQueue());
}
}
@AnyThread
public synchronized void warmUp() {
if (warmedUp) {
return;
} else {
warmedUp = true;
}
SignalExecutors.BOUNDED.execute(() -> {
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
try (ThreadDatabase.Reader reader = threadDatabase.readerFor(threadDatabase.getConversationList())) {
int i = 0;
ThreadRecord record = null;
List<Recipient> recipients = new ArrayList<>();
while ((record = reader.getNext()) != null && i < CACHE_WARM_MAX) {
recipients.add(record.getRecipient());
i++;
}
Log.d(TAG, "Warming up " + recipients.size() + " recipients.");
Collections.reverse(recipients);
Stream.of(recipients).map(Recipient::getId).forEach(this::getLive);
}
});
}
/**
* Always supply both {@param uuid} and {@param e164} if you have both.
*/
@AnyThread
@SuppressLint("WrongThread")
public static @NonNull RecipientId from(@Nullable UUID uuid, @Nullable String e164) {
RecipientId recipientId = RecipientIdCache.INSTANCE.get(uuid, e164);
if (recipientId == null) {
recipientId = Recipient.externalPush(ApplicationDependencies.getApplication(), uuid, e164).getId();
}
return recipientId;
}
/**
* Converts source rectangle from tile, which treats the image file as if it were in the correct orientation already,
* to the rectangle of the image that needs to be loaded.
*/
@SuppressWarnings("SuspiciousNameCombination")
@AnyThread
private void fileSRect(Rect sRect, Rect target) {
if (getRequiredRotation() == 0) {
target.set(sRect);
} else if (getRequiredRotation() == 90) {
target.set(sRect.top, sHeight - sRect.right, sRect.bottom, sHeight - sRect.left);
} else if (getRequiredRotation() == 180) {
target.set(sWidth - sRect.right, sHeight - sRect.bottom, sWidth - sRect.left, sHeight - sRect.top);
} else {
target.set(sWidth - sRect.bottom, sRect.left, sWidth - sRect.top, sRect.right);
}
}
/**
* Determines the rotation to be applied to tiles, based on EXIF orientation or chosen setting.
*/
@AnyThread
private int getRequiredRotation() {
if (orientation == ORIENTATION_USE_EXIF) {
return sOrientation;
} else {
return orientation;
}
}
/**
* Debug logger
*/
@AnyThread
private void debug(String message, Object... args) {
if (debug) {
Log.d(TAG, String.format(message, args));
}
}
@AnyThread
public static void showMessage(final @NonNull Context context, final CharSequence message) {
XposedApp.runOnUiThread(() -> new MaterialDialog.Builder(context)
.content(message)
.positiveText(R.string.ok)
.show());
}
/**
* Converts source rectangle from tile, which treats the image file as if it were in the correct orientation already,
* to the rectangle of the image that needs to be loaded.
*/
@SuppressWarnings("SuspiciousNameCombination")
@AnyThread
private void fileSRect(Rect sRect, Rect target) {
if (getRequiredRotation() == 0) {
target.set(sRect);
} else if (getRequiredRotation() == 90) {
target.set(sRect.top, sHeight - sRect.right, sRect.bottom, sHeight - sRect.left);
} else if (getRequiredRotation() == 180) {
target.set(sWidth - sRect.right, sHeight - sRect.bottom, sWidth - sRect.left, sHeight - sRect.top);
} else {
target.set(sWidth - sRect.bottom, sRect.left, sWidth - sRect.top, sRect.right);
}
}
/**
* Determines the rotation to be applied to tiles, based on EXIF orientation or chosen setting.
*/
@AnyThread
private int getRequiredRotation() {
if (orientation == ORIENTATION_USE_EXIF) {
return sOrientation;
} else {
return orientation;
}
}
/**
* Debug logger
*/
@AnyThread
private void debug(String message, Object... args) {
if (debug) {
Log.d(TAG, String.format(message, args));
}
}
@AnyThread
void addViewportChangedListener(@Nullable ViewportChanged viewportChangedListener) {
if (viewportChangedListener == null) {
return;
}
synchronized (this) {
mViewportChangedListeners.add(viewportChangedListener);
}
}
@AnyThread
void removeViewportChangedListener(@Nullable ViewportChanged viewportChangedListener) {
if (viewportChangedListener == null) {
return;
}
synchronized (this) {
if (mViewportChangedListeners.isEmpty()) {
return;
}
mViewportChangedListeners.remove(viewportChangedListener);
}
}
@AnyThread
public static AuthStateManager getInstance(@NonNull Context context) {
AuthStateManager manager = INSTANCE_REF.get().get();
if (manager == null) {
manager = new AuthStateManager(context.getApplicationContext());
INSTANCE_REF.set(new WeakReference<>(manager));
}
return manager;
}