版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Android后台杀死系列之二:ActivityManagerService 与 App 现场恢复机制 本篇是Android后台杀死系列的第二篇,主要讲解ActivityMangerService是如何恢复被后台 杀死的进程的(基于4.3 ),在开篇FragmentActivity及PhoneWindow后台杀死处理机制 中, 简述了后台杀死所引起的一些常见问题,还有Android系统控件对后台杀死所做的一些兼容,以及onSaveInstance跟onRestoreInstance的作用于执行时机,最后说了如何应对后台杀死, 但是对于被后台杀死的进程如何恢复的并没有讲解,本篇不涉及后台杀死,比
2、如 LowmemoryKiller机制,只讲述被杀死的进程如何恢复的。作者:佚名来源:segmentfault|2017-01-12 15:06收藏 分享用户空间APP0- w* T- TSr个AMSm Activities -ActivitySiackActivttyRecord AActivityAActivity BActivity Record BActivity CActivity Record CActivity DAc tiv rty Record DLowMemoryKiller本篇是Android后台杀死系列的第二篇,主要讲解 ActivityMangerService是如何
3、 恢复被后台杀死的进程的(基于4.3 ),在开篇FragmentActivity及PhoneWindow 后台杀死处理机制 中,简述了后台杀死所引起的一些常见问题,还有 Android 系统控件对后台杀死所做的一些兼容,以及onSaveInstance跟onRestoreInstance的作用于执行时机,最后说了如何应对后台杀死,但是对于被后台杀死的进程如 何恢复的并没有讲解,本篇不涉及后台杀死,比如LowmemoryKiller机制,只讲 述被杀死的进程如何恢复的。假设,一个应用被后台杀死,再次从最近的任务列 表唤起App时候,系统是如何处理的呢?有这么几个问题可能需要解决: Android
4、框架层(AMS)如何知道App被杀死了 App被杀前的场景是如何保存的 系统(AMS)如何恢复被杀的App 被后台杀死的App的启动流程跟普通的启动有什么区别 Activity的恢复顺序为什么是倒序恢复系统(AMS)如何知道App被杀死了首先来看第一个问题,系统如何知道Application被杀死了,Android使用了 Linux 的oomKiller机制,只是简单的做了个变种,采用分等级的 LowmemoryKiller , 但这个其实是内核层面的,LowmemoryKiller杀死进程后,不会像用户空间发送 通知,也就是说框架层的ActivityMangerService无法知道App是
5、否被杀死,但是, 只有知道App或者Activity是否被杀死,AMS(ActivityMangerService)才能正确 的走唤起流程,那么AMS究竟是在什么时候知道App或者Activity被后台杀死 了呢?我们先看一下从最近的任务列表进行唤起的时候,究竟发生了什么。从最近的任务列表或者Icon再次唤起App的流程在系统源码systemUi的包里,有个RecentActivity,这个其实就是最近的任务列 表的入口,而其呈现界面是通过RecentsPanelView来展现的,点击最近的 App其执行代码如下:1. public void handleOnClick( View view
6、) 2. ViewHolder holder = (ViewHolder) view .getTag();3. TaskDescription ad = holder.taskDescription;4. final Context context = view .getContext();5. final ActivityManager am = (ActivityManager)6. context.getSystemService(Context.ACTIVITY_SERVICE);7. Bitmap bm = holder.thumbnailViewImageBitmap;8. .9.
7、 /关键点1如果TaskDescription 没有被主动关闭,正常关闭,ad.taskId就是>=010. if (ad.taskId >= 0) 11./ This is an active task; it should just go to the foreground.12. am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HO ME, 13.opts);14. else15. Intent intent = ent;16. intent.addFlags(Intent.FLAG_ACT
8、IVITY_LAUNCHED_FROM_HISTORY17. |Intent.FLAG_ACTIVITY_TASK_ON_HOME18. |Intent.FLAG_ACTIVITY_NEW_TASK);19. try 20. context.startActivityAsUser(intent, opts,21. new UserHandle(UserHandle.USER_CURRENT);22. .23. 在上面的代码里面,有个判断ad.taskId >= 0,如果满足这个条件,就通过moveTaskToFront唤起 APP,那么ad.taskId是如何获取的?recent包里面有
9、各类 RecentTasksLoader这个类就是用来加载最近任务列表的一个Loader,看一下它的源码,主要看一下加载:1. Override2. protected Void doInBackground(Void. params) 3. / We load in two stages: first , we update progress with just the first screenful4. / of items. Then , we update with the rest of the items5. final int origPri = Process.getThrea
10、dPriority(Process.myTid();6. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 7.final PackageManager pm = mContext.getPackageManager();8. final ActivityManager am = (ActivityManager)9. mContext.getSystemService(Context.ACTIVITY_SERVICE);10.11.final List<ActivityManager.RecentTaskInf
11、o> recentTasks =12. am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE); 13. 14.15. TaskDescription item = createTaskDescription(recentInfo.id,16. recentInfo.persistentId, recentInfo.baseIntent,17. recentInfo.origActivity, recentInfo.description);18. .19. 可以看到,其实就是通过 ActivityM
12、anger的getRecentTasks向AMS请求最近的 任务信息,然后通过 createTaskDescription创建 TaskDescription,这里传递的 recentInfo.id 其实就是 TaskDescription 的 taskId,来看一下它的意义:1. public List<ActivityManager.RecentTaskInfo> getRecentTasks( int maxNum,2. int flags, int userId)3. .4. IPackageManager pm=AppGlobals.getPackageManager()
13、;5.6. final int N = mRecentTasks. size ();7.8. for (int i=0; i<N && maxNum > 0; i+) 9. TaskRecord tr = mRecentTasks.get(i);10. if (i = 011. |(flags&ActivityManager.RECENT_WITH_EXCLUDED)!= 0)12. |(ent = null)13. |(ent.getFlags()14. &Intent.FLAG_ACTIVITY_EXCLUDE_FROM_
14、RECENTS) = 0)15. ActivityManager.RecentTaskInfo rti16. = new ActivityManager.RecentTaskInfo。;17. rti.id = tr.numActivities > 0 ? tr.taskId : -1;18. rti.persistentId = tr.taskId;19. rti.baseIntent = new Intent(20. ent != null ? ent : tr.affinityIntent);21. if (!detailed) 22. rti.baseIn
15、tent.replaceExtras(Bundle) null);23. 可以看出 RecentTaskInfo的id是由TaskRecord决定的,如果 TaskRecord中 numActivities > 0 就去 TaskRecord的 Id,否则就取-1,这里的 numActivities 其实 就是TaskRecode中记录的ActivityRecord的数目,更具体的细节可以自行查看 ActivityManagerService及ActivityStack ,那么这里就容易解释了,只要是存活的 APP、或者被 LowmemoryKiller 杀死的 APP,其 AMS 的
16、ActivityRecord 是完整 保存的,这就是恢复的依据。RecentActivity获取的数据其实就是AMS中的翻版, RecentActivity并不知道将要唤起的 APP是否是存活的,只要 TaskRecord告诉 RecentActivity是存货的,那么久直接走唤起流程,也就是通过ActivityManager的moveTaskToFront唤起App,至于后续的工作,就完全交给 AMS来处理。现看一下到这里的流程图:I在唤起App的时候AMS侦测App或者Activity是否被异常杀死 接着往下看 moveTaskToFrontLocked,这个函数在 ActivitySta
17、ck 中,ActivityStack 主要用来管理 ActivityRecord栈的,所有start的Activity都在ActivityStack中保 留一个 ActivityRecord ,这个也是 AMS 管理 Activity 的一个依据,ActivityStack 最终 moveTaskToFrontLocked 会调用 resumeTopActivityLocked 来唤起 Activity ,AMS获取即将resume的Activity信息的方式主要是通过 ActivityRecord ,它并不 知道Activity本身是否存活,获取之后,AMS知道唤醒Activity的环节才知
18、道 App 或者 Activity 被杀死,具体看一下 resumeTopActivityLocked 源码:1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) 2.3. / This activity is now becoming visible.4. mService.mWindowManager.setAppVisibility( next .appToken, true );5.6. 恢复逻辑7. if ( next .app != null && next .ap
19、p.thread != null) 8. /正常恢复9. try 10. / Deliver all pending results.11. ArrayList a = next .results;12. if (a != null) 13. final int N = a. size ();14. if (! next .finishing && N > 0) 15. next .app.thread.scheduleSendResult( next .appToken, a);17. )18. .19. next .app.thread.scheduleResumeA
20、ctivity( next .appToken,20. mService.isNextTransitionForward();21. .22. catch (Exception e) 23. / Whoops, need to restart this activity!24. /这里需要重启,难道被后台杀死,走的是异常分支吗? ? ? ? 异常杀死25. if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to " 26.+ lastState +": " + next );27.
21、 next .state = lastState;28. mResumedActivity = lastResumedActivity;29. <!-确实这里是因为进程挂掉了 ->30. Slog.i(TAG, "Restarting because process died: "+ next );31. 。32. startSpecificActivityLocked( next , true , false );33. return true ;34. 35. .36. 由于没有主动调用 finish的,所以 AMS并不会清理掉ActivityRecord与
22、TaskRecord ,因此resume的时候走的就是上面的分支,可以这里会调用 next.app.thread.scheduleSendResu®者 next.app.thread.scheduleResumeActivityS 行唤起上一个Activity,但是如果APP或者Activity被异常杀死,那么唤起的操 作一定是失败,会抛出异常,首先假设APP整个被杀死,那么 APP端同AMS通信的Binder线程也不复存在,这个时候通过 Binder进行通信就会抛出 RemoteException,如止匕,就会走下面的 catch部分,通过 startSpecificActivit
23、yLocked 再次将APP重建,并且将最后的Activity重建,其实你可以本地利用 AIDL写一 个C/S通信,在将一端关闭,然后用另一端访问,就会抛出RemoteException异常,如下图:androijdt£xceptijonat android. Qs.airderP roxy, trdnact (Nat Ive MettwdJat con.snail.latwffinIHyAidL工ntE1 口口速蛤上ub±Praxy.objErtT£iMiriUtfirnt岂rft工雪.t snail>cli«nt.HinActivcrh(;ja
24、t saiUcll-eHt.MainActlvitVdeiJeiHde/Sl.deCll£kf':at txjtterknile. internal.OetiouncingOnCl IckListeneROoCliciif i. i j 1:.' :at androids view*View*performedic()m七 a力dM±d. u±睦亡1c 11力(1at mndnoXd,口与*厢fi仇已.hiTHReQlUmKf.t anclnoid,qs.Hatidler.dispatcMIessgei>at android.QSwL(
25、87;per¥lcxip'')at android, app. ActLvityThread .tubi (Act ivityTTinewi*java: 51S3)at java.lartg.ref lect+Metiwd. invokeWative(Matlve Hetlwri) u :nternal hi 1。at cof"*ndroid. internals.ZgotelfllttMethouAfwiArgCaller-run (Z/gotelrit» jav: 7371at 却箫 sMinZyottlfiit. j ava:S53)at t
26、fa lv Ik - sy. N9t 1 vest. ma in (Ndt 1 vc ftothcxi)还有一种可能,APP没有被kill ,但是ActMty被Kill掉了,这个时候会怎么样? 首先,Activity的管理是一定通过 AMS的,Activity的kill 一定是是AMS操刀 的,是有记录的,严格来说,这种情况并不属于后台杀死,因为这属于 AMS正 常的管理,在可控范围,比如打开了开发者模式中的不保留活动”这个时候,虽然会杀死Activity,但是仍然保留了 ActivitRecord ,所以再唤醒,或者回退的 的时候仍然有迹可循,看一下ActivityStack的Destroy
27、回调代码,1. final boolean destroyActivityLocked(ActivityRecord r,2. boolean removeFromApp, boolean oomAdj, String reason) 3. .4. if (hadApp) 5. .6. boolean skipDestroy =false ;7. try 8. 关键代码19. r.app.thread.scheduleDestroyActivity(r.appToken,.finishing,10. r.configChangeFlags);11. .12. if (r.finishing &
28、amp;& !skipDestroy) 13.if (DEBUG_STATES) Slog.v(TAG, "Moving to DESTROYING: "+ r14. + " (destroy requested)");15. r.state = ActivityState.DESTROYING;16. Message msg = mHandler.obtainMessage(DESTROY_TIMEOUT_MSG);17. msg.obj = r;18. mHandler.sendMessageDelayed(msg, DESTROY_TIME
29、OUT);19. else 20. 关键代码221. r.state = ActivityState.DESTROYED;22.if (DEBUG_APP) Slog.v(TAG, "Clearing app during destroy for activity "+ r);23. r.app = null;24. 25. 26. return removedFromHistory;27. 这里有两个关键啊你单,1是告诉客户端的AcvitityThread清除Activity , 2是标 记如果 AMS自己非正常关闭的 Activity ,就将ActivityRecord
30、的state设置为 ActivityState.DESTROYED ,并且清空它的 ProcessRecorcBl用:r.app = null。这 里是唤醒时候的一个重要标志,通过这里 AMS就能知道Activity被自己异常关 闭了,设置ActivityState.DESTROYED是为了让避免后面的清空逻辑。1. final void activityDestroyed(IBinder token) 2. synchronized (mService) 3. final long origId = Binder.clearCallingIdentity();4. try 5. Activi
31、tyRecord r = ActivityRecord.forToken(token);6. if (r != null) 7. mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r);8. 9. intindex= indexOfActivityLocked(r);10. if (index>= 0) 11. 1 <!-这里会是否从history列表移除 ActivityRecord-12. if (r.state = ActivityState.DESTROYING) 13. cleanUpActivityLocked(r, true ,
32、 false );14. removeActivityFromHistoryLocked(r);15. 16. 17. resumeTopActivityLocked( null);18. finally 19. Binder.restoreCallingldentity(origld);20. 21. 22. 看代码关键点1,只有r.state = ActivityState.DESTROYING 的时候,才会移除ActivityRecord ,但是对于不非正常finish的Activity ,其状态是不会被设置成ActivityState.DESTROYING ,是直接跳过了 Activi
33、tyState.DESTROYING ,被设置 成了 ActivityState.DESTROYED ,所以不会 removeActivityFromHistoryLocked , 也就是保留了 ActivityRecord现场,好像也是依靠异常来区分是否是正常的结束 掉Activity。这种情况下是如何启动 Activity的呢?通过上面两点分析,就知道 了两个关键点. ActivityRecord 没有动 HistoryRecord 列表中移除.ActivityRecord 的 ProcessRecor疗段被置空,r.app = null这样就保证了在 resumeTopActivityL
34、ocked 的时候,走 startSpecificActivityLocked 分支1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) 2. .3.4. if ( next .app != null && next .app.thread != null) 5. .6.7. else 8. / Whoops, need to restart this activity!9. .10. startSpecificActivityLocked( next , true , tru
35、e );11. 12.13. return true ;14. 到这里,AMS就知道了这个APP或者Activity是不是被异常杀死过,从而,决 定是走resume流程还是restore流程。App被杀前的场景是如何保存的:新Activity启动跟旧Activity的保存App现场的保存流程相对是比较简单的,入口基本就是 startActivity的时候,只 要是界面的跳转基本都牵扯到Activity的切换跟当前Activity场景的保存:先画个简单的图形,开偏里面讲FragmentActivity的时候,简单说了一些 onSavelnstance的执行时机,这里详细看一下AMS是如何管理这些
36、跳转以及场景保存的,模拟场景:Activity A启动Activity B的时候,这个时候A不可见, 可能会被销毁,需要保存 A的现场,这个流程是什么样的:简述如下 ActivityA startActivity ActivityB ActivityA pause ActivityB create ActivityB start ActivityB resume ActivityA onSavelnstance ActivityA stop流程大概是如下样子: .u i.ffwr |,."二 tovriB | | " m, i q i |'现在我们通过源码一步一步跟
37、一下,看看 AMS在新Activity启动跟旧Activity 的保存的时候,到底做了什么:跳过简单的startActivity,直接去AMS中去看ActivityManagerService1. public final int startActivityAsUser(IApplicationThread caller, String callingPacka ge,2. Intent intent, String resolvedType, IBinder resultTo,3. String resultWho, int requestCode, int startFlags,4. St
38、ring profileFile, ParcelFileDescriptor profileFd, Bundle options,int userId)5. enforceNotIsolatedCaller( "startActivity");6. .7. return mMainStack.startActivityMayWait(caller,-1, callingPackage, intent, resolvedType,8. resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,9.
39、 null, null, options, userId);10. ActivityStackint callingUid,1. final int startActivityMayWait(IApplicationThread caller,3.int res = startActivityLocked(caller, intent, resolvedType,4.aInfo, resultTo, resultWho, requestCode, callingPid, callingUid,5. callingPackage, startFlags, options, componentSp
40、ecified, null);6.7. 。8. )这里通过startActivityMayWait启动新的APP,或者新Activity,这里只看简单的,至于从桌面启动App的流程,可以去参考更详细的文章,比如老罗的startActivity 流程,大概就是新建 ActivityRecord , ProcessRecorctL类,并加入 AMS中相应 的堆栈等,resumeTopActivityLocked是界面切换的统一入口,第一次进来的时候, 由于ActivityA还在没有pause,因此需要先暂停 ActivityA ,这些完成后, ActivityStack1. final boole
41、an resumeTopActivityLocked(ActivityRecord prev, Bundle options) 2. .3. <!-必须将当前 Resume的Activity设置为pause然后stop才能继续->4. / We need to start pausing the current activity so the top one5. / can be resumed.6. if (mResumedActivity != null) 7. if ( next .app != null && next .app.thread != null
42、) 8.9. mService.updateLruProcessLocked( next .app, false );10. 11. startPausingLocked(userLeaving,false );12. return true ;13. 14. 其实这里就是暂停 ActivityA , AMS通过Binder告诉ActivityThread需要暂停的 ActivityA , ActivityThread 完成后再通过 Binder 通知 AMS , AMS 会开始 resume ActivityB ,1. private final void startPausingLocke
43、d(boolean userLeaving, boolean uiSleeping) 2.3. if (prev.app != null && prev.app.thread != null) 4. .5. try 6.prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,7.userLeaving, prev.configChangeFlags);ActivityThread1. private void handlePauseActivity(IBinder token, boolean fi
44、nished,2. boolean userLeaving, int configChanges) 3. ActivityClientRecord r = mActivities.get(token);4. if (r != null) 5. .6. performPauseActivity(token, finished, r.isPreHoneycomb();7.8. / Telltheactivitymanager wehave paused.9. try 10. ActivityManagerNative.getDefault().activityPaused(token);11. c
45、atch (RemoteException ex)12. 13. 14. AMS收到ActivityA发送过来的pause消息之后,就会唤起ActivityB ,入口还是 resumeTopActivityLocked,唤醒B,之后还会 A给进一步stop掉,这个时候就牵 扯到现场的保存,ActivityStack1. private final void completePauseLocked() 2.3. if (!mService.isSleeping() 4. resumeTopActivityLocked(prev);5. else 6.7. .8.ActivityB如何启动的,本文
46、不关心,只看 ActivityA 如何保存现场的,ActivityB 起来后,会通过 ActivityStack 的 stopActivityLocked 去 stop ActivityA ,1. private final void stopActivityLocked(ActivityRecord r) 2. .3. if (mMainStack) 5.r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);6.7.回看 APP 端,看一下 ActivityThread 中的调用:首先通过
47、callActivityOnSaveInstanceState,将现场保存至U Bundle 中去,1. private void performStopActivityInner(ActivityClientRecord r,2. StopInfo info, boolean keepShown, boolean saveState) 3. .4. / Next have the activity save its current state and managed dialogs.5. if (!r.activity.mFinished && saveState) 6. i
48、f (r.state = null) 7. state = new Bundle();8. state.setAllowFds(false);9. mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);10. r.state = state;12.之后,通过 ActivityManagerNative.getDefault().activityStopped ,通知 AMS Stop 动作完成,在通知的时候,还会将保存的现场数据带过去。1. private static class StopInfo implem
49、ents Runnable 2. ActivityClientRecordactivity;3. Bundle state;4. Bitmap thumbnail;5. CharSequence description;6.7. Override publicvoid run() 8. / Tell activity manager we have been stopped.9. try 10.11. ActivityManagerNative.getDefault().activityStopped(12. activity.token, state, thumbnail, descript
50、ion);13. catch (RemoteException ex) 14. 15. 通过上面流程,AMS不仅启动了新的Activity ,同时也将上一个Activity的现场 进行了保存,及时由于种种原因上一个 Actiivity被杀死,在回退,或者重新唤醒 的过程中AMS也能知道如何唤起Activiyt ,并恢复。现在解决两个问题,1、如何保存现场,2、AMS怎么判断知道APP或者Activity 是否被异常杀死,那么就剩下最后一个问题了,AMS如何恢复被异常杀死的APP 或者Activity呢。整个Application被后台杀死情况下的恢复逻辑其实在讲解AMS怎么判断知道APP或者A
51、ctivity是否被异常杀死的时候,就已 经涉及了恢复的逻辑,也知道了一旦AMS知道了 APP被后台杀死了,那就不是 正常的resuem流程了,而是要重新laucher,先来看一下整个APP被干掉的会怎 么处理,看resumeTopActivityLocked部分,从上面的分析已知,这种场景下,会 因为Binder通信抛异常走异常分支,如下:1. final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) 2. 3. if ( next .app != null && next .app.
52、thread != null) 4. if (DEBUG_SWITCH) Slog.v(TAG, "Resume running: "+next);5. .6. try 7. .8. catch (Exceptione)9. / Whoops, need to restart this activity!10. 这里是知道整个 app被杀死的11. Slog.i(TAG, "Restarting because process died: "+ next );12. next .state = lastState;13. mResumedActivity
53、 = lastResumedActivity;14. Slog.i(TAG, "Restarting because process died: "+ next );15.16. startSpecificActivityLocked( next , true , false );17. return true ;18. 从上面的代码可以知道,其实就是走 startSpecificActivityLocked ,这根第一次从 桌面唤起APP没多大区别,只是有一点需要注意,那就是这种时候启动的Activity 是有上一次的现场数据传递过得去的,因为上次在退到后台的时候,所有A
54、ctivity 界面的现场都是被保存了,并且传递到 AMS中去的,那么这次的恢复启动就会将这些数据返回给 ActivityThread ,再来仔细看一下performLaunchActivity里面关于恢复的特殊处理代码:1. private Activity performLaunchActivity(ActivityClientRecord r, Intent customintent )(2. Activityinfo ainfo = r.activityinfo;3. Activity activity = null;4. try (5. java.lang.ClassLoader cl = r.packageinfo.getClassLoader();6. activity = minstrumentation.newActivity(7. cl, component.getClassName(), ent);8. StrictMode.incrementExpectedActivityCount(activity.getClass();9. ent.setExtrasClassLoader(cl);10. if (r.state != null) 11. r.state.setClassLoader(cl);12. 13. ca
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 上海市师大附中2026届高二上生物期末监测试题含解析
- 2026届甘肃省泾川县第三中学高二上生物期末监测模拟试题含解析
- 上海市嘉定区2026届初三一模物理试题(含答案)
- 2026届北京市房山区市级名校高二上数学期末综合测试模拟试题含解析
- 绿化团队施工方案(3篇)
- 护坡施工方案审查(3篇)
- 疏解盖板施工方案(3篇)
- 小学农场活动策划方案(3篇)
- 花园美食活动策划方案(3篇)
- 企业企业合同管理与风险控制手册(标准版)
- 普通高中化学课程标准(2025年修订版)与2020年版对比
- 低空智能-从感知推理迈向群体具身
- 福建国有资产管理公司招聘面试题及答案
- 四川省2025年高职单招职业技能综合测试(中职类)电子信息类试卷
- 2025年熔化焊接与热切割作业考试题库及答案
- 账务清理合同(标准版)
- 质量互变课件
- 幼儿园重大事项社会稳定风险评估制度(含实操模板)
- 2026年包头轻工职业技术学院单招职业适应性测试题库附答案
- 2025至2030中国应急行业市场深度分析及发展趋势与行业项目调研及市场前景预测评估报告
- 2025年中厚钢板行业分析报告及未来发展趋势预测
评论
0/150
提交评论