В моем Андроид приложении я хотел реализовать новую функциональность: поделиться изображением самолета. После поиска в Интернете я нашел несколько методов, которые сводились к одному подходу — конвертировать загруженное изображение и затем поделиться им. Однако этот процесс был очень медленным. В этом посте я покажу вам, как сделать это быстро и эффективно.
Common Method for Image Sharing in Android
Наиболее распространенный способ реализовать функцию “поделиться изображением” в Андроид выглядит следующим образом:
Bitmap icon = mBitmap;
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("image/jpeg");
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
icon.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
File f = new File(Environment.getExternalStorageDirectory() + File.separator + "temporary_file.jpg");
try {
f.createNewFile();
FileOutputStream fo = new FileOutputStream(f);
fo.write(bytes.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
share.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:///sdcard/temporary_file.jpg"));
startActivity(Intent.createChooser(share, "Share Image"));
Однако вызов метода icon.compress(Bitmap.CompressFormat.JPEG, 100, bytes) очень медленный. На моем устройстве он иногда занимал 3 секунды и более. Мое устройство — POCO F3, который не является самым дешевым или медленным смартфоном на Андроид.
Optimizing Image Sharing with Picasso and OkHttpClient
Чтобы увеличить производительность моего кода, я использовал библиотеку Picasso с OkHttpClient. Вот как вы можете настроить их с помощью Dagger:
Конфигурация Picasso
@Provides
@Singleton
@NonNull
Picasso picasso(Application application, @Named("httpClient") OkHttpClient client) {
LruCache lruCache = new LruCache(MEMORY_CACHE_SIZE);
return new Picasso.Builder(application)
.memoryCache(lruCache)
.downloader(new OkHttp3Downloader(client))
.indicatorsEnabled(false).build();
}
Конфигурация HttpClient
@Provides
@Singleton
@NonNull
@Named("httpClient")
OkHttpClient httpClient(Application application) {
return provideOkHttpClient(application, true, true);
}
Метод для Создания HttpClient
private OkHttpClient provideOkHttpClient(Application application, boolean followRedirects,
boolean useDiskCache) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(2, TimeUnit.SECONDS)
.followRedirects(followRedirects)
.retryOnConnectionFailure(true)
.addInterceptor(logging);
if (useDiskCache) {
File cache = new File(application.getCacheDir(), "picasso_cache");
if (!cache.exists()) {
cache.mkdirs();
}
Cache diskCache = new Cache(cache, DISK_CACHE_SIZE);
builder.cache(diskCache);
}
return builder.build();
}
OkHttpClient настроен с файловым кэшем, поэтому я могу использовать уже загруженные файлы в своем коде.
Следующий код демонстрирует, как это работает:
httpClient.newCall(new Request.Builder()
.get()
.url(medias.get(startPosition).getData())
.build()
).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Toast.makeText(GalleryActivity.this, "Failed ot share", Toast.LENGTH_SHORT).show();
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
Log.d(TAG, "Image loaded in: " + (System.currentTimeMillis() - ts) + "ms");
File downloadedFile = new File(
GalleryActivity.this.getExternalCacheDir(),
System.currentTimeMillis() + ".jpg"
);
try (BufferedSink sink = Okio.buffer(Okio.sink(downloadedFile))) {
sink.writeAll(response.body().source());
Uri uri = FileProvider.getUriForFile(
GalleryActivity.this,
GalleryActivity.this.getPackageName() + ".provider",
downloadedFile
);
String msg = aircraftName + "\r\nShow it in: https://play.google.com/store/apps/details?id=net.nevinsky.airwar";
Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, msg);
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
shareIntent.setType("image/png");
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Log.d(TAG, "Call start 'Share with' activity after " + (System.currentTimeMillis() - ts) + "ms");
GalleryActivity.this.startActivity(Intent.createChooser(shareIntent, "Share with"));
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
Toast.makeText(GalleryActivity.this, "Failed ot share", Toast.LENGTH_SHORT).show();
}
}
});
Я использую библиотеку Okio от Squareup для передачи байтовых данных из ответа в файл. Затем я могу использовать content provider для этого файла.
Указание Content provider в AndroidManifest.xml
Вы должны указать content provider в вашем AndroidManifest.xml следующим образом:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="net.nevinsky.airwar.provider"
android:exported="false"
android:grantUriPermissions="true">
<!-- resource file to create -->
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
Конфигурация Файла ‘@xml/file_paths’
Файл @xml/file_paths содержит следующий код:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
</paths>
Примеры
В приложении это будет выглядеть так:
А в чате Telegram это будет выглядеть так:
Следуя этим шагам, вы сможете реализовать быструю функцию “поделиться изображением” в вашем Андроид приложении без замедлений, связанных с традиционными методами. Этот подход использует Picasso и OkHttpClient для оптимальной производительности.