""" Android Job Scheduler API — JobScheduler, JobInfo, JobService, JobParameters, JobWorkItem 方法签名完全对齐 Android SDK """ import heapq import threading import time from datetime import datetime, timedelta from typing import Any, Dict, List, Optional, Set, Tuple, Callable from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException IllegalArgumentException = type("IllegalArgumentException", (RuntimeException,), {}) __all__ = [ "JobScheduler", "JobInfo", "JobService", "JobParameters", "JobWorkItem", ] # ═══════════════════════════════════════════════════════════════ # JobScheduler # ═══════════════════════════════════════════════════════════════ class JobScheduler(Stub): """JobScheduler — 作业调度器 (桌面线程模拟)""" def __init__(self): super().__init__() self._lock = threading.Lock() self._jobs: Dict[int, "JobInfo"] = {} self._next_id = 1 self._worker_thread: Optional[threading.Thread] = None self._running = False self._start_worker() def _start_worker(self): """启动后台工作线程""" if self._worker_thread is None or not self._worker_thread.is_alive(): self._running = True self._worker_thread = threading.Thread(target=self._run_loop, daemon=True) self._worker_thread.start() def _run_loop(self): """运行作业调度循环""" while self._running: with self._lock: now = time.time() for job_id, info in list(self._jobs.items()): if info._deadline and now >= info._deadline: self._jobs.pop(job_id, None) time.sleep(1.0) def schedule(self, job: "JobInfo") -> int: """调度一个作业""" with self._lock: job_id = self._next_id self._next_id += 1 job._id = job_id if job._periodic: job._deadline = time.time() + job._period_ms / 1000.0 elif job._deadline_ms: job._deadline = time.time() + job._deadline_ms / 1000.0 else: job._deadline = None self._jobs[job_id] = job return job_id def enqueue(self, job: "JobInfo", workItem: "JobWorkItem") -> int: """将作业加入队列""" return self.schedule(job) def cancel(self, jobId: int): """取消指定作业""" with self._lock: self._jobs.pop(jobId, None) def cancelAll(self): """取消所有作业""" with self._lock: self._jobs.clear() def getAllPendingJobs(self) -> List["JobInfo"]: """获取所有待处理作业""" with self._lock: return list(self._jobs.values()) def getPendingJob(self, jobId: int) -> Optional["JobInfo"]: """获取指定待处理作业""" with self._lock: return self._jobs.get(jobId) def getTransientNetwork(self) -> Optional[Any]: """获取瞬时网络""" return None def getStartedJobs(self) -> List["JobInfo"]: """获取已开始的作业""" return [] def toString(self) -> str: with self._lock: return f"JobScheduler(pending={len(self._jobs)})" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # JobInfo # ═══════════════════════════════════════════════════════════════ class JobInfo(Stub): """JobInfo — 作业信息""" # --- NetworkType 常量 --- NETWORK_TYPE_NONE = 0 NETWORK_TYPE_ANY = 1 NETWORK_TYPE_UNMETERED = 2 NETWORK_TYPE_NOT_ROAMING = 3 NETWORK_TYPE_CELLULAR = 4 # --- Backoff 策略 --- BACKOFF_POLICY_LINEAR = 0 BACKOFF_POLICY_EXPONENTIAL = 1 def __init__(self): super().__init__() self._id = 0 self._service = None self._required_network_type = self.NETWORK_TYPE_NONE self._requires_charging = False self._requires_device_idle = False self._deadline_ms = 0 self._min_latency_ms = 0 self._period_ms = 0 self._periodic = False self._backoff_policy = self.BACKOFF_POLICY_EXPONENTIAL self._backoff_delay_ms = 30000 self._extras = None self._transient_network = False self._estimated_network_bytes = -1 self._prefetch_network_bytes = -1 self._requires_storage_not_low = False def getId(self) -> int: return self._id def getService(self) -> Optional[Any]: return self._service def getRequiredNetworkType(self) -> int: return self._required_network_type def isRequiresCharging(self) -> bool: return self._requires_charging def isRequiresDeviceIdle(self) -> bool: return self._requires_device_idle def isRequiresStorageNotLow(self) -> bool: return self._requires_storage_not_low def getMinLatencyMillis(self) -> int: return self._min_latency_ms def getMaxExecutionDelayMillis(self) -> int: return self._deadline_ms def isPeriodic(self) -> bool: return self._periodic def getBackoffPolicy(self) -> int: return self._backoff_policy def getInitialBackoffMillis(self) -> int: return self._backoff_delay_ms def getEstimatedNetworkBytes(self) -> int: return self._estimated_network_bytes def getPrefetchNetworkBytes(self) -> int: return self._prefetch_network_bytes def getTransientNetwork(self) -> Optional[Any]: return None def isPersisted(self) -> bool: return False def getExtras(self) -> Optional[Any]: return self._extras # ═══════════════════════════════════════════════════════════ # Builder # ═══════════════════════════════════════════════════════════ class Builder(Stub): """JobInfo.Builder — 作业构建器""" def __init__(self, jobId: int, jobService: Any): super().__init__() self._info = JobInfo() self._info._id = jobId self._info._service = jobService def setRequiredNetworkType(self, networkType: int) -> "JobInfo.Builder": self._info._required_network_type = networkType return self def setRequiresCharging(self, requiresCharging: bool) -> "JobInfo.Builder": self._info._requires_charging = requiresCharging return self def setRequiresDeviceIdle(self, requiresDeviceIdle: bool) -> "JobInfo.Builder": self._info._requires_device_idle = requiresDeviceIdle return self def setRequiresStorageNotLow(self, requiresStorageNotLow: bool) -> "JobInfo.Builder": self._info._requires_storage_not_low = requiresStorageNotLow return self def setOverrideDeadline(self, deadlineMillis: int) -> "JobInfo.Builder": self._info._deadline_ms = deadlineMillis return self def setMinimumLatency(self, minLatencyMillis: int) -> "JobInfo.Builder": self._info._min_latency_ms = minLatencyMillis return self def setBackoffCriteria( self, initialBackoffMillis: int, backoffPolicy: int ) -> "JobInfo.Builder": self._info._backoff_delay_ms = initialBackoffMillis self._info._backoff_policy = backoffPolicy return self def setPeriodic(self, intervalMillis: int) -> "JobInfo.Builder": self._info._periodic = True self._info._period_ms = intervalMillis return self def setTransientNetwork(self, transient: bool) -> "JobInfo.Builder": self._info._transient_network = transient return self def setEstimatedNetworkBytes(self, estimatedBytes: int) -> "JobInfo.Builder": self._info._estimated_network_bytes = estimatedBytes return self def setPrefetchNetworkBytes(self, prefetchBytes: int) -> "JobInfo.Builder": self._info._prefetch_network_bytes = prefetchBytes return self def setExtras(self, extras: Any) -> "JobInfo.Builder": self._info._extras = extras return self def build(self) -> "JobInfo": return self._info def toString(self) -> str: return f"Builder(jobId={self._info._id})" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF def toString(self) -> str: return f"JobInfo(id={self._id}, network={self._required_network_type})" def equals(self, other) -> bool: if not isinstance(other, JobInfo): return False return self._id == other._id def hashCode(self) -> int: return self._id & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # JobService # ═══════════════════════════════════════════════════════════════ class JobService(Stub): """JobService — 作业服务基类""" def __init__(self): super().__init__() def onStartJob(self, params: "JobParameters") -> bool: """作业开始时调用。返回 True 表示需要后台线程处理。""" return False def onStopJob(self, params: "JobParameters") -> bool: """作业停止时调用。返回 True 表示应重试。""" return False def jobFinished(self, params: "JobParameters", needsReschedule: bool): """通知系统作业已完成""" pass def toString(self) -> str: return f"JobService@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return id(self) & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # JobParameters # ═══════════════════════════════════════════════════════════════ class JobParameters(Stub): """JobParameters — 作业参数""" # --- StopReason 常量 --- STOP_REASON_UNDEFINED = 0 STOP_REASON_DATA_CLEARED = 1 STOP_REASON_APP_BACKGROUND = 2 STOP_REASON_APP_STANDBY = 3 STOP_REASON_DEVICE_IDLE = 4 STOP_REASON_PREEXISTING = 5 STOP_REASON_ESTIMATED_NETWORK_BYTES_EXCEEDED = 6 STOP_REASON_QUOTA = 7 STOP_REASON_TAPPED_CANCEL = 8 STOP_REASON_USER = 9 STOP_REASON_CONSTRAINT_BATTERY_NOT_OK = 10 STOP_REASON_CONSTRAINT_NETWORK_NOT_OK = 11 STOP_REASON_CONSTRAINT_CONNECTIVITY_NOT_OK = 12 STOP_REASON_CONSTRAINT_STORAGE_NOT_OK = 13 STOP_REASON_CONSTRAINT_CHARGING_NOT_OK = 14 STOP_REASON_CONSTRAINT_IDLE_NOT_OK = 15 STOP_REASON_CANCELED = 16 STOP_REASON_CANCELLED_BY_APP = 17 STOP_REASON_DEVICE_STATE = 18 STOP_REASON_SYSTEM_PROCESSING = 19 STOP_REASON_WORK_POLICY = 20 STOP_REASON_SYSTEM_EXECUTION = 21 STOP_REASON_RESTRICTED_BACKGROUND = 22 STOP_REASON_TIMEOUT = 23 def __init__(self, jobId: int = 0): super().__init__() self._job_id = jobId self._extras = None self._network = None self._transient_network = None self._stop_reason = 0 self._override_deadline_expired = False self._requires_charging = False self._requires_device_idle = False self._requires_storage_not_low = False self._required_network_type = JobInfo.NETWORK_TYPE_NONE def getJobId(self) -> int: return self._job_id def getExtras(self) -> Optional[Any]: return self._extras def getNetwork(self) -> Optional[Any]: return self._network def getTransientNetwork(self) -> Optional[Any]: return self._transient_network def getStopReason(self) -> int: return self._stop_reason def isOverrideDeadlineExpired(self) -> bool: return self._override_deadline_expired def isRequiresCharging(self) -> bool: return self._requires_charging def isRequiresDeviceIdle(self) -> bool: return self._requires_device_idle def isRequiresStorageNotLow(self) -> bool: return self._requires_storage_not_low def getRequiredNetworkType(self) -> int: return self._required_network_type def toString(self) -> str: return f"JobParameters(jobId={self._job_id})" def equals(self, other) -> bool: if not isinstance(other, JobParameters): return False return self._job_id == other._job_id def hashCode(self) -> int: return self._job_id & 0x7FFFFFFF # ═══════════════════════════════════════════════════════════════ # JobWorkItem # ═══════════════════════════════════════════════════════════════ class JobWorkItem(Stub): """JobWorkItem — 作业工作项""" def __init__(self, intent: Any = None): super().__init__() self._intent = intent self._delivery_count = 0 self._latest_delivery_intent = None def getIntent(self) -> Optional[Any]: return self._intent def getDeliveryCount(self) -> int: return self._delivery_count def getLatestDeliveryIntent(self) -> Optional[Any]: return self._latest_delivery_intent def toString(self) -> str: return f"JobWorkItem(deliveryCount={self._delivery_count})" def equals(self, other) -> bool: if not isinstance(other, JobWorkItem): return False return self._intent == other._intent def hashCode(self) -> int: return hash(str(self._intent)) & 0x7FFFFFFF