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:
1 parent
4645015004
commit
8c3bd3c415
9 files changed
+1017
-52
No files matched your search
@@ -378,7 +378,7 @@ __all__ = [
|
|||||||
"AccessibilityNodeInfo", "AccessibilityService",
|
"AccessibilityNodeInfo", "AccessibilityService",
|
||||||
"AccessibilityServiceInfo",
|
"AccessibilityServiceInfo",
|
||||||
"Account", "AccountManager",
|
"Account", "AccountManager",
|
||||||
"ActivityManager",
|
"Activity", "ActivityManager",
|
||||||
"AlphaAnimation",
|
"AlphaAnimation",
|
||||||
"Animation",
|
"Animation",
|
||||||
"Animator", "ValueAnimator", "ObjectAnimator", "AnimatorSet",
|
"Animator", "ValueAnimator", "ObjectAnimator", "AnimatorSet",
|
||||||
@@ -512,6 +512,18 @@ __all__ = [
|
|||||||
"String", "StringBuilder", "StringBuffer",
|
"String", "StringBuilder", "StringBuffer",
|
||||||
"Integer", "Math", "System", "Thread",
|
"Integer", "Math", "System", "Thread",
|
||||||
"BigInteger", "BigDecimal",
|
"BigInteger", "BigDecimal",
|
||||||
|
# Java Exception 体系
|
||||||
|
"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",
|
||||||
"SimpleDateFormat", "DecimalFormat",
|
"SimpleDateFormat", "DecimalFormat",
|
||||||
"LocalDateTime", "LocalDate", "LocalTime", "Instant", "Duration", "Period",
|
"LocalDateTime", "LocalDate", "LocalTime", "Instant", "Duration", "Period",
|
||||||
"ChronoUnit", "ChronoField", "DayOfWeek", "Month", "ZoneId", "ZoneOffset",
|
"ChronoUnit", "ChronoField", "DayOfWeek", "Month", "ZoneId", "ZoneOffset",
|
||||||
|
|||||||
@@ -1698,6 +1698,129 @@ class Activity(Stub):
|
|||||||
def onPictureInPictureModeChanged(self, is_in_pip_mode: bool, new_config):
|
def onPictureInPictureModeChanged(self, is_in_pip_mode: bool, new_config):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# ─── 生命周期 (扩展) ───────────────────────────────────────
|
||||||
|
|
||||||
|
def onPostCreate(self, savedInstanceState):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onPostResume(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onUserLeaveHint(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onUserInteraction(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onCreateContextMenu(self, menu, v, menuInfo):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onContextMenuClosed(self, menu):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onContextItemSelected(self, item):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onWindowFocusChanged(self, hasFocus):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onWindowAttributesChanged(self, params):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onContentChanged(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onAttachedToWindow(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onDetachedFromWindow(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onTitleChanged(self, title, color):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ─── 权限 (扩展) ───────────────────────────────────────────
|
||||||
|
|
||||||
|
def requestPermissions(self, permissions, requestCode):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def checkSelfPermission(self, permission):
|
||||||
|
return 0 # PERMISSION_GRANTED
|
||||||
|
|
||||||
|
def shouldShowRequestPermissionRationale(self, permission):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# ─── UI (扩展) ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
def getWindow(self):
|
||||||
|
if not hasattr(self, '_window'):
|
||||||
|
from .view import Window
|
||||||
|
self._window = Window()
|
||||||
|
return self._window
|
||||||
|
|
||||||
|
def getWindowManager(self):
|
||||||
|
if not hasattr(self, '_window_manager'):
|
||||||
|
from .view import WindowManager
|
||||||
|
self._window_manager = WindowManager()
|
||||||
|
return self._window_manager
|
||||||
|
|
||||||
|
def setRequestedOrientation(self, requestedOrientation):
|
||||||
|
self._requested_orientation = requestedOrientation
|
||||||
|
|
||||||
|
def getRequestedOrientation(self):
|
||||||
|
return getattr(self, '_requested_orientation', 0)
|
||||||
|
|
||||||
|
def runOnUiThread(self, action):
|
||||||
|
import threading
|
||||||
|
# 在桌面环境直接执行
|
||||||
|
action()
|
||||||
|
|
||||||
|
def overridePendingTransition(self, enterAnim, exitAnim):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getActionBar(self):
|
||||||
|
return getattr(self, '_action_bar', None)
|
||||||
|
|
||||||
|
def setActionBar(self, toolbar):
|
||||||
|
self._action_bar = toolbar
|
||||||
|
|
||||||
|
def getMenuInflater(self):
|
||||||
|
from .view import MenuInflater
|
||||||
|
return MenuInflater()
|
||||||
|
|
||||||
|
def onCreateOptionsMenu(self, menu):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def onPrepareOptionsMenu(self, menu):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def onOptionsItemSelected(self, item):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def registerForContextMenu(self, view):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unregisterForContextMenu(self, view):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def openContextMenu(self, view):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def finishAffinity(self):
|
||||||
|
self.finish()
|
||||||
|
|
||||||
|
def invalidateOptionsMenu(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setVisible(self, visible):
|
||||||
|
self._visible = visible
|
||||||
|
|
||||||
|
def setTheme(self, resid):
|
||||||
|
self._theme = resid
|
||||||
|
|
||||||
|
def getTheme(self):
|
||||||
|
return getattr(self, '_theme', 0)
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# Service
|
# Service
|
||||||
|
|||||||
@@ -743,6 +743,86 @@ class Context(Stub):
|
|||||||
def toString(self) -> str:
|
def toString(self) -> str:
|
||||||
return f"Context{{{self._package_name}}}"
|
return f"Context{{{self._package_name}}}"
|
||||||
|
|
||||||
|
# ─── 文件流 / 权限 (扩展) ─────────────────────────────────
|
||||||
|
|
||||||
|
def openFileInput(self, name):
|
||||||
|
path = os.path.join(self.getFilesDir(), name)
|
||||||
|
return open(path, 'rb')
|
||||||
|
|
||||||
|
def openFileOutput(self, name, mode):
|
||||||
|
path = os.path.join(self.getFilesDir(), name)
|
||||||
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||||
|
return open(path, 'wb')
|
||||||
|
|
||||||
|
def getFileStreamPath(self, name):
|
||||||
|
from ..java.io import File
|
||||||
|
return File(self.getFilesDir(), name)
|
||||||
|
|
||||||
|
def grantUriPermission(self, toPackage, uri, modeFlags):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def revokeUriPermission(self, uri, modeFlags):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def checkUriPermission(self, uri, pid, uid, modeFlags):
|
||||||
|
return 0 # PERMISSION_GRANTED
|
||||||
|
|
||||||
|
def checkPermission(self, permission, pid, uid):
|
||||||
|
return 0 # PERMISSION_GRANTED
|
||||||
|
|
||||||
|
def enforceUriPermission(self, uri, pid, uid, modeFlags, message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ─── 资源 / 主题 (扩展) ───────────────────────────────────
|
||||||
|
|
||||||
|
def getApplicationInfo(self):
|
||||||
|
from .content_pm import ApplicationInfo
|
||||||
|
return ApplicationInfo()
|
||||||
|
|
||||||
|
def getTheme(self):
|
||||||
|
return getattr(self, '_theme', 0)
|
||||||
|
|
||||||
|
def setTheme(self, resid):
|
||||||
|
self._theme = resid
|
||||||
|
|
||||||
|
def getText(self, resId):
|
||||||
|
return str(resId)
|
||||||
|
|
||||||
|
def getQuantityString(self, resId, quantity, *formatArgs):
|
||||||
|
return str(resId)
|
||||||
|
|
||||||
|
def getStringArray(self, resId):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def getTextArray(self, resId):
|
||||||
|
return []
|
||||||
|
|
||||||
|
def getIntArray(self, resId):
|
||||||
|
return []
|
||||||
|
|
||||||
|
# ─── Context 派生 / 回调 (扩展) ───────────────────────────
|
||||||
|
|
||||||
|
def createConfigurationContext(self, overrideConfiguration):
|
||||||
|
return Context()
|
||||||
|
|
||||||
|
def createDisplayContext(self, display):
|
||||||
|
return Context()
|
||||||
|
|
||||||
|
def registerComponentCallbacks(self, callback):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def unregisterComponentCallbacks(self, callback):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def getExternalStoragePublicDirectory(self, type_):
|
||||||
|
from ..java.io import File
|
||||||
|
from .os import Environment
|
||||||
|
return Environment.getExternalStoragePublicDirectory(type_)
|
||||||
|
|
||||||
|
def getDisplayMetrics(self):
|
||||||
|
from .util import DisplayMetrics
|
||||||
|
return DisplayMetrics()
|
||||||
|
|
||||||
|
|
||||||
class ContextWrapper(Context):
|
class ContextWrapper(Context):
|
||||||
"""ContextWrapper — Context 的包装器"""
|
"""ContextWrapper — Context 的包装器"""
|
||||||
|
|||||||
@@ -2404,8 +2404,25 @@ class Canvas(Stub):
|
|||||||
|
|
||||||
ALL_SAVE_FLAG = 0x1F
|
ALL_SAVE_FLAG = 0x1F
|
||||||
|
|
||||||
def __init__(self, width: int = 0, height: int = 0):
|
def __init__(self, width=0, height=0):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
# 支持 Canvas(Bitmap) 重载
|
||||||
|
if isinstance(width, Bitmap):
|
||||||
|
bitmap = width
|
||||||
|
self._bitmap = bitmap
|
||||||
|
self._width = bitmap.getWidth()
|
||||||
|
self._height = bitmap.getHeight()
|
||||||
|
if HAS_PIL:
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
self._image = bitmap._image if hasattr(bitmap, '_image') and bitmap._image else None
|
||||||
|
self._draw = ImageDraw.Draw(self._image) if self._image else None
|
||||||
|
else:
|
||||||
|
self._pixels = None
|
||||||
|
self._save_count = 0
|
||||||
|
self._matrix_stack = [Matrix()]
|
||||||
|
self._clip_stack = [Rect(0, 0, self._width, self._height)]
|
||||||
|
self._matrix = Matrix()
|
||||||
|
return
|
||||||
if HAS_PIL:
|
if HAS_PIL:
|
||||||
from PIL import Image, ImageDraw
|
from PIL import Image, ImageDraw
|
||||||
self._width = width
|
self._width = width
|
||||||
@@ -2685,21 +2702,43 @@ class Canvas(Stub):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def drawBitmap(self, bitmap: Bitmap, left: float, top: float, paint: Paint = None):
|
def drawBitmap(self, *args):
|
||||||
if HAS_PIL and self._draw is not None and hasattr(bitmap, "_image") and bitmap._image:
|
"""drawBitmap 重载: (bitmap,left,top,paint) / (bitmap,src,dst,paint) / (bitmap,matrix,paint)"""
|
||||||
self._image.paste(bitmap._image, (int(left), int(top)))
|
if len(args) == 4:
|
||||||
elif HAS_PIL and self._image is not None:
|
bitmap, a, b, paint = args
|
||||||
pass
|
if isinstance(a, (int, float)) and isinstance(b, (int, float)):
|
||||||
|
if HAS_PIL and self._draw is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||||
def drawBitmap(self, bitmap: Bitmap, src: Rect, dst: RectF, paint: Paint = None):
|
self._image.paste(bitmap._image, (int(a), int(b)))
|
||||||
if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image:
|
elif HAS_PIL and self._image is not None:
|
||||||
if src:
|
pass
|
||||||
cropped = bitmap._image.crop((src.left, src.top, src.right, src.bottom))
|
|
||||||
resized = cropped.resize((int(dst.width()), int(dst.height())))
|
|
||||||
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
|
||||||
else:
|
else:
|
||||||
resized = bitmap._image.resize((int(dst.width()), int(dst.height())))
|
src, dst = a, b
|
||||||
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||||
|
if src:
|
||||||
|
cropped = bitmap._image.crop((src.left, src.top, src.right, src.bottom))
|
||||||
|
resized = cropped.resize((int(dst.width()), int(dst.height())))
|
||||||
|
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
||||||
|
else:
|
||||||
|
resized = bitmap._image.resize((int(dst.width()), int(dst.height())))
|
||||||
|
self._image.paste(resized, (int(dst.left), int(dst.top)))
|
||||||
|
elif len(args) == 3:
|
||||||
|
bitmap, matrix, paint = args
|
||||||
|
if HAS_PIL and self._image is not None and hasattr(bitmap, "_image") and bitmap._image:
|
||||||
|
try:
|
||||||
|
from PIL import Image
|
||||||
|
inv = Matrix()
|
||||||
|
if hasattr(matrix, "invert") and matrix.invert(inv):
|
||||||
|
v = inv.getValues()
|
||||||
|
elif hasattr(matrix, "getValues"):
|
||||||
|
v = matrix.getValues()
|
||||||
|
else:
|
||||||
|
v = [1, 0, 0, 0, 1, 0, 0, 0, 1]
|
||||||
|
transformed = bitmap._image.transform(
|
||||||
|
bitmap._image.size, Image.AFFINE,
|
||||||
|
(v[0], v[1], v[2], v[3], v[4], v[5]))
|
||||||
|
self._image.paste(transformed, (0, 0))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
def drawText(self, text: str, x: float, y: float, paint: Paint):
|
def drawText(self, text: str, x: float, y: float, paint: Paint):
|
||||||
if HAS_PIL and self._draw is not None:
|
if HAS_PIL and self._draw is not None:
|
||||||
|
|||||||
+214
-15
@@ -466,6 +466,61 @@ class Bundle(BaseBundle):
|
|||||||
def putIntegerArrayList(self, key: str, value: Optional[List[int]]):
|
def putIntegerArrayList(self, key: str, value: Optional[List[int]]):
|
||||||
self._map[key] = value
|
self._map[key] = value
|
||||||
|
|
||||||
|
# ---- 数组类型 ----
|
||||||
|
def putIntArray(self, key: str, value: Optional[List[int]]):
|
||||||
|
self._map[key] = list(value) if value is not None else None
|
||||||
|
|
||||||
|
def getIntArray(self, key: str) -> Optional[List[int]]:
|
||||||
|
v = self._map.get(key)
|
||||||
|
if isinstance(v, list):
|
||||||
|
return [int(x) for x in v]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def putStringArray(self, key: str, value: Optional[List[str]]):
|
||||||
|
self._map[key] = list(value) if value is not None else None
|
||||||
|
|
||||||
|
def getStringArray(self, key: str) -> Optional[List[str]]:
|
||||||
|
v = self._map.get(key)
|
||||||
|
if isinstance(v, list):
|
||||||
|
return [str(x) for x in v]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def putLongArray(self, key: str, value: Optional[List[int]]):
|
||||||
|
self._map[key] = list(value) if value is not None else None
|
||||||
|
|
||||||
|
def getLongArray(self, key: str) -> Optional[List[int]]:
|
||||||
|
v = self._map.get(key)
|
||||||
|
if isinstance(v, list):
|
||||||
|
return [int(x) for x in v]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def putBooleanArray(self, key: str, value: Optional[List[bool]]):
|
||||||
|
self._map[key] = list(value) if value is not None else None
|
||||||
|
|
||||||
|
def getBooleanArray(self, key: str) -> Optional[List[bool]]:
|
||||||
|
v = self._map.get(key)
|
||||||
|
if isinstance(v, list):
|
||||||
|
return [bool(x) for x in v]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def putDoubleArray(self, key: str, value: Optional[List[float]]):
|
||||||
|
self._map[key] = list(value) if value is not None else None
|
||||||
|
|
||||||
|
def getDoubleArray(self, key: str) -> Optional[List[float]]:
|
||||||
|
v = self._map.get(key)
|
||||||
|
if isinstance(v, list):
|
||||||
|
return [float(x) for x in v]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def putByteArray(self, key: str, value: Optional[List[int]]):
|
||||||
|
self._map[key] = list(value) if value is not None else None
|
||||||
|
|
||||||
|
def getByteArray(self, key: str) -> Optional[List[int]]:
|
||||||
|
v = self._map.get(key)
|
||||||
|
if isinstance(v, list):
|
||||||
|
return [int(x) & 0xFF for x in v]
|
||||||
|
return None
|
||||||
|
|
||||||
def putAll(self, bundle: "Bundle"):
|
def putAll(self, bundle: "Bundle"):
|
||||||
if bundle:
|
if bundle:
|
||||||
self._map.update(bundle._map)
|
self._map.update(bundle._map)
|
||||||
@@ -729,6 +784,14 @@ class Message(Stub):
|
|||||||
def setTarget(self, handler: "Handler"):
|
def setTarget(self, handler: "Handler"):
|
||||||
self._target = handler
|
self._target = handler
|
||||||
|
|
||||||
|
@property
|
||||||
|
def target(self) -> Optional["Handler"]:
|
||||||
|
return self._target
|
||||||
|
|
||||||
|
@target.setter
|
||||||
|
def target(self, handler: "Handler"):
|
||||||
|
self._target = handler
|
||||||
|
|
||||||
def getCallback(self) -> Optional[Callable]:
|
def getCallback(self) -> Optional[Callable]:
|
||||||
return self._callback
|
return self._callback
|
||||||
|
|
||||||
@@ -769,15 +832,17 @@ class Looper(Stub):
|
|||||||
_main_looper: Optional["Looper"] = None
|
_main_looper: Optional["Looper"] = None
|
||||||
_thread_local = threading.local()
|
_thread_local = threading.local()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, quit_allowed: bool = True):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._thread = threading.current_thread()
|
self._thread = threading.current_thread()
|
||||||
self._running = False
|
self._running = False
|
||||||
self._message_queue: List[Message] = []
|
self._message_queue: List[Message] = []
|
||||||
self._quit_allowed = True
|
self._queue_lock = threading.Lock()
|
||||||
|
self._quit_allowed = quit_allowed
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def prepare():
|
def prepare():
|
||||||
|
"""为当前线程准备一个 Looper"""
|
||||||
looper = Looper()
|
looper = Looper()
|
||||||
Looper._thread_local.looper = looper
|
Looper._thread_local.looper = looper
|
||||||
return looper
|
return looper
|
||||||
@@ -799,16 +864,76 @@ class Looper(Stub):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def loop():
|
def loop():
|
||||||
|
"""运行消息循环,处理到期的消息"""
|
||||||
looper = Looper.myLooper()
|
looper = Looper.myLooper()
|
||||||
if looper is None:
|
if looper is None:
|
||||||
raise RuntimeError("No Looper prepared")
|
raise RuntimeError("No Looper prepared")
|
||||||
looper._running = True
|
looper._running = True
|
||||||
|
while looper._running:
|
||||||
|
msg = looper._next()
|
||||||
|
if msg is not None:
|
||||||
|
target = msg._target
|
||||||
|
if target is not None:
|
||||||
|
target.dispatchMessage(msg)
|
||||||
|
msg.recycle()
|
||||||
|
else:
|
||||||
|
# 无到期消息,短暂 sleep 避免忙等
|
||||||
|
_time.sleep(0.001)
|
||||||
|
|
||||||
|
def _next(self) -> Optional[Message]:
|
||||||
|
"""非阻塞地取出一条到期消息(when <= now)"""
|
||||||
|
with self._queue_lock:
|
||||||
|
now = SystemClock.uptimeMillis()
|
||||||
|
for i, msg in enumerate(self._message_queue):
|
||||||
|
if msg.when <= now:
|
||||||
|
return self._message_queue.pop(i)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _enqueue(self, msg: Message):
|
||||||
|
"""将消息按 when 时间顺序入队"""
|
||||||
|
with self._queue_lock:
|
||||||
|
inserted = False
|
||||||
|
for i, m in enumerate(self._message_queue):
|
||||||
|
if msg.when < m.when:
|
||||||
|
self._message_queue.insert(i, msg)
|
||||||
|
inserted = True
|
||||||
|
break
|
||||||
|
if not inserted:
|
||||||
|
self._message_queue.append(msg)
|
||||||
|
|
||||||
|
def _remove_callbacks(self, callback):
|
||||||
|
"""从队列中移除匹配 callback 的消息"""
|
||||||
|
with self._queue_lock:
|
||||||
|
self._message_queue = [
|
||||||
|
m for m in self._message_queue
|
||||||
|
if m._callback is not callback
|
||||||
|
]
|
||||||
|
|
||||||
|
def _remove_messages(self, what: int):
|
||||||
|
"""从队列中移除匹配 what 的消息"""
|
||||||
|
with self._queue_lock:
|
||||||
|
self._message_queue = [
|
||||||
|
m for m in self._message_queue
|
||||||
|
if m.what != what
|
||||||
|
]
|
||||||
|
|
||||||
|
def _has_messages(self, what: int) -> bool:
|
||||||
|
"""检查队列中是否有匹配 what 的消息"""
|
||||||
|
with self._queue_lock:
|
||||||
|
return any(m.what == what for m in self._message_queue)
|
||||||
|
|
||||||
def quit(self):
|
def quit(self):
|
||||||
self._running = False
|
"""退出消息循环"""
|
||||||
|
if not self._quit_allowed:
|
||||||
|
raise RuntimeError("Main Looper is not allowed to quit")
|
||||||
|
with self._queue_lock:
|
||||||
|
self._running = False
|
||||||
|
self._message_queue.clear()
|
||||||
|
|
||||||
def quitSafely(self):
|
def quitSafely(self):
|
||||||
self._running = False
|
"""安全退出,仅停止循环(已入队消息不再处理)"""
|
||||||
|
with self._queue_lock:
|
||||||
|
self._running = False
|
||||||
|
|
||||||
def isCurrentThread(self) -> bool:
|
def isCurrentThread(self) -> bool:
|
||||||
return threading.current_thread() == self._thread
|
return threading.current_thread() == self._thread
|
||||||
@@ -858,7 +983,6 @@ class Handler(Stub):
|
|||||||
def postDelayed(self, r: Callable, delay_ms: int) -> bool:
|
def postDelayed(self, r: Callable, delay_ms: int) -> bool:
|
||||||
msg = Message.obtain(self)
|
msg = Message.obtain(self)
|
||||||
msg._callback = r
|
msg._callback = r
|
||||||
msg.when = int(_time.time() * 1000) + delay_ms
|
|
||||||
return self.sendMessageDelayed(msg, delay_ms)
|
return self.sendMessageDelayed(msg, delay_ms)
|
||||||
|
|
||||||
def postAtTime(self, r: Callable, uptime_ms: int) -> bool:
|
def postAtTime(self, r: Callable, uptime_ms: int) -> bool:
|
||||||
@@ -869,6 +993,11 @@ class Handler(Stub):
|
|||||||
|
|
||||||
def sendMessage(self, msg: Message) -> bool:
|
def sendMessage(self, msg: Message) -> bool:
|
||||||
msg._target = self
|
msg._target = self
|
||||||
|
looper = self._looper
|
||||||
|
if looper is not None and looper._running:
|
||||||
|
looper._enqueue(msg)
|
||||||
|
return True
|
||||||
|
# 当前线程无运行中的 Looper,回退到同步执行(保持兼容性)
|
||||||
self.dispatchMessage(msg)
|
self.dispatchMessage(msg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -878,31 +1007,53 @@ class Handler(Stub):
|
|||||||
|
|
||||||
def sendMessageDelayed(self, msg: Message, delay_ms: int) -> bool:
|
def sendMessageDelayed(self, msg: Message, delay_ms: int) -> bool:
|
||||||
msg._target = self
|
msg._target = self
|
||||||
msg.when = int(_time.time() * 1000) + delay_ms
|
msg.when = SystemClock.uptimeMillis() + delay_ms
|
||||||
|
looper = self._looper
|
||||||
|
if looper is not None and looper._running:
|
||||||
|
looper._enqueue(msg)
|
||||||
|
return True
|
||||||
|
# 回退到同步执行
|
||||||
self.dispatchMessage(msg)
|
self.dispatchMessage(msg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def sendMessageAtTime(self, msg: Message, uptime_ms: int) -> bool:
|
def sendMessageAtTime(self, msg: Message, uptime_ms: int) -> bool:
|
||||||
msg._target = self
|
msg._target = self
|
||||||
msg.when = uptime_ms
|
msg.when = uptime_ms
|
||||||
|
looper = self._looper
|
||||||
|
if looper is not None and looper._running:
|
||||||
|
looper._enqueue(msg)
|
||||||
|
return True
|
||||||
self.dispatchMessage(msg)
|
self.dispatchMessage(msg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def sendMessageAtFrontOfQueue(self, msg: Message) -> bool:
|
def sendMessageAtFrontOfQueue(self, msg: Message) -> bool:
|
||||||
msg._target = self
|
msg._target = self
|
||||||
|
msg.when = 0
|
||||||
|
looper = self._looper
|
||||||
|
if looper is not None and looper._running:
|
||||||
|
with looper._queue_lock:
|
||||||
|
looper._message_queue.insert(0, msg)
|
||||||
|
return True
|
||||||
self.dispatchMessage(msg)
|
self.dispatchMessage(msg)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def removeCallbacks(self, r: Callable):
|
def removeCallbacks(self, r: Callable):
|
||||||
pass
|
looper = self._looper
|
||||||
|
if looper is not None:
|
||||||
|
looper._remove_callbacks(r)
|
||||||
|
|
||||||
def removeMessages(self, what: int):
|
def removeMessages(self, what: int):
|
||||||
pass
|
looper = self._looper
|
||||||
|
if looper is not None:
|
||||||
|
looper._remove_messages(what)
|
||||||
|
|
||||||
def obtainMessage(self, what: int = 0) -> Message:
|
def obtainMessage(self, what: int = 0) -> Message:
|
||||||
return Message.obtain(self, what)
|
return Message.obtain(self, what)
|
||||||
|
|
||||||
def hasMessages(self, what: int) -> bool:
|
def hasMessages(self, what: int) -> bool:
|
||||||
|
looper = self._looper
|
||||||
|
if looper is not None:
|
||||||
|
return looper._has_messages(what)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def getLooper(self) -> Looper:
|
def getLooper(self) -> Looper:
|
||||||
@@ -950,6 +1101,42 @@ class HandlerThread(threading.Thread, Stub):
|
|||||||
return self.quit()
|
return self.quit()
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# AsyncTask Executor (守护线程池)
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
class _AsyncTaskExecutor:
|
||||||
|
"""守护线程池执行器 — 用于 AsyncTask 后台执行,不阻塞程序退出。
|
||||||
|
|
||||||
|
使用 BoundedSemaphore 限制并发数,每个任务在独立的守护线程中运行。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, max_workers: int = 4):
|
||||||
|
self._max_workers = max_workers
|
||||||
|
self._semaphore = threading.BoundedSemaphore(max_workers)
|
||||||
|
self._shutdown_flag = False
|
||||||
|
|
||||||
|
def submit(self, fn, *args, **kwargs):
|
||||||
|
if self._shutdown_flag:
|
||||||
|
raise RuntimeError("Executor has been shut down")
|
||||||
|
self._semaphore.acquire()
|
||||||
|
|
||||||
|
def _run():
|
||||||
|
try:
|
||||||
|
fn(*args, **kwargs)
|
||||||
|
finally:
|
||||||
|
self._semaphore.release()
|
||||||
|
|
||||||
|
t = threading.Thread(target=_run, daemon=True)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
def execute(self, command):
|
||||||
|
self.submit(command)
|
||||||
|
|
||||||
|
def shutdown(self, wait: bool = True):
|
||||||
|
self._shutdown_flag = True
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# AsyncTask
|
# AsyncTask
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
@@ -967,6 +1154,7 @@ class AsyncTask(Stub):
|
|||||||
self._status = AsyncTask.Status.PENDING
|
self._status = AsyncTask.Status.PENDING
|
||||||
self._result: Any = None
|
self._result: Any = None
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
|
self._handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
def doInBackground(self, *params) -> Any:
|
def doInBackground(self, *params) -> Any:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@@ -984,19 +1172,25 @@ class AsyncTask(Stub):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def execute(self, *params) -> "AsyncTask":
|
def execute(self, *params) -> "AsyncTask":
|
||||||
|
return self.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, *params)
|
||||||
|
|
||||||
|
def executeOnExecutor(self, exec: Any, *params) -> "AsyncTask":
|
||||||
self._status = AsyncTask.Status.RUNNING
|
self._status = AsyncTask.Status.RUNNING
|
||||||
self.onPreExecute()
|
self.onPreExecute()
|
||||||
|
exec.submit(self._run_background, params)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def _run_background(self, params):
|
||||||
|
"""在后台线程中执行 doInBackground,完成后通过 Handler 回调主线程"""
|
||||||
try:
|
try:
|
||||||
self._result = self.doInBackground(*params)
|
self._result = self.doInBackground(*params)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._result = e
|
self._result = e
|
||||||
self._status = AsyncTask.Status.FINISHED
|
self._status = AsyncTask.Status.FINISHED
|
||||||
if not self._cancelled:
|
if self._cancelled:
|
||||||
self.onPostExecute(self._result)
|
self._handler.post(lambda: self.onCancelled(self._result))
|
||||||
return self
|
else:
|
||||||
|
self._handler.post(lambda: self.onPostExecute(self._result))
|
||||||
def executeOnExecutor(self, exec: Any, *params) -> "AsyncTask":
|
|
||||||
return self.execute(*params)
|
|
||||||
|
|
||||||
def get(self) -> Any:
|
def get(self) -> Any:
|
||||||
return self._result
|
return self._result
|
||||||
@@ -1012,7 +1206,12 @@ class AsyncTask(Stub):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def publishProgress(self, *values):
|
def publishProgress(self, *values):
|
||||||
self.onProgressUpdate(*values)
|
self._handler.post(lambda: self.onProgressUpdate(*values))
|
||||||
|
|
||||||
|
|
||||||
|
# 初始化默认执行器
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR = _AsyncTaskExecutor(max_workers=4)
|
||||||
|
AsyncTask.SERIAL_EXECUTOR = _AsyncTaskExecutor(max_workers=1)
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -777,6 +777,155 @@ class View(Stub):
|
|||||||
def inflate(context, resource, root):
|
def inflate(context, resource, root):
|
||||||
return View(context)
|
return View(context)
|
||||||
|
|
||||||
|
# ─── 事件处理 (扩展) ───────────────────────────────────────
|
||||||
|
|
||||||
|
def onGenericMotionEvent(self, event) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onHoverEvent(self, event) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onDragEvent(self, event) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def onSizeChanged(self, w, h, oldw, oldh):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onFocusChanged(self, gainFocus, direction, previouslyFocusedRect):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onScrollChanged(self, l, t, oldl, oldt):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onVisibilityChanged(self, changedView, visibility):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ─── 监听器设置 (扩展) ─────────────────────────────────────
|
||||||
|
|
||||||
|
def setOnGenericMotionListener(self, listener):
|
||||||
|
self._on_generic_motion_listener = listener
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setOnHoverListener(self, listener):
|
||||||
|
self._on_hover_listener = listener
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setOnDragListener(self, listener):
|
||||||
|
self._on_drag_listener = listener
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setOnFocusChangeListener(self, listener):
|
||||||
|
self._on_focus_change_listener = listener
|
||||||
|
return self
|
||||||
|
|
||||||
|
def setOnContextClickListener(self, listener):
|
||||||
|
self._on_context_click_listener = listener
|
||||||
|
return self
|
||||||
|
|
||||||
|
def addOnLayoutChangeListener(self, listener):
|
||||||
|
if not hasattr(self, '_layout_change_listeners'):
|
||||||
|
self._layout_change_listeners = []
|
||||||
|
self._layout_change_listeners.append(listener)
|
||||||
|
|
||||||
|
def removeOnLayoutChangeListener(self, listener):
|
||||||
|
if hasattr(self, '_layout_change_listeners'):
|
||||||
|
try:
|
||||||
|
self._layout_change_listeners.remove(listener)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setOnApplyWindowInsetsListener(self, listener):
|
||||||
|
self._on_apply_window_insets_listener = listener
|
||||||
|
return self
|
||||||
|
|
||||||
|
# ─── 位置和尺寸 (扩展) ─────────────────────────────────────
|
||||||
|
|
||||||
|
def getLocationOnScreen(self, location):
|
||||||
|
location[0] = self._left if hasattr(self, '_left') else 0
|
||||||
|
location[1] = self._top if hasattr(self, '_top') else 0
|
||||||
|
|
||||||
|
def getLocationInWindow(self, location):
|
||||||
|
self.getLocationOnScreen(location)
|
||||||
|
|
||||||
|
def offsetLeftAndRight(self, offset):
|
||||||
|
if hasattr(self, '_left'):
|
||||||
|
self._left += offset
|
||||||
|
|
||||||
|
def offsetTopAndBottom(self, offset):
|
||||||
|
if hasattr(self, '_top'):
|
||||||
|
self._top += offset
|
||||||
|
|
||||||
|
def getHitRect(self, out_rect):
|
||||||
|
out_rect.left = self._left if hasattr(self, '_left') else 0
|
||||||
|
out_rect.top = self._top if hasattr(self, '_top') else 0
|
||||||
|
out_rect.right = out_rect.left + (self._width if hasattr(self, '_width') else 0)
|
||||||
|
out_rect.bottom = out_rect.top + (self._height if hasattr(self, '_height') else 0)
|
||||||
|
|
||||||
|
def getDrawingRect(self, out_rect):
|
||||||
|
out_rect.left = 0
|
||||||
|
out_rect.top = 0
|
||||||
|
out_rect.right = self._width if hasattr(self, '_width') else 0
|
||||||
|
out_rect.bottom = self._height if hasattr(self, '_height') else 0
|
||||||
|
|
||||||
|
# ─── 其他扩展方法 ──────────────────────────────────────────
|
||||||
|
|
||||||
|
def setTooltipText(self, text):
|
||||||
|
self._tooltip_text = text
|
||||||
|
|
||||||
|
def getTooltipText(self):
|
||||||
|
return getattr(self, '_tooltip_text', None)
|
||||||
|
|
||||||
|
def setTranslationZ(self, translationZ):
|
||||||
|
self._translation_z = translationZ
|
||||||
|
|
||||||
|
def getTranslationZ(self):
|
||||||
|
return getattr(self, '_translation_z', 0.0)
|
||||||
|
|
||||||
|
def setZ(self, z):
|
||||||
|
self._z = z
|
||||||
|
|
||||||
|
def getZ(self):
|
||||||
|
return getattr(self, '_z', 0.0)
|
||||||
|
|
||||||
|
def setTransitionName(self, name):
|
||||||
|
self._transition_name = name
|
||||||
|
|
||||||
|
def getTransitionName(self):
|
||||||
|
return getattr(self, '_transition_name', None)
|
||||||
|
|
||||||
|
def setClipToOutline(self, clipToOutline):
|
||||||
|
self._clip_to_outline = clipToOutline
|
||||||
|
|
||||||
|
def getClipToOutline(self):
|
||||||
|
return getattr(self, '_clip_to_outline', False)
|
||||||
|
|
||||||
|
def setFitsSystemWindows(self, fitSystemWindows):
|
||||||
|
self._fits_system_windows = fitSystemWindows
|
||||||
|
|
||||||
|
def getFitsSystemWindows(self):
|
||||||
|
return getattr(self, '_fits_system_windows', False)
|
||||||
|
|
||||||
|
def playSoundEffect(self, soundConstant):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setSaveEnabled(self, enabled):
|
||||||
|
self._save_enabled = enabled
|
||||||
|
|
||||||
|
def isSaveEnabled(self):
|
||||||
|
return getattr(self, '_save_enabled', True)
|
||||||
|
|
||||||
|
def setSystemUiVisibility(self, visibility):
|
||||||
|
self._system_ui_visibility = visibility
|
||||||
|
|
||||||
|
def getSystemUiVisibility(self):
|
||||||
|
return getattr(self, '_system_ui_visibility', 0)
|
||||||
|
|
||||||
|
def requestApplyInsets(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def onApplyWindowInsets(self, insets):
|
||||||
|
return insets
|
||||||
|
|
||||||
|
|
||||||
class MeasureSpec:
|
class MeasureSpec:
|
||||||
"""MeasureSpec — 测量规格包装"""
|
"""MeasureSpec — 测量规格包装"""
|
||||||
@@ -2646,6 +2795,24 @@ class SurfaceTexture(Stub):
|
|||||||
mtx[i] = 1.0 if i % 5 == 0 else 0.0
|
mtx[i] = 1.0 if i % 5 == 0 else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# MenuInflater
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
class MenuInflater(Stub):
|
||||||
|
"""MenuInflater — 菜单填充器(桌面环境桩实现)"""
|
||||||
|
|
||||||
|
def __init__(self, context=None):
|
||||||
|
super().__init__()
|
||||||
|
self._context = context
|
||||||
|
|
||||||
|
def inflate(self, menu_res: int, menu: Any):
|
||||||
|
return menu
|
||||||
|
|
||||||
|
def toString(self) -> str:
|
||||||
|
return f"MenuInflater@{hex(id(self))}"
|
||||||
|
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
# Window
|
# Window
|
||||||
# ═══════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|||||||
@@ -416,9 +416,13 @@ class File(Stub):
|
|||||||
except OSError:
|
except OSError:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def compareTo(self, other):
|
def compareTo(self, other) -> int:
|
||||||
if isinstance(other, File):
|
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
|
return -1
|
||||||
|
|
||||||
def equals(self, other):
|
def equals(self, other):
|
||||||
|
|||||||
+346
-12
@@ -18,6 +18,16 @@ from datetime import datetime as _datetime
|
|||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Object", "Throwable", "Exception", "RuntimeException", "Error",
|
"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",
|
"String", "Integer", "Long", "Short", "Byte", "Float", "Double",
|
||||||
"Boolean", "Character", "Math", "StrictMath", "System",
|
"Boolean", "Character", "Math", "StrictMath", "System",
|
||||||
"Thread", "ThreadLocal", "StringBuilder", "StringBuffer",
|
"Thread", "ThreadLocal", "StringBuilder", "StringBuffer",
|
||||||
@@ -148,6 +158,75 @@ class Error(Throwable):
|
|||||||
pass
|
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
|
# StackTraceElement
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -277,11 +356,10 @@ class String(CharSequence):
|
|||||||
def getBytes(self, charset="UTF-8"):
|
def getBytes(self, charset="UTF-8"):
|
||||||
return self._str.encode(charset, errors="replace")
|
return self._str.encode(charset, errors="replace")
|
||||||
|
|
||||||
def hashCode(self):
|
def hashCode(self) -> int:
|
||||||
h = 0
|
h = 0
|
||||||
for c in self._str:
|
for i in range(len(self._str)):
|
||||||
h = (31 * h + ord(c)) & 0xFFFFFFFF
|
h = 31 * h + ord(self._str[i])
|
||||||
return h if h < 0x80000000 else h - 0x100000000
|
|
||||||
return h & 0x7FFFFFFF
|
return h & 0x7FFFFFFF
|
||||||
|
|
||||||
def indexOf(self, ch, fromIndex=0):
|
def indexOf(self, ch, fromIndex=0):
|
||||||
@@ -362,6 +440,89 @@ class String(CharSequence):
|
|||||||
def trim(self):
|
def trim(self):
|
||||||
return String(self._str.strip())
|
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
|
@staticmethod
|
||||||
def valueOf(obj):
|
def valueOf(obj):
|
||||||
return String(str(obj))
|
return String(str(obj))
|
||||||
@@ -1200,7 +1361,8 @@ class Math:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def floor(a): return int(math.floor(a))
|
def floor(a): return int(math.floor(a))
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def round(a): return round(a)
|
def round(a: float) -> int:
|
||||||
|
return int(math.floor(a + 0.5))
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def min(a, b): return a if a < b else b
|
def min(a, b): return a if a < b else b
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -1266,8 +1428,58 @@ class StrictMath(Math):
|
|||||||
pass
|
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:
|
class System:
|
||||||
"""java.lang.System"""
|
"""java.lang.System"""
|
||||||
|
_console = None
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def currentTimeMillis(): return int(time.time() * 1000)
|
def currentTimeMillis(): return int(time.time() * 1000)
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -1294,6 +1506,28 @@ class System:
|
|||||||
os.environ[key] = value
|
os.environ[key] = value
|
||||||
return value
|
return value
|
||||||
@staticmethod
|
@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
|
def lineSeparator(): return _os.linesep
|
||||||
in_ = _sys.stdin
|
in_ = _sys.stdin
|
||||||
out = _sys.stdout
|
out = _sys.stdout
|
||||||
@@ -1306,6 +1540,18 @@ class Thread(Runnable):
|
|||||||
NORM_PRIORITY = 5
|
NORM_PRIORITY = 5
|
||||||
MAX_PRIORITY = 10
|
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):
|
def __init__(self, target=None, name=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._target = target
|
self._target = target
|
||||||
@@ -1313,25 +1559,91 @@ class Thread(Runnable):
|
|||||||
self._alive = False
|
self._alive = False
|
||||||
self._daemon = False
|
self._daemon = False
|
||||||
self._interrupted = 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):
|
def start(self):
|
||||||
self._alive = True
|
self._alive = True
|
||||||
if self._target:
|
self._state = Thread.RUNNABLE
|
||||||
_threading.Thread(target=self.run, daemon=self._daemon).start()
|
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):
|
def run(self):
|
||||||
if self._target:
|
if self._target:
|
||||||
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):
|
def interrupt(self):
|
||||||
self._interrupted = True
|
self._interrupted = True
|
||||||
|
|
||||||
def isInterrupted(self):
|
def isInterrupted(self):
|
||||||
return self._interrupted
|
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):
|
def isAlive(self):
|
||||||
|
if self._py_thread is not None:
|
||||||
|
return self._py_thread.is_alive()
|
||||||
return self._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 getName(self): return self._name
|
||||||
def setName(self, n): self._name = n
|
def setName(self, n): self._name = n
|
||||||
def setDaemon(self, d): self._daemon = d
|
def setDaemon(self, d): self._daemon = d
|
||||||
@@ -1339,15 +1651,37 @@ class Thread(Runnable):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def currentThread():
|
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
|
@staticmethod
|
||||||
def sleep(millis):
|
def sleep(millis):
|
||||||
import time
|
_time.sleep(millis / 1000.0)
|
||||||
time.sleep(millis / 1000.0)
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def yield_():
|
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):
|
class ThreadLocal(Stub):
|
||||||
|
|||||||
@@ -666,13 +666,20 @@ class HashMap(Stub):
|
|||||||
return None
|
return None
|
||||||
return self._data[key]
|
return self._data[key]
|
||||||
|
|
||||||
def remove(self, key, value=None) -> bool:
|
def remove(self, key, value=None):
|
||||||
if value is None:
|
if value is not None:
|
||||||
return self._data.pop(key, None) is not None or key in self._data
|
# remove(key, value) — 返回 boolean
|
||||||
if key in self._data and self._data[key] == value:
|
if key in self._data and self._data[key] == value:
|
||||||
del self._data[key]
|
del self._data[key]
|
||||||
return True
|
return True
|
||||||
return False
|
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):
|
def replace(self, key, old_value=None, new_value=None):
|
||||||
if old_value is None and new_value is None:
|
if old_value is None and new_value is None:
|
||||||
|
|||||||
Reference in new issue
Block a user