Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: How to use Android Framework features that rely on Activities, BroadcastReceivers, etc #104

Open
balazsgerlei opened this issue Feb 8, 2021 · 2 comments

Comments

@balazsgerlei
Copy link

balazsgerlei commented Feb 8, 2021

I proposed this question originally on Twitter to @android10 and he was right that an issue is better. I implemented similar architectures in apps but frequently wondered what is the best way to integrate features provided by the Android Framework that rely heavily on Activities, Fragments, BroadcastReceivers, etc. (I kind of just hacked these into it if I had to TBH)

I would like to ask this question generally about any such features, but in order to talk about this easier, I will show an example: DownloadManager

It's nice functionality that is provided by the system and while of course downloading something can be done via Retrofit or some other implementation as well, DownloadManager handles the UI feedback (via Notifications), errors and retries, reboots, etc. so it has different pros and cons that might be more fitting to an app. And think about the (storage) permission requirements, they are compeletely different if you use your own logic vs DownloadManager.

Instinct may suggest making this a Repository as well, but DownloadManager relies on Broadcasts to notify the app about the download result for example, so it cannot really be put in a Repository. An example BroadcastReceiver to be notified about the result and get the path for the downloaded file:

private class DownloadCompletedBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        final Bundle extras = intent.getExtras();
        if (extras != null) {
            final long downloadId = extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
            final DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
            try (Cursor c = downloadManager.query(query)) {
                if (c.moveToFirst()) {
                    final int status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
                    if (status == DownloadManager.STATUS_SUCCESSFUL) {
                        final String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                        final String filePath = Uri.parse(uriString).getPath(); // This is the path of the downloaded File
                    }
                }
            } catch (Exception ignored) {}
        }
    }

};

So how would be the best way to integrate such features with this architecture?

@android10
Copy link
Owner

hey @balazsgerlei sorry for the late reply. After thinking a bit about it, I do not think there is a clean solution without breaking the core of this architecture.

This architecture exists for decoupling framework components in order to favor modularization and testing.

I would wait for someone else to reply but my take would be to just not use DownloadManager because from my perspective it is already coupling the framework by forcing the usage of a BroadcastReceiver.

You can have the same result by just using Retrofit for downloading a file, thus sticking to the architecture.

@balazsgerlei
Copy link
Author

No problem and it's not an urgent question or anything, but I would be also very interested in other's take. As I mentioned I did implemented architectures inspired by this one and in some occasions I had to also integrate Android Framework features like DownloadManager - again this was only an example as I do remember needing it at one point, but there are certainly other similar APIs on Android.

As I also mentioned, what I did in that case was kind of "hacked it into" the architecture, basically circumventing the architecture by registering the BroadcastReceiver in the single Activity of the app and forwarding the result to the Fragment that is displayed. I didn't need too much logic anyway, as I just cancelled the indeterminate ProgressBar and displayed a SnackBar about the result (that the file is downloaded to a particular path). Even though I had Retrofit in that project and I'm well aware that the download could be implemented with that (and in fact it was at first), but the features that DownloadManager provides were ultimately more important (handling retries, connectivity issues, system restarts) - even though most of these (or maybe all) could have also been implemented with custom logic, it would certainly take much-much more effort and produce considerably more code that need to be maintained so I decided to use DownloadManager though I would have been much happier if I could have integrated it better in the architecture. And ever since (it was multiple years ago) I was wondering how it could have been done but I only thought about asking about it now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants