feat: 大幅增强原生行为 — 修复P0 Bug + 补全核心API

P0 Bug 修复:
- Canvas.drawBitmap 重复定义导致重载丢失 → 合并为*args重载
- String.hashCode() 不可达的第二return → 修复为单return
- File.compareTo() 调用不存在的__cmp__ → 改为显式比较
- HashMap.remove() 逻辑错误 → 区分单参(返回旧值)/双参(返回boolean)

P1 核心行为增强:
- Handler: sendMessage/sendMessageDelayed 现在真正入队异步执行
- Looper: loop() 实现真正的消息循环(按when时间取出到期消息)
- AsyncTask: execute() 在后台线程池执行,完成后主线程回调
- Bundle: 新增12个数组类型put/get方法
- Math.round: 改为Java语义(floor(a+0.5))而非Python银行家舍入
- Thread: currentThread()缓存对象, 新增join/getState/getId/getPriority
- String: 新增strip/repeat/isBlank/lines/subSequence/getChars/chars/formatted/translateEscapes

P2 缺失API补全:
- 新增21个Java标准Exception类 (NullPointerException等)
- View: 新增39个事件处理/监听器/位置尺寸方法
- Activity: 新增33个生命周期/权限/UI方法
- Context: 新增22个文件流/权限/资源/主题方法
- Canvas: 支持 Canvas(Bitmap) 构造函数重载
- __init__.py: __all__ 补全Exception体系和Activity类
This commit is contained in:
shuiyu1123 committed 2026-06-24 15:04:17 +00:00
1 parent 4645015004
commit 8c3bd3c415
9 files changed
+1017 -52

No files matched your search

