Как Реализовать Быструю Функцию Поделиться изображением в Вашем Андроид Приложении

В моем Андроид приложении я хотел реализовать новую функциональность: поделиться изображением самолета. После поиска в Интернете я нашел несколько методов, которые сводились к одному подходу — конвертировать загруженное изображение и затем поделиться им. Однако этот процесс был очень медленным. В этом посте я покажу вам, как сделать это быстро и эффективно.

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 для оптимальной производительности.