ECn Categories > Non-informative Message (NIM) > Inconsistent Message (IM)
The displayed messages include explicit exception traces or error codes, i.e., there is no textual description of the error.
Amount of Issues | App List |
---|---|
15 | (A6) PressureNet, (A7) AnntenaPod, (A14) Wikimedia Commons, (A23) Prey, (A34) c:geo, (A38) Wake You In Music, |
Examples
AntennaPod
The following code snippets show the classes and files that are involved in the generation of the previuos issue.
To start reviewing this example we can see in the following snippet the initialization of a variable that will represent the subscribe button.
app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
399. subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
As the variable is a button an onClickListener must be added, in this case this method at line 429 make a request to start downloading the podcast content calling the “downloadFeed” feed
app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
subscribeButton.setOnClickListener(v -> {
if(feed != null && feedInFeedlist(feed)) {
Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class);
// feed.getId() is always 0, we have to retrieve the id from the feed list from
// the database
intent.putExtra(MainActivity.EXTRA_FEED_ID, getFeedId(feed));
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
} else {
Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
f.setPreferences(feed.getPreferences());
this.feed = f;
try {
429. DownloadRequester.getInstance().downloadFeed(this, f);
} catch (DownloadRequestException e) {
Log.e(TAG, Log.getStackTraceString(e));
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this, e.getMessage());
}
setSubscribeButtonState(feed);
}
});
In this class there are two methods “downloadFeed”, the first is a wrapper method that calls the following method with some predefined values
core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException {
183. downloadFeed(context, feed, false, false);
}
The second one is in charge of validating some values before starting the download of a feed. When all the validations succeed it calls the method in charge of triggering the download at lines 177-178
core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages,
boolean force)
throws DownloadRequestException {
if (feedFileValid(feed)) {
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
String lastModified = feed.isPaged() || force ? null : feed.getLastUpdate();
Bundle args = new Bundle();
args.putInt(REQUEST_ARG_PAGE_NR, feed.getPageNr());
args.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, loadAllPages);
177. download(context, feed, null, new File(getFeedfilePath(context),
178. getFeedfileName(feed)), true, username, password, lastModified, true, args);
}
}
Nevertheless the method called in the previous snippet is also a wrapper but in this case is for a simpler method. This method builds based on the parameters a new request and calls the simpler method at line 136.
core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
private void download(Context context, FeedFile item, FeedFile container, File dest,
boolean overwriteIfExists, String username, String password,
String lastModified, boolean deleteOnFailure, Bundle arguments) {
...
DownloadRequest.Builder builder = new DownloadRequest.Builder(dest.toString(), item)
.withAuthentication(username, password)
.lastModified(lastModified)
.deleteOnFailure(deleteOnFailure)
.withArguments(arguments);
DownloadRequest request = builder.build();
136. download(context, request);
}
Now that the previous method made the verifications needed this method take charge of triggering the download with the given DownloadRequest. It creates a new intent between lines 82-84 for the service that asynchronously retrieve the data.
core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
public synchronized boolean download(@NonNull Context context,
@NonNull DownloadRequest request) {
if (downloads.containsKey(request.getSource())) {
if (BuildConfig.DEBUG) Log.i(TAG, "DownloadRequest is already stored.");
return false;
}
downloads.put(request.getSource(), request);
82. Intent launchIntent = new Intent(context, DownloadService.class);
83. launchIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
84. context.startService(launchIntent);
return true;
}
Now that the download request is made the DownloadService take the responsability. To start the following method is called on service start and depending on the value of the intent extra it add the download request to a queue or stop itself.
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getParcelableExtra(EXTRA_REQUEST) != null) {
251. onDownloadQueued(intent);
} else if (numberOfDownloads.get() == 0) {
stopSelf();
}
return Service.START_NOT_STICKY;
}
Method in charge of queuing the request that are made to the service. At line 436 a new request is submitted to the “downloadExecutor”.
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
private void onDownloadQueued(Intent intent) {
Log.d(TAG, "Received enqueue request");
DownloadRequest request = intent.getParcelableExtra(EXTRA_REQUEST);
if (request == null) {
throw new IllegalArgumentException(
"ACTION_ENQUEUE_DOWNLOAD intent needs request extra");
}
Downloader downloader = getDownloader(request);
if (downloader != null) {
numberOfDownloads.incrementAndGet();
// smaller rss feeds before bigger media files
if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
downloads.add(0, downloader);
} else {
downloads.add(downloader);
}
436. downloadExecutor.submit(downloader);
postDownloaders();
}
queryDownloads();
}
The “downloadExecutor” is defined in the line 119 as a “CompletionService”
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
119. private CompletionService<Downloader> downloadExecutor;
In the other hand, the following snippet shows the most important line of the “onCreate” method of DownloadService, at line 293 a new thread is created in order to asynchronously execute the requests. The created thread is defined in the next snippet.
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@Override
public void onCreate() {
...
293. downloadCompletionThread.start();
At line 174 a new request is taked from the submitted on “onDownloadQueue” method. At line 178 the status of the request is saved in order to behave based on it.
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
private Thread downloadCompletionThread = new Thread() {
private static final String TAG = "downloadCompletionThd";
@Override
public void run() {
Log.d(TAG, "downloadCompletionThread was started");
while (!isInterrupted()) {
try {
174. Downloader downloader = downloadExecutor.take().get();
Log.d(TAG, "Received 'Download Complete' - message.");
removeDownload(downloader);
DownloadStatus status = downloader.getResult();
178. boolean successful = status.isSuccessful();
Knowing that due to connectionless state the status of the request is not succesful, and also there is not an Unauthorized error, therefore the validations made in the following line and leads to the else statement that at line 200 save the status of the request.
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
if (successful) {
...
} else {
numberOfDownloads.decrementAndGet();
if (!status.isCancelled()) {
if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
...
} else {
Log.e(TAG, "Download failed");
200. saveDownloadStatus(status);
handleFailedDownload(status, downloader.getDownloadRequest());
Adds a new DownloadStatus object to the list of completed downloads and saves it in the database
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
private void saveDownloadStatus(DownloadStatus status) {
473. reportQueue.add(status);
DBWriter.addDownloadStatus(status);
}
Validation to know if a report has to be generated, when its true a new notification is generated in the status bar for the user showing the summary of the download events.
core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
if (createReport) {
Log.d(TAG, "Creating report");
// create notification object
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
505. .setTicker(getString(R.string.download_report_title))
.setContentTitle(getString(R.string.download_report_content_title))
.setContentText(
String.format(
509. getString(R.string.download_report_content),
successfulDownloads, failedDownloads)
)
.setSmallIcon(R.drawable.stat_notify_sync_error)
.setLargeIcon(
BitmapFactory.decodeResource(getResources(),
R.drawable.stat_notify_sync_error)
)
.setContentIntent(
ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(this)
)
.setAutoCancel(true);
Text used as title for the notification shown as a resume of the downloads
core/src/main/res/values/strings.xml
200. <string name="download_report_title">Downloads completed with error(s)</string>
Text used as content for the notification shown as a resume of the downloads
core/src/main/res/values/strings.xml
212. <string name="download_report_content">%1$d downloads succeeded, %2$d failed</string>