+6 -2
View File
@@ -416,9 +416,13 @@ class File(Stub):
except OSError:
return 0
def compareTo(self, other):
def compareTo(self, other) -> int:
if isinstance(other, File):
return self._path.__cmp__(other._path)
if self._path < other._path:
return -1
elif self._path > other._path:
return 1
return 0
return -1
def equals(self, other):
+346 -12
View File
@@ -18,6 +18,16 @@ from datetime import datetime as _datetime
__all__ = [
"Object", "Throwable", "Exception", "RuntimeException", "Error",
"NullPointerException", "IllegalArgumentException", "IllegalStateException",
"IndexOutOfBoundsException", "ArrayIndexOutOfBoundsException",
"StringIndexOutOfBoundsException", "ClassCastException",
"UnsupportedOperationException", "ArithmeticException",
"NumberFormatException", "SecurityException",
"ConcurrentModificationException", "NegativeArraySizeException",
"NoSuchFieldException", "NoSuchMethodException", "InterruptedException",
"CloneNotSupportedException", "IllegalMonitorStateException",
"IllegalThreadStateException", "EnumConstantNotPresentException",
"TypeNotPresentException",
"String", "Integer", "Long", "Short", "Byte", "Float", "Double",
"Boolean", "Character", "Math", "StrictMath", "System",
"Thread", "ThreadLocal", "StringBuilder", "StringBuffer",
@@ -148,6 +158,75 @@ class Error(Throwable):
pass
class NullPointerException(RuntimeException):
pass
class IllegalArgumentException(RuntimeException):
pass
class IllegalStateException(RuntimeException):
pass
class IndexOutOfBoundsException(RuntimeException):
pass
class ArrayIndexOutOfBoundsException(IndexOutOfBoundsException):
pass
class StringIndexOutOfBoundsException(IndexOutOfBoundsException):
pass
class ClassCastException(RuntimeException):
pass
class UnsupportedOperationException(RuntimeException):
pass
class ArithmeticException(RuntimeException):
pass
class NumberFormatException(IllegalArgumentException):
pass
class SecurityException(RuntimeException):
pass
class ConcurrentModificationException(RuntimeException):
pass
class NegativeArraySizeException(RuntimeException):
pass
class NoSuchFieldException(Exception):
pass
class NoSuchMethodException(Exception):
pass
class InterruptedException(Exception):
pass
class CloneNotSupportedException(Exception):
pass
class IllegalMonitorStateException(RuntimeException):
pass
class IllegalThreadStateException(IllegalArgumentException):
pass
class EnumConstantNotPresentException(RuntimeException):
def __init__(self, e, constant):
super().__init__(f"{e}.{constant}")
self.enumType = e
self.constantName = constant
class TypeNotPresentException(RuntimeException):
def __init__(self, typeName, cause=None):
super().__init__(f"Type {typeName} not present", cause)
self.typeName = typeName
# ============================================================
# StackTraceElement
# ============================================================
@@ -277,11 +356,10 @@ class String(CharSequence):
def getBytes(self, charset="UTF-8"):
return self._str.encode(charset, errors="replace")
def hashCode(self):
def hashCode(self) -> int:
h = 0
for c in self._str:
h = (31 * h + ord(c)) & 0xFFFFFFFF
return h if h < 0x80000000 else h - 0x100000000
for i in range(len(self._str)):
h = 31 * h + ord(self._str[i])
return h & 0x7FFFFFFF
def indexOf(self, ch, fromIndex=0):
@@ -362,6 +440,89 @@ class String(CharSequence):
def trim(self):
return String(self._str.strip())
def strip(self):
return String(self._str.strip())
def stripLeading(self):
return String(self._str.lstrip())
def stripTrailing(self):
return String(self._str.rstrip())
def isBlank(self):
return self._str.strip() == ""
def repeat(self, count):
return String(self._str * count)
def lines(self):
from .util import ArrayList
result = ArrayList()
for line in self._str.splitlines():
result.add(String(line))
return result
def subSequence(self, start, end):
return self.substring(start, end)
def getChars(self, srcBegin, srcEnd, dst, dstBegin):
chars = self._str[srcBegin:srcEnd]
for i, c in enumerate(chars):
dst[dstBegin + i] = c
def chars(self):
return [ord(c) for c in self._str]
def formatted(self, *args):
return String(self._str.format(*args))
def translateEscapes(self):
result = []
s = self._str
i = 0
n = len(s)
while i < n:
c = s[i]
if c == '\\' and i + 1 < n:
nxt = s[i + 1]
if nxt == 'b':
result.append('\b'); i += 2
elif nxt == 't':
result.append('\t'); i += 2
elif nxt == 'n':
result.append('\n'); i += 2
elif nxt == 'f':
result.append('\f'); i += 2
elif nxt == 'r':
result.append('\r'); i += 2
elif nxt == 's':
result.append(' '); i += 2
elif nxt == '"':
result.append('"'); i += 2
elif nxt == "'":
result.append("'"); i += 2
elif nxt == '\\':
result.append('\\'); i += 2
elif nxt in '01234567':
octal = nxt
i += 2
max_digits = 2 if nxt in '0123' else 1
while len(octal) < max_digits + 1 and i < n and s[i] in '01234567':
octal += s[i]
i += 1
result.append(chr(int(octal, 8)))
elif nxt == '\n':
i += 2
elif nxt == '\r':
i += 2
if i < n and s[i] == '\n':
i += 1
else:
result.append(nxt); i += 2
else:
result.append(c); i += 1
return String(''.join(result))
@staticmethod
def valueOf(obj):
return String(str(obj))
@@ -1200,7 +1361,8 @@ class Math:
@staticmethod
def floor(a): return int(math.floor(a))
@staticmethod
def round(a): return round(a)
def round(a: float) -> int:
return int(math.floor(a + 0.5))
@staticmethod
def min(a, b): return a if a < b else b
@staticmethod
@@ -1266,8 +1428,58 @@ class StrictMath(Math):
pass
class _Console:
"""java.io.Console 简化实现,供 System.console() 使用"""
def readLine(self, fmt=None, *args):
if fmt is not None:
try:
_sys.stdout.write(str(fmt) % args if args else str(fmt))
except Exception:
_sys.stdout.write(str(fmt))
_sys.stdout.flush()
line = _sys.stdin.readline()
if line == "":
return None
return line.rstrip("\r\n")
def readPassword(self, fmt=None, *args):
import getpass
if fmt is not None:
try:
_sys.stderr.write(str(fmt) % args if args else str(fmt))
except Exception:
_sys.stderr.write(str(fmt))
_sys.stderr.flush()
pwd = getpass.getpass("")
return list(pwd) if pwd is not None else None
def printf(self, fmt, *args):
try:
_sys.stdout.write(str(fmt) % args if args else str(fmt))
except Exception:
_sys.stdout.write(str(fmt))
return self
def format(self, fmt, *args):
try:
_sys.stdout.write(str(fmt) % args if args else str(fmt))
except Exception:
_sys.stdout.write(str(fmt))
return self
def flush(self):
_sys.stdout.flush()
def writer(self):
return _sys.stdout
def reader(self):
return _sys.stdin
class System:
"""java.lang.System"""
_console = None
@staticmethod
def currentTimeMillis(): return int(time.time() * 1000)
@staticmethod
@@ -1294,6 +1506,28 @@ class System:
os.environ[key] = value
return value
@staticmethod
def clearProperty(key):
return _os.environ.pop(key, None)
@staticmethod
def getenv(name=None):
if name is None:
return dict(_os.environ)
return _os.environ.get(name)
@staticmethod
def console():
if System._console is None:
System._console = _Console()
return System._console
@staticmethod
def setIn(in_stream):
System.in_ = in_stream
@staticmethod
def setOut(out_stream):
System.out = out_stream
@staticmethod
def setErr(err_stream):
System.err = err_stream
@staticmethod
def lineSeparator(): return _os.linesep
in_ = _sys.stdin
out = _sys.stdout
@@ -1306,6 +1540,18 @@ class Thread(Runnable):
NORM_PRIORITY = 5
MAX_PRIORITY = 10
# Thread.State 常量(java.lang.Thread.State)
NEW = "NEW"
RUNNABLE = "RUNNABLE"
BLOCKED = "BLOCKED"
WAITING = "WAITING"
TIMED_WAITING = "TIMED_WAITING"
TERMINATED = "TERMINATED"
# 按 Python 线程 ident 缓存 Thread 对象,供 currentThread() 复用
_thread_cache = {}
_default_uncaught_exception_handler = None
def __init__(self, target=None, name=None):
super().__init__()
self._target = target
@@ -1313,25 +1559,91 @@ class Thread(Runnable):
self._alive = False
self._daemon = False
self._interrupted = False
self._priority = Thread.NORM_PRIORITY
self._state = Thread.NEW
self._py_thread = None
self._id = _threading.get_ident()
def start(self):
self._alive = True
if self._target:
_threading.Thread(target=self.run, daemon=self._daemon).start()
self._state = Thread.RUNNABLE
self._py_thread = _threading.Thread(
target=self._run_wrapper, daemon=self._daemon, name=self._name
)
self._py_thread.start()
self._id = self._py_thread.ident
def _run_wrapper(self):
tid = _threading.get_ident()
self._id = tid
Thread._thread_cache[tid] = self
try:
self.run()
except (SystemExit, KeyboardInterrupt):
raise
except BaseException as e:
# 注意:模块内 Exception 已被 java.lang.Exception 覆盖,
# 这里用 BaseException 捕获 Python 原生异常并转发给未捕获异常处理器
handler = Thread._default_uncaught_exception_handler
if handler is not None:
try:
handler.uncaughtException(self, e)
except BaseException:
pass
finally:
self._alive = False
self._state = Thread.TERMINATED
Thread._thread_cache.pop(tid, None)
def run(self):
if self._target:
self._target()
def join(self, millis=0):
if self._py_thread is not None:
if millis and millis > 0:
self._py_thread.join(millis / 1000.0)
else:
self._py_thread.join()
def interrupt(self):
self._interrupted = True
def isInterrupted(self):
return self._interrupted
@staticmethod
def interrupted():
tid = _threading.get_ident()
t = Thread._thread_cache.get(tid)
if t is not None:
status = t._interrupted
t._interrupted = False
return status
return False
def isAlive(self):
if self._py_thread is not None:
return self._py_thread.is_alive()
return self._alive
def getState(self):
if self._py_thread is not None:
if self._py_thread.is_alive():
return Thread.RUNNABLE
return Thread.TERMINATED
return self._state
def getPriority(self):
return self._priority
def setPriority(self, priority):
if Thread.MIN_PRIORITY <= priority <= Thread.MAX_PRIORITY:
self._priority = priority
def getId(self):
return self._id
def getName(self): return self._name
def setName(self, n): self._name = n
def setDaemon(self, d): self._daemon = d
@@ -1339,15 +1651,37 @@ class Thread(Runnable):
@staticmethod
def currentThread():
return Thread(name="main")
tid = _threading.get_ident()
t = Thread._thread_cache.get(tid)
if t is not None:
return t
# 未缓存的线程(如主线程):创建并缓存一个代表对象
try:
is_main = (tid == _threading.main_thread().ident)
except Exception:
is_main = False
t = Thread(name="main" if is_main else f"Thread-{tid}")
t._state = Thread.RUNNABLE
t._alive = True
t._id = tid
Thread._thread_cache[tid] = t
return t
@staticmethod
def sleep(millis):
import time
time.sleep(millis / 1000.0)
_time.sleep(millis / 1000.0)
@staticmethod
def yield_():
import time
time.sleep(0)
_time.sleep(0)
@staticmethod
def setDefaultUncaughtExceptionHandler(handler):
Thread._default_uncaught_exception_handler = handler
@staticmethod
def getDefaultUncaughtExceptionHandler():
return Thread._default_uncaught_exception_handler
class ThreadLocal(Stub):
+14 -7
View File
@@ -666,13 +666,20 @@ class HashMap(Stub):
return None
return self._data[key]
def remove(self, key, value=None) -> bool:
if value is None:
return self._data.pop(key, None) is not None or key in self._data
if key in self._data and self._data[key] == value:
del self._data[key]
return True
return False
def remove(self, key, value=None):
if value is not None:
# remove(key, value) — 返回 boolean
if key in self._data and self._data[key] == value:
del self._data[key]
return True
return False
else:
# remove(key) — 返回被删除的 value(与 Java 语义一致)
if key in self._data:
old = self._data[key]
del self._data[key]
return old
return None
def replace(self, key, old_value=None, new_value=None):
if old_value is None and new_value is None: