Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
925dd5aa
authored
Apr 08, 2019
by
olly
Committed by
Oliver Woodman
Apr 13, 2019
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Fix most remaining issues with DownloadManager threading
PiperOrigin-RevId: 242439330
parent
c17c7221
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
193 additions
and
115 deletions
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java
View file @
925dd5aa
...
@@ -29,10 +29,10 @@ import static com.google.android.exoplayer2.offline.DownloadState.STATE_RESTARTI
...
@@ -29,10 +29,10 @@ import static com.google.android.exoplayer2.offline.DownloadState.STATE_RESTARTI
import
static
com
.
google
.
android
.
exoplayer2
.
offline
.
DownloadState
.
STATE_STOPPED
;
import
static
com
.
google
.
android
.
exoplayer2
.
offline
.
DownloadState
.
STATE_STOPPED
;
import
android.content.Context
;
import
android.content.Context
;
import
android.os.ConditionVariable
;
import
android.os.Handler
;
import
android.os.Handler
;
import
android.os.HandlerThread
;
import
android.os.HandlerThread
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.os.Message
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
...
@@ -43,12 +43,14 @@ import com.google.android.exoplayer2.scheduler.RequirementsWatcher;
...
@@ -43,12 +43,14 @@ import com.google.android.exoplayer2.scheduler.RequirementsWatcher;
import
com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters
;
import
com.google.android.exoplayer2.upstream.cache.CacheUtil.CachingCounters
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.Log
;
import
com.google.android.exoplayer2.util.Util
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.concurrent.CopyOnWriteArraySet
;
import
java.util.concurrent.CopyOnWriteArraySet
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
...
@@ -108,6 +110,20 @@ public final class DownloadManager {
...
@@ -108,6 +110,20 @@ public final class DownloadManager {
public
static
final
Requirements
DEFAULT_REQUIREMENTS
=
public
static
final
Requirements
DEFAULT_REQUIREMENTS
=
new
Requirements
(
Requirements
.
NETWORK_TYPE_ANY
,
false
,
false
);
new
Requirements
(
Requirements
.
NETWORK_TYPE_ANY
,
false
,
false
);
// Messages posted to the main handler.
private
static
final
int
MSG_INITIALIZED
=
0
;
private
static
final
int
MSG_PROCESSED
=
1
;
private
static
final
int
MSG_DOWNLOAD_STATE_CHANGED
=
2
;
// Messages posted to the background handler.
private
static
final
int
MSG_INITIALIZE
=
0
;
private
static
final
int
MSG_ADD_DOWNLOAD
=
1
;
private
static
final
int
MSG_REMOVE_DOWNLOAD
=
2
;
private
static
final
int
MSG_SET_MANUAL_STOP_REASON
=
3
;
private
static
final
int
MSG_SET_NOT_MET_REQUIREMENTS
=
4
;
private
static
final
int
MSG_DOWNLOAD_THREAD_STOPPED
=
5
;
private
static
final
int
MSG_RELEASE
=
6
;
@Retention
(
RetentionPolicy
.
SOURCE
)
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
@IntDef
({
START_THREAD_SUCCEEDED
,
START_THREAD_SUCCEEDED
,
...
@@ -133,6 +149,8 @@ public final class DownloadManager {
...
@@ -133,6 +149,8 @@ public final class DownloadManager {
private
final
Handler
mainHandler
;
private
final
Handler
mainHandler
;
private
final
HandlerThread
internalThread
;
private
final
HandlerThread
internalThread
;
private
final
Handler
internalHandler
;
private
final
Handler
internalHandler
;
private
final
RequirementsWatcher
.
Listener
requirementsListener
;
private
final
Object
releaseLock
;
// Collections that are accessed on the main thread.
// Collections that are accessed on the main thread.
private
final
CopyOnWriteArraySet
<
Listener
>
listeners
;
private
final
CopyOnWriteArraySet
<
Listener
>
listeners
;
...
@@ -143,7 +161,8 @@ public final class DownloadManager {
...
@@ -143,7 +161,8 @@ public final class DownloadManager {
private
final
HashMap
<
Download
,
DownloadThread
>
activeDownloads
;
private
final
HashMap
<
Download
,
DownloadThread
>
activeDownloads
;
// Mutable fields that are accessed on the main thread.
// Mutable fields that are accessed on the main thread.
private
boolean
idle
;
private
int
pendingMessages
;
private
int
activeDownloadCount
;
private
boolean
initialized
;
private
boolean
initialized
;
private
boolean
released
;
private
boolean
released
;
private
RequirementsWatcher
requirementsWatcher
;
private
RequirementsWatcher
requirementsWatcher
;
...
@@ -224,61 +243,52 @@ public final class DownloadManager {
...
@@ -224,61 +243,52 @@ public final class DownloadManager {
downloads
=
new
ArrayList
<>();
downloads
=
new
ArrayList
<>();
downloadStates
=
new
HashMap
<>();
downloadStates
=
new
HashMap
<>();
activeDownloads
=
new
HashMap
<>();
activeDownloads
=
new
HashMap
<>();
listeners
=
new
CopyOnWriteArraySet
<>();
releaseLock
=
new
Object
();
Looper
looper
=
Looper
.
myLooper
();
requirementsListener
=
this
::
onRequirementsStateChanged
;
if
(
looper
==
null
)
{
looper
=
Looper
.
getMainLooper
();
}
mainHandler
=
new
Handler
(
looper
);
mainHandler
=
new
Handler
(
Util
.
getLooper
(),
this
::
handleMainMessage
);
internalThread
=
new
HandlerThread
(
"DownloadManager file i/o"
);
internalThread
=
new
HandlerThread
(
"DownloadManager file i/o"
);
internalThread
.
start
();
internalThread
.
start
();
internalHandler
=
new
Handler
(
internalThread
.
getLooper
());
internalHandler
=
new
Handler
(
internalThread
.
getLooper
()
,
this
::
handleInternalMessage
);
listeners
=
new
CopyOnWriteArraySet
<>();
requirementsWatcher
=
new
RequirementsWatcher
(
context
,
requirementsListener
,
requirements
);
int
notMetRequirements
=
requirementsWatcher
.
start
();
int
notMetRequirements
=
watchRequirements
(
requirements
);
pendingMessages
=
1
;
runOnInternalThread
(
internalHandler
()
->
{
.
obtainMessage
(
MSG_INITIALIZE
,
notMetRequirements
,
/* unused */
0
)
setNotMetRequirements
(
notMetRequirements
);
.
sendToTarget
();
loadDownloads
();
});
logd
(
"Created"
);
}
}
/** Returns whether the manager has completed initialization. */
/** Returns whether the manager has completed initialization. */
public
boolean
isInitialized
()
{
public
boolean
isInitialized
()
{
Assertions
.
checkState
(!
released
);
return
initialized
;
return
initialized
;
}
}
/** Returns whether there are no active downloads. */
/** Returns whether there are no active downloads. */
public
boolean
isIdle
()
{
public
boolean
isIdle
()
{
Assertions
.
checkState
(!
released
);
return
activeDownloadCount
==
0
&&
pendingMessages
==
0
;
return
idle
;
}
}
/** Returns the used {@link DownloadIndex}. */
/** Returns the used {@link DownloadIndex}. */
public
DownloadIndex
getDownloadIndex
()
{
public
DownloadIndex
getDownloadIndex
()
{
Assertions
.
checkState
(!
released
);
return
downloadIndex
;
return
downloadIndex
;
}
}
/** Returns the number of downloads. */
/** Returns the number of downloads. */
public
int
getDownloadCount
()
{
public
int
getDownloadCount
()
{
Assertions
.
checkState
(!
released
);
return
downloadStates
.
size
();
return
downloadStates
.
size
();
}
}
/** Returns the states of all current downloads. */
/** Returns the states of all current downloads. */
public
DownloadState
[]
getAllDownloadStates
()
{
public
DownloadState
[]
getAllDownloadStates
()
{
Assertions
.
checkState
(!
released
);
return
downloadStates
.
values
().
toArray
(
new
DownloadState
[
0
]);
return
downloadStates
.
values
().
toArray
(
new
DownloadState
[
0
]);
}
}
/** Returns the requirements needed to be met to start downloads. */
/** Returns the requirements needed to be met to start downloads. */
public
Requirements
getRequirements
()
{
public
Requirements
getRequirements
()
{
Assertions
.
checkState
(!
released
);
return
requirementsWatcher
.
getRequirements
();
return
requirementsWatcher
.
getRequirements
();
}
}
...
@@ -288,7 +298,6 @@ public final class DownloadManager {
...
@@ -288,7 +298,6 @@ public final class DownloadManager {
* @param listener The listener to be added.
* @param listener The listener to be added.
*/
*/
public
void
addListener
(
Listener
listener
)
{
public
void
addListener
(
Listener
listener
)
{
Assertions
.
checkState
(!
released
);
listeners
.
add
(
listener
);
listeners
.
add
(
listener
);
}
}
...
@@ -298,7 +307,6 @@ public final class DownloadManager {
...
@@ -298,7 +307,6 @@ public final class DownloadManager {
* @param listener The listener to be removed.
* @param listener The listener to be removed.
*/
*/
public
void
removeListener
(
Listener
listener
)
{
public
void
removeListener
(
Listener
listener
)
{
Assertions
.
checkState
(!
released
);
listeners
.
remove
(
listener
);
listeners
.
remove
(
listener
);
}
}
...
@@ -312,25 +320,25 @@ public final class DownloadManager {
...
@@ -312,25 +320,25 @@ public final class DownloadManager {
return
;
return
;
}
}
requirementsWatcher
.
stop
();
requirementsWatcher
.
stop
();
int
notMetRequirements
=
watchRequirements
(
requirements
);
requirementsWatcher
=
new
RequirementsWatcher
(
context
,
requirementsListener
,
requirements
);
onRequirementsStateChanged
(
notMetRequirements
);
int
notMetRequirements
=
requirementsWatcher
.
start
();
onRequirementsStateChanged
(
requirementsWatcher
,
notMetRequirements
);
}
}
/**
/**
* Clears manual stop reason of all downloads. Downloads are started if the requirements are met.
* Clears manual stop reason of all downloads. Downloads are started if the requirements are met.
*/
*/
public
void
startDownloads
()
{
public
void
startDownloads
()
{
logd
(
"manual stop is cancelled"
);
postSetManualStopReason
(
/* id= */
null
,
MANUAL_STOP_REASON_NONE
);
runOnInternalThread
(()
->
setManualStopReason
(
/* id= */
null
,
MANUAL_STOP_REASON_NONE
));
}
}
/** Signals all downloads to stop. Call {@link #startDownloads()} to let them to be started. */
/** Signals all downloads to stop. Call {@link #startDownloads()} to let them to be started. */
public
void
stopDownloads
()
{
public
void
stopDownloads
()
{
stopDownloads
(
/* manualStopReason= */
MANUAL_STOP_REASON_UNDEFINED
);
stopDownloads
(
MANUAL_STOP_REASON_UNDEFINED
);
}
}
/**
/**
* S
ignals all downloads to stop. Call {@link #startDownloads()} to let them to be started
.
* S
ets a manual stop reason for all downloads
.
*
*
* @param manualStopReason An application defined stop reason. Value {@value
* @param manualStopReason An application defined stop reason. Value {@value
* DownloadState#MANUAL_STOP_REASON_NONE} is not allowed and value {@value
* DownloadState#MANUAL_STOP_REASON_NONE} is not allowed and value {@value
...
@@ -339,8 +347,7 @@ public final class DownloadManager {
...
@@ -339,8 +347,7 @@ public final class DownloadManager {
*/
*/
public
void
stopDownloads
(
int
manualStopReason
)
{
public
void
stopDownloads
(
int
manualStopReason
)
{
Assertions
.
checkArgument
(
manualStopReason
!=
MANUAL_STOP_REASON_NONE
);
Assertions
.
checkArgument
(
manualStopReason
!=
MANUAL_STOP_REASON_NONE
);
logd
(
"downloads are stopped manually"
);
postSetManualStopReason
(
/* id= */
null
,
manualStopReason
);
runOnInternalThread
(()
->
setManualStopReason
(
/* id= */
null
,
manualStopReason
));
}
}
/**
/**
...
@@ -350,7 +357,7 @@ public final class DownloadManager {
...
@@ -350,7 +357,7 @@ public final class DownloadManager {
* @param id The unique content id of the download to be started.
* @param id The unique content id of the download to be started.
*/
*/
public
void
startDownload
(
String
id
)
{
public
void
startDownload
(
String
id
)
{
runOnInternalThread
(()
->
setManualStopReason
(
id
,
MANUAL_STOP_REASON_NONE
)
);
postSetManualStopReason
(
id
,
MANUAL_STOP_REASON_NONE
);
}
}
/**
/**
...
@@ -360,8 +367,7 @@ public final class DownloadManager {
...
@@ -360,8 +367,7 @@ public final class DownloadManager {
* @param id The unique content id of the download to be stopped.
* @param id The unique content id of the download to be stopped.
*/
*/
public
void
stopDownload
(
String
id
)
{
public
void
stopDownload
(
String
id
)
{
runOnInternalThread
(
stopDownload
(
id
,
MANUAL_STOP_REASON_UNDEFINED
);
()
->
stopDownload
(
id
,
/* manualStopReason= */
MANUAL_STOP_REASON_UNDEFINED
));
}
}
/**
/**
...
@@ -376,7 +382,7 @@ public final class DownloadManager {
...
@@ -376,7 +382,7 @@ public final class DownloadManager {
*/
*/
public
void
stopDownload
(
String
id
,
int
manualStopReason
)
{
public
void
stopDownload
(
String
id
,
int
manualStopReason
)
{
Assertions
.
checkArgument
(
manualStopReason
!=
MANUAL_STOP_REASON_NONE
);
Assertions
.
checkArgument
(
manualStopReason
!=
MANUAL_STOP_REASON_NONE
);
runOnInternalThread
(()
->
setManualStopReason
(
id
,
manualStopReason
)
);
postSetManualStopReason
(
id
,
manualStopReason
);
}
}
/**
/**
...
@@ -385,7 +391,8 @@ public final class DownloadManager {
...
@@ -385,7 +391,8 @@ public final class DownloadManager {
* @param action The download action.
* @param action The download action.
*/
*/
public
void
addDownload
(
DownloadAction
action
)
{
public
void
addDownload
(
DownloadAction
action
)
{
runOnInternalThread
(()
->
addDownloadInternal
(
action
));
pendingMessages
++;
internalHandler
.
obtainMessage
(
MSG_ADD_DOWNLOAD
,
action
).
sendToTarget
();
}
}
/**
/**
...
@@ -394,7 +401,8 @@ public final class DownloadManager {
...
@@ -394,7 +401,8 @@ public final class DownloadManager {
* @param id The unique content id of the download to be started.
* @param id The unique content id of the download to be started.
*/
*/
public
void
removeDownload
(
String
id
)
{
public
void
removeDownload
(
String
id
)
{
runOnInternalThread
(()
->
removeDownloadInternal
(
id
));
pendingMessages
++;
internalHandler
.
obtainMessage
(
MSG_REMOVE_DOWNLOAD
,
id
).
sendToTarget
();
}
}
/**
/**
...
@@ -403,74 +411,154 @@ public final class DownloadManager {
...
@@ -403,74 +411,154 @@ public final class DownloadManager {
* called.
* called.
*/
*/
public
void
release
()
{
public
void
release
()
{
if
(
released
)
{
synchronized
(
releaseLock
)
{
return
;
if
(
released
)
{
}
return
;
released
=
true
;
}
if
(
requirementsWatcher
!=
null
)
{
internalHandler
.
sendEmptyMessage
(
MSG_RELEASE
);
requirementsWatcher
.
stop
();
boolean
wasInterrupted
=
false
;
while
(!
released
)
{
try
{
releaseLock
.
wait
();
}
catch
(
InterruptedException
e
)
{
wasInterrupted
=
true
;
}
}
if
(
wasInterrupted
)
{
// Restore the interrupted status.
Thread
.
currentThread
().
interrupt
();
}
mainHandler
.
removeCallbacksAndMessages
(
/* token= */
null
);
// Reset state.
pendingMessages
=
0
;
activeDownloadCount
=
0
;
initialized
=
false
;
downloadStates
.
clear
();
}
}
ConditionVariable
fileIOFinishedCondition
=
new
ConditionVariable
();
internalHandler
.
post
(
()
->
{
releaseInternal
();
fileIOFinishedCondition
.
open
();
});
fileIOFinishedCondition
.
block
();
logd
(
"Released"
);
}
}
private
void
runOnInternalThread
(
Runnable
runnable
)
{
private
void
postSetManualStopReason
(
@Nullable
String
id
,
int
manualStopReason
)
{
Assertions
.
checkState
(!
released
);
pendingMessages
++;
internalHandler
.
post
(
runnable
);
internalHandler
.
obtainMessage
(
MSG_SET_MANUAL_STOP_REASON
,
manualStopReason
,
/* unused */
0
,
id
)
.
sendToTarget
();
}
}
private
void
notifyListenersDownloadStateChange
(
DownloadState
downloadState
)
{
private
void
onRequirementsStateChanged
(
if
(
isFinished
(
downloadState
.
state
))
{
RequirementsWatcher
requirementsWatcher
,
downloadStates
.
remove
(
downloadState
.
id
);
@Requirements
.
RequirementFlags
int
notMetRequirements
)
{
}
else
{
Requirements
requirements
=
requirementsWatcher
.
getRequirements
();
downloadStates
.
put
(
downloadState
.
id
,
downloadState
);
}
for
(
Listener
listener
:
listeners
)
{
for
(
Listener
listener
:
listeners
)
{
listener
.
on
DownloadStateChanged
(
this
,
downloadState
);
listener
.
on
RequirementsStateChanged
(
this
,
requirements
,
notMetRequirements
);
}
}
pendingMessages
++;
internalHandler
.
obtainMessage
(
MSG_SET_NOT_MET_REQUIREMENTS
,
notMetRequirements
,
/* unused */
0
)
.
sendToTarget
();
}
}
@Requirements
.
RequirementFlags
// Main thread message handling.
private
int
watchRequirements
(
Requirements
requirements
)
{
RequirementsWatcher
.
Listener
listener
=
@SuppressWarnings
(
"unchecked"
)
(
requirementsWatcher
,
notMetRequirements
)
->
onRequirementsStateChanged
(
notMetRequirements
);
private
boolean
handleMainMessage
(
Message
message
)
{
requirementsWatcher
=
new
RequirementsWatcher
(
context
,
listener
,
requirements
);
switch
(
message
.
what
)
{
return
requirementsWatcher
.
start
();
case
MSG_INITIALIZED:
List
<
DownloadState
>
downloadStates
=
(
List
<
DownloadState
>)
message
.
obj
;
onInitialized
(
downloadStates
);
break
;
case
MSG_DOWNLOAD_STATE_CHANGED:
DownloadState
state
=
(
DownloadState
)
message
.
obj
;
onDownloadStateChanged
(
state
);
break
;
case
MSG_PROCESSED:
int
processedMessageCount
=
message
.
arg1
;
int
activeDownloadCount
=
message
.
arg2
;
onMessageProcessed
(
processedMessageCount
,
activeDownloadCount
);
break
;
default
:
throw
new
IllegalStateException
();
}
return
true
;
}
}
private
void
onRequirementsStateChanged
(
@Requirements
.
RequirementFlags
int
notMetRequirements
)
{
// TODO: Merge these three events into a single MSG_STATE_CHANGE that can carry all updates. This
Requirements
requirements
=
requirementsWatcher
.
getRequirements
();
// allows updating idle at the same point as the downloads that can be queried changes.
private
void
onInitialized
(
List
<
DownloadState
>
downloadStates
)
{
initialized
=
true
;
for
(
int
i
=
0
;
i
<
downloadStates
.
size
();
i
++)
{
DownloadState
downloadState
=
downloadStates
.
get
(
i
);
this
.
downloadStates
.
put
(
downloadState
.
id
,
downloadState
);
}
for
(
Listener
listener
:
listeners
)
{
for
(
Listener
listener
:
listeners
)
{
listener
.
on
RequirementsStateChanged
(
this
,
requirements
,
notMetRequirement
s
);
listener
.
on
Initialized
(
DownloadManager
.
thi
s
);
}
}
internalHandler
.
post
(()
->
setNotMetRequirements
(
notMetRequirements
));
}
}
private
void
onInitialized
()
{
private
void
onDownloadStateChanged
(
DownloadState
downloadState
)
{
initialized
=
true
;
if
(
isFinished
(
downloadState
.
state
))
{
downloadStates
.
remove
(
downloadState
.
id
);
}
else
{
downloadStates
.
put
(
downloadState
.
id
,
downloadState
);
}
for
(
Listener
listener
:
listeners
)
{
for
(
Listener
listener
:
listeners
)
{
listener
.
on
Initialized
(
DownloadManager
.
this
);
listener
.
on
DownloadStateChanged
(
this
,
downloadState
);
}
}
}
}
private
void
onIdleStateChange
(
boolean
idle
)
{
private
void
onMessageProcessed
(
int
processedMessageCount
,
int
activeDownloadCount
)
{
if
(!
this
.
idle
&&
idle
)
{
this
.
pendingMessages
-=
processedMessageCount
;
this
.
activeDownloadCount
=
activeDownloadCount
;
if
(
isIdle
())
{
for
(
Listener
listener
:
listeners
)
{
for
(
Listener
listener
:
listeners
)
{
listener
.
onIdle
(
this
);
listener
.
onIdle
(
this
);
}
}
}
}
this
.
idle
=
idle
;
}
}
// Methods that run on internal thread.
// Internal thread message handling.
private
boolean
handleInternalMessage
(
Message
message
)
{
boolean
processedExternalMessage
=
true
;
switch
(
message
.
what
)
{
case
MSG_INITIALIZE:
int
notMetRequirements
=
message
.
arg1
;
initializeInternal
(
notMetRequirements
);
break
;
case
MSG_ADD_DOWNLOAD:
DownloadAction
action
=
(
DownloadAction
)
message
.
obj
;
addDownloadInternal
(
action
);
break
;
case
MSG_REMOVE_DOWNLOAD:
String
id
=
(
String
)
message
.
obj
;
removeDownloadInternal
(
id
);
break
;
case
MSG_SET_MANUAL_STOP_REASON:
id
=
(
String
)
message
.
obj
;
int
manualStopReason
=
message
.
arg1
;
setManualStopReasonInternal
(
id
,
manualStopReason
);
break
;
case
MSG_SET_NOT_MET_REQUIREMENTS:
notMetRequirements
=
message
.
arg1
;
setNotMetRequirementsInternal
(
notMetRequirements
);
break
;
case
MSG_DOWNLOAD_THREAD_STOPPED:
DownloadThread
downloadThread
=
(
DownloadThread
)
message
.
obj
;
onDownloadThreadStoppedInternal
(
downloadThread
);
processedExternalMessage
=
false
;
break
;
case
MSG_RELEASE:
releaseInternal
();
return
true
;
// Don't post back to mainHandler on release.
default
:
throw
new
IllegalStateException
();
}
mainHandler
.
obtainMessage
(
MSG_PROCESSED
,
processedExternalMessage
?
1
:
0
,
activeDownloads
.
size
())
.
sendToTarget
();
return
true
;
}
private
void
setManualStopReason
(
@Nullable
String
id
,
int
manualStopReason
)
{
private
void
setManualStopReason
Internal
(
@Nullable
String
id
,
int
manualStopReason
)
{
if
(
id
!=
null
)
{
if
(
id
!=
null
)
{
Download
download
=
getDownload
(
id
);
Download
download
=
getDownload
(
id
);
if
(
download
!=
null
)
{
if
(
download
!=
null
)
{
...
@@ -530,14 +618,17 @@ public final class DownloadManager {
...
@@ -530,14 +618,17 @@ public final class DownloadManager {
private
void
onDownloadStateChange
(
Download
download
,
DownloadState
downloadState
)
{
private
void
onDownloadStateChange
(
Download
download
,
DownloadState
downloadState
)
{
logd
(
"Download state is changed"
,
download
);
logd
(
"Download state is changed"
,
download
);
updateDownloadIndex
(
downloadState
);
updateDownloadIndex
(
downloadState
);
mainHandler
.
post
(()
->
notifyListenersDownloadStateChange
(
downloadState
));
int
index
=
downloads
.
indexOf
(
download
);
if
(
isFinished
(
download
.
state
))
{
if
(
isFinished
(
download
.
state
))
{
downloads
.
remove
(
index
);
downloads
.
remove
(
download
);
}
}
mainHandler
.
obtainMessage
(
MSG_DOWNLOAD_STATE_CHANGED
,
downloadState
).
sendToTarget
();
}
}
private
void
setNotMetRequirements
(
@Requirements
.
RequirementFlags
int
notMetRequirements
)
{
private
void
setNotMetRequirementsInternal
(
@Requirements
.
RequirementFlags
int
notMetRequirements
)
{
if
(
this
.
notMetRequirements
==
notMetRequirements
)
{
return
;
}
this
.
notMetRequirements
=
notMetRequirements
;
this
.
notMetRequirements
=
notMetRequirements
;
logdFlags
(
"Not met requirements are changed"
,
notMetRequirements
);
logdFlags
(
"Not met requirements are changed"
,
notMetRequirements
);
for
(
int
i
=
0
;
i
<
downloads
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
downloads
.
size
();
i
++)
{
...
@@ -565,35 +656,28 @@ public final class DownloadManager {
...
@@ -565,35 +656,28 @@ public final class DownloadManager {
return
null
;
return
null
;
}
}
private
void
loadDownloads
()
{
private
void
initializeInternal
(
int
notMetRequirements
)
{
DownloadState
[]
loadedStates
;
this
.
notMetRequirements
=
notMetRequirements
;
ArrayList
<
DownloadState
>
loadedStates
=
new
ArrayList
<>();
try
(
DownloadStateCursor
cursor
=
try
(
DownloadStateCursor
cursor
=
downloadIndex
.
getDownloadStates
(
downloadIndex
.
getDownloadStates
(
STATE_QUEUED
,
STATE_STOPPED
,
STATE_DOWNLOADING
,
STATE_REMOVING
,
STATE_RESTARTING
))
{
STATE_QUEUED
,
STATE_STOPPED
,
STATE_DOWNLOADING
,
STATE_REMOVING
,
STATE_RESTARTING
))
{
loadedStates
=
new
DownloadState
[
cursor
.
getCount
()];
while
(
cursor
.
moveToNext
())
{
for
(
int
i
=
0
,
length
=
loadedStates
.
length
;
i
<
length
;
i
++)
{
loadedStates
.
add
(
cursor
.
getDownloadState
());
cursor
.
moveToNext
();
loadedStates
[
i
]
=
cursor
.
getDownloadState
();
}
}
logd
(
"Download states are loaded."
);
logd
(
"Download states are loaded."
);
}
catch
(
Throwable
e
)
{
}
catch
(
Throwable
e
)
{
Log
.
e
(
TAG
,
"Download state loading failed."
,
e
);
Log
.
e
(
TAG
,
"Download state loading failed."
,
e
);
loadedStates
=
new
DownloadState
[
0
]
;
loadedStates
.
clear
()
;
}
}
for
(
DownloadState
downloadState
:
loadedStates
)
{
for
(
DownloadState
downloadState
:
loadedStates
)
{
addDownloadForState
(
downloadState
);
addDownloadForState
(
downloadState
);
}
}
logd
(
"Downloads are created."
);
logd
(
"Downloads are created."
);
mainHandler
.
post
(
this
::
onInitialized
);
mainHandler
.
obtainMessage
(
MSG_INITIALIZED
,
loadedStates
).
sendToTarget
(
);
for
(
int
i
=
0
;
i
<
downloads
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
downloads
.
size
();
i
++)
{
downloads
.
get
(
i
).
start
();
downloads
.
get
(
i
).
start
();
}
}
checkIfIdle
();
}
private
void
checkIfIdle
()
{
boolean
idle
=
activeDownloads
.
isEmpty
();
mainHandler
.
post
(()
->
onIdleStateChange
(
idle
));
}
}
private
void
addDownloadForState
(
DownloadState
downloadState
)
{
private
void
addDownloadForState
(
DownloadState
downloadState
)
{
...
@@ -650,7 +734,6 @@ public final class DownloadManager {
...
@@ -650,7 +734,6 @@ public final class DownloadManager {
DownloadThread
downloadThread
=
new
DownloadThread
(
download
);
DownloadThread
downloadThread
=
new
DownloadThread
(
download
);
activeDownloads
.
put
(
download
,
downloadThread
);
activeDownloads
.
put
(
download
,
downloadThread
);
download
.
setCounters
(
downloadThread
.
downloader
.
getCounters
());
download
.
setCounters
(
downloadThread
.
downloader
.
getCounters
());
checkIfIdle
();
logd
(
"Download is started"
,
download
);
logd
(
"Download is started"
,
download
);
return
START_THREAD_SUCCEEDED
;
return
START_THREAD_SUCCEEDED
;
}
}
...
@@ -670,20 +753,23 @@ public final class DownloadManager {
...
@@ -670,20 +753,23 @@ public final class DownloadManager {
stopDownloadThread
(
download
);
stopDownloadThread
(
download
);
}
}
internalThread
.
quit
();
internalThread
.
quit
();
synchronized
(
releaseLock
)
{
released
=
true
;
releaseLock
.
notifyAll
();
}
}
}
private
void
onDownloadThreadStopped
(
DownloadThread
downloadThread
,
Throwable
finalError
)
{
private
void
onDownloadThreadStopped
Internal
(
DownloadThread
downloadThread
)
{
Download
download
=
downloadThread
.
download
;
Download
download
=
downloadThread
.
download
;
logd
(
"Download is stopped"
,
download
);
logd
(
"Download is stopped"
,
download
);
activeDownloads
.
remove
(
download
);
activeDownloads
.
remove
(
download
);
checkIfIdle
();
boolean
tryToStartDownloads
=
false
;
boolean
tryToStartDownloads
=
false
;
if
(!
downloadThread
.
isRemoveThread
)
{
if
(!
downloadThread
.
isRemoveThread
)
{
// If maxSimultaneousDownloads was hit, there might be a download waiting for a slot.
// If maxSimultaneousDownloads was hit, there might be a download waiting for a slot.
tryToStartDownloads
=
simultaneousDownloads
==
maxSimultaneousDownloads
;
tryToStartDownloads
=
simultaneousDownloads
==
maxSimultaneousDownloads
;
simultaneousDownloads
--;
simultaneousDownloads
--;
}
}
download
.
onDownloadThreadStopped
(
downloadThread
.
isCanceled
,
finalError
);
download
.
onDownloadThreadStopped
(
downloadThread
.
isCanceled
,
downloadThread
.
finalError
);
if
(
tryToStartDownloads
)
{
if
(
tryToStartDownloads
)
{
for
(
int
i
=
0
;
for
(
int
i
=
0
;
simultaneousDownloads
<
maxSimultaneousDownloads
&&
i
<
downloads
.
size
();
simultaneousDownloads
<
maxSimultaneousDownloads
&&
i
<
downloads
.
size
();
...
@@ -726,11 +812,6 @@ public final class DownloadManager {
...
@@ -726,11 +812,6 @@ public final class DownloadManager {
}
}
public
void
addAction
(
DownloadAction
newAction
)
{
public
void
addAction
(
DownloadAction
newAction
)
{
Assertions
.
checkArgument
(
getId
().
equals
(
newAction
.
id
));
if
(!
downloadState
.
type
.
equals
(
newAction
.
type
))
{
String
format
=
"Action type (%s) doesn't match existing download type (%s)"
;
Log
.
e
(
TAG
,
String
.
format
(
format
,
newAction
.
type
,
downloadState
.
type
));
}
downloadState
=
downloadState
.
mergeAction
(
newAction
);
downloadState
=
downloadState
.
mergeAction
(
newAction
);
initialize
();
initialize
();
}
}
...
@@ -885,7 +966,9 @@ public final class DownloadManager {
...
@@ -885,7 +966,9 @@ public final class DownloadManager {
private
final
Download
download
;
private
final
Download
download
;
private
final
Downloader
downloader
;
private
final
Downloader
downloader
;
private
final
boolean
isRemoveThread
;
private
final
boolean
isRemoveThread
;
private
volatile
boolean
isCanceled
;
private
volatile
boolean
isCanceled
;
private
Throwable
finalError
;
private
DownloadThread
(
Download
download
)
{
private
DownloadThread
(
Download
download
)
{
this
.
download
=
download
;
this
.
download
=
download
;
...
@@ -905,7 +988,6 @@ public final class DownloadManager {
...
@@ -905,7 +988,6 @@ public final class DownloadManager {
@Override
@Override
public
void
run
()
{
public
void
run
()
{
logd
(
"Download started"
,
download
);
logd
(
"Download started"
,
download
);
Throwable
error
=
null
;
try
{
try
{
if
(
isRemoveThread
)
{
if
(
isRemoveThread
)
{
downloader
.
remove
();
downloader
.
remove
();
...
@@ -934,13 +1016,9 @@ public final class DownloadManager {
...
@@ -934,13 +1016,9 @@ public final class DownloadManager {
}
}
}
}
}
catch
(
Throwable
e
)
{
}
catch
(
Throwable
e
)
{
e
rror
=
e
;
finalE
rror
=
e
;
}
}
final
Throwable
finalError
=
error
;
internalHandler
.
obtainMessage
(
MSG_DOWNLOAD_THREAD_STOPPED
,
this
).
sendToTarget
();
internalHandler
.
post
(
()
->
{
onDownloadThreadStopped
(
this
,
finalError
);
});
}
}
private
int
getRetryDelayMillis
(
int
errorCount
)
{
private
int
getRetryDelayMillis
(
int
errorCount
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment