工作日报2022。4。19PackageManagerSer
2022。4。19问题澄清;
witen5。1升级到bturnc5。3无imei绑定用户选项澄清;应用平台设备鉴权822优化,合入工具类修改、及基本功能验证,非记录仪数据存入缓存,coding20;代码仓readme文件创建;
6扫描APK目录
PackageManagerService的构造函数中调用了scanDirTracedLI方法来扫描某个目录的apk文件。
Android10。0中,PKMS主要扫描以下路径的APK信息:
vendoroverlay
productoverlay
productservicesoverlay
odmoverlay
oemoverlay
systemframework
systemprivapp
systemapp
vendorprivapp
vendorapp
odmprivapp
odmapp
oemapp
oemprivapp
productprivapp
productapp
productservicesprivapp
productservicesapp
productservicesprivapp
我们就scanDirTracedLI()为入口来进行分析:
6。1〔ParallelPackageParser。java〕scanDirTracedLI()
从下面的函数可见,scanDirTracedLI的入口很简单,首先加入了一些systtrace的日志追踪,然后调用scanDirLI()进行分析
privatevoidscanDirTracedLI(FilescanDir,finalintparseFlags,intscanFlags,longcurrentTime){
Trace。traceBegin(TRACETAGPACKAGEMANAGER,scanDir〔scanDir。getAbsolutePath()〕);
try{
scanDirLI(scanDir,parseFlags,scanFlags,currentTime);
}finally{
Trace。traceEnd(TRACETAGPACKAGEMANAGER);
}
}
6。2〔ParallelPackageParser。java〕scanDirLI()
scanDirLI()中使用了ParallelPackageParser的对象,ParallelPackageParser是一个队列,我们这里手机所有系统的apk,然后从这些队列里面取出apk,再调用PackageParser解析进行解析
privatevoidscanDirLI(FilescanDir,intparseFlags,intscanFlags,longcurrentTime){
finalFile〔〕filesscanDir。listFiles();
if(ArrayUtils。isEmpty(files)){
Log。d(TAG,NofilesinappdirscanDir);
return;
}
if(DEBUGPACKAGESCANNING){
Log。d(TAG,ScanningappdirscanDirscanFlagsscanFlags
flags0xInteger。toHexString(parseFlags));
}
parallelPackageParser是一个队列,收集系统apk文件,
然后从这个队列里面一个个取出apk,调用PackageParser解析
try(ParallelPackageParserparallelPackageParsernewParallelPackageParser(
mSeparateProcesses,mOnlyCore,mMetrics,mCacheDir,
mParallelPackageParserCallback)){
Submitfilesforparsinginparallel
intfileCount0;
for(Filefile:files){
是Apk文件,或者是目录
finalbooleanisPackage(isApkFile(file)file。isDirectory())
!PackageInstallerService。isStageName(file。getName());
过滤掉非apk文件,如果不是则跳过继续扫描
if(!isPackage){
Ignoreentrieswhicharenotpackages
continue;
}
把APK信息存入parallelPackageParser中的对象mQueue,PackageParser()函数赋给了队列中的pkg成员
参考〔6。3〕
parallelPackageParser。submit(file,parseFlags);
fileCount;
}
Processresultsonebyone
for(;fileCount0;fileCount){
从parallelPackageParser中取出队列apk的信息
ParallelPackageParser。ParseResultparseResultparallelPackageParser。take();
ThrowablethrowableparseResult。throwable;
interrorCodePackageManager。INSTALLSUCCEEDED;
if(throwablenull){
TODO(toddke):movelowerinthescanchain
Staticsharedlibrarieshavesyntheticpackagenames
if(parseResult。pkg。applicationInfo。isStaticSharedLibrary()){
renameStaticSharedLibraryPackage(parseResult。pkg);
}
try{
调用scanPackageChildLI方法扫描一个特定的apk文件
该类的实例代表一个APK文件,所以它就是和apk文件对应的数据结构。
参考〔6。4〕
scanPackageChildLI(parseResult。pkg,parseFlags,scanFlags,
currentTime,null);
}catch(PackageManagerExceptione){
errorCodee。error;
Slog。w(TAG,FailedtoscanparseResult。scanFile:e。getMessage());
}
}elseif(throwableinstanceofPackageParser。PackageParserException){
PackageParser。PackageParserExceptione(PackageParser。PackageParserException)
throwable;
errorCodee。error;
Slog。w(TAG,FailedtoparseparseResult。scanFile:e。getMessage());
}else{
thrownewIllegalStateException(Unexpectedexceptionoccurredwhileparsing
parseResult。scanFile,throwable);
}
Deleteinvaliduserdataapps
如果是非系统apk并且解析失败
if((scanFlagsSCANASSYSTEM)0
errorCode!PackageManager。INSTALLSUCCEEDED){
logCriticalInfo(Log。WARN,
DeletinginvalidpackageatparseResult。scanFile);
非系统Package扫描失败,删除文件
removeCodePathLI(parseResult。scanFile);
}
}
}
}
6。3〔ParallelPackageParser。java〕submit
把扫描路径中的APK等内容,放入队列mQueue,并把parsePackage()赋给ParseResult,用于后面的调用
publicvoidsubmit(FilescanFile,intparseFlags){
mService。submit((){
ParseResultprnewParseResult();
Trace。traceBegin(TRACETAGPACKAGEMANAGER,parallelparsePackage〔scanFile〕);
try{
PackageParserppnewPackageParser();
pp。setSeparateProcesses(mSeparateProcesses);
pp。setOnlyCoreApps(mOnlyCore);
pp。setDisplayMetrics(mMetrics);
pp。setCacheDir(mCacheDir);
pp。setCallback(mPackageParserCallback);
pr。scanFilescanFile;
pr。pkgparsePackage(pp,scanFile,parseFlags);
}catch(Throwablee){
pr。throwablee;
}finally{
Trace。traceEnd(TRACETAGPACKAGEMANAGER);
}
try{
mQueue。put(pr);
}catch(InterruptedExceptione){
Thread。currentThread()。interrupt();
Propagateresulttocallersoftake()。
Thisishelpfultopreventmainthreadfromgettingstuckwaitingon
ParallelPackageParsertofinishincaseofinterruption
mInterruptedInThreadThread。currentThread()。getName();
}
});
}
通过parsePackage进行apk解析,如果传入的packageFile是目录,调用parseClusterPackage()解析,
如果传入的是APK文件,就调用parseMonolithicPackage()解析
publicPackageparsePackage(FilepackageFile,intflags,booleanuseCaches)
throwsPackageParserException{
。。。
if(packageFile。isDirectory()){
如果传入的packageFile是目录,调用parseClusterPackage()解析
parsedparseClusterPackage(packageFile,flags);
}else{
如果是APK文件,就调用parseMonolithicPackage()解析
parsedparseMonolithicPackage(packageFile,flags);
}
。。。
returnparsed;
}
我们先来看看parseClusterPackage()
作用:解析给定目录中包含的所有apk,将它们视为单个包。这还可以执行完整性检查,比如需要相同的包名和版本代码、单个基本APK和惟一的拆分名称
首先通过parseClusterPackageLite()对目录下的apk文件进行初步分析,主要区别是核心应用还是非核心应用。核心应用只有一个,非核心应用可以没有,或者多个,非核心应用的作用主要用来保存资源和代码。然后对核心应用调用parseBaseApk分析并生成Package。对非核心应用调用parseSplitApk,分析结果放在前面的Package对象中
privatePackageparseClusterPackage(FilepackageDir,intflags)throwsPackageParserException{
获取应用目录的PackageLite对象,这个对象分开保存了目录下的核心应用以及非核心应用的名称
finalPackageLiteliteparseClusterPackageLite(packageDir,0);
如果lite中没有核心应用,退出
if(mOnlyCoreApps!lite。coreApp){
thrownewPackageParserException(INSTALLPARSEFAILEDMANIFESTMALFORMED,
NotacoreApp:packageDir);
}
Buildthesplitdependencytree。
构建分割的依赖项树
SparseArrayint〔〕splitDependenciesnull;
finalSplitAssetLoaderassetLoader;
if(lite。isolatedSplits!ArrayUtils。isEmpty(lite。splitNames)){
try{
splitDependenciesSplitAssetDependencyLoader。createDependenciesFromPackage(lite);
assetLoadernewSplitAssetDependencyLoader(lite,splitDependencies,flags);
}catch(SplitAssetDependencyLoader。IllegalDependencyExceptione){
thrownewPackageParserException(INSTALLPARSEFAILEDBADMANIFEST,e。getMessage());
}
}else{
assetLoadernewDefaultSplitAssetLoader(lite,flags);
}
try{
finalAssetManagerassetsassetLoader。getBaseAssetManager();
finalFilebaseApknewFile(lite。baseCodePath);
对核心应用解析
finalPackagepkgparseBaseApk(baseApk,assets,flags);
if(pkgnull){
thrownewPackageParserException(INSTALLPARSEFAILEDNOTAPK,
FailedtoparsebaseAPK:baseApk);
}
if(!ArrayUtils。isEmpty(lite。splitNames)){
finalintnumlite。splitNames。length;
pkg。splitNameslite。splitNames;
pkg。splitCodePathslite。splitCodePaths;
pkg。splitRevisionCodeslite。splitRevisionCodes;
pkg。splitFlagsnewint〔num〕;
pkg。splitPrivateFlagsnewint〔num〕;
pkg。applicationInfo。splitNamespkg。splitNames;
pkg。applicationInfo。splitDependenciessplitDependencies;
pkg。applicationInfo。splitClassLoaderNamesnewString〔num〕;
for(inti0;inum;i){
finalAssetManagersplitAssetsassetLoader。getSplitAssetManager(i);
对非核心应用的处理
parseSplitApk(pkg,i,splitAssets,flags);
}
}
pkg。setCodePath(packageDir。getCanonicalPath());
pkg。setUse32bitAbi(lite。use32bitAbi);
returnpkg;
}catch(IOExceptione){
thrownewPackageParserException(INSTALLPARSEFAILEDUNEXPECTEDEXCEPTION,
Failedtogetpath:lite。baseCodePath,e);
}finally{
IoUtils。closeQuietly(assetLoader);
}
}
再看parseMonolithicPackage(),它的作用是解析给定的APK文件,将其作为单个单块包处理。
最终也是调用parseBaseApk()进行解析,我们接下来看下parseBaseApk()
publicPackageparseMonolithicPackage(FileapkFile,intflags)throwsPackageParserException{
finalPackageLiteliteparseMonolithicPackageLite(apkFile,flags);
if(mOnlyCoreApps){
if(!lite。coreApp){
thrownewPackageParserException(INSTALLPARSEFAILEDMANIFESTMALFORMED,
NotacoreApp:apkFile);
}
}
finalSplitAssetLoaderassetLoadernewDefaultSplitAssetLoader(lite,flags);
try{
对核心应用解析
finalPackagepkgparseBaseApk(apkFile,assetLoader。getBaseAssetManager(),flags);
pkg。setCodePath(apkFile。getCanonicalPath());
pkg。setUse32bitAbi(lite。use32bitAbi);
returnpkg;
}catch(IOExceptione){
thrownewPackageParserException(INSTALLPARSEFAILEDUNEXPECTEDEXCEPTION,
Failedtogetpath:apkFile,e);
}finally{
IoUtils。closeQuietly(assetLoader);
}
}
parseBaseApk()主要是对AndroidManifest。xml进行解析,解析后所有的信息放在Package对象中。
privatePackageparseBaseApk(FileapkFile,AssetManagerassets,intflags)
throwsPackageParserException{
finalStringapkPathapkFile。getAbsolutePath();
。。。
XmlResourceParserparsernull;
。。。
finalintcookieassets。findCookieForPath(apkPath);
if(cookie0){
thrownewPackageParserException(INSTALLPARSEFAILEDBADMANIFEST,
Failedaddingassetpath:apkPath);
}
获得一个XML资源解析对象,该对象解析的是APK中的AndroidManifest。xml文件。
parserassets。openXmlResourceParser(cookie,ANDROIDMANIFESTFILENAME);
finalResourcesresnewResources(assets,mMetrics,null);
finalString〔〕outErrornewString〔1〕;
再调用重载函数parseBaseApk()最终到parseBaseApkCommon(),解析AndroidManifest。xml后得到一个Package对象
finalPackagepkgparseBaseApk(apkPath,res,parser,flags,outError);
。。。
pkg。setVolumeUuid(volumeUuid);
pkg。setApplicationVolumeUuid(volumeUuid);
pkg。setBaseCodePath(apkPath);
pkg。setSigningDetails(SigningDetails。UNKNOWN);
returnpkg;
。。。
}
从AndroidManifest。xml中获取标签名,解析标签中的各个item的内容,存入Package对象中
例如获取标签application、permission
privatePackageparseBaseApkCommon(Packagepkg,SetacceptedTags,Resourcesres,
XmlResourceParserparser,intflags,String〔〕outError)throwsXmlPullParserException,
IOException{
TypedArraysares。obtainAttributes(parser,
com。android。internal。R。styleable。AndroidManifest);
拿到AndroidManifest。xml中的sharedUserId,一般情况下有android。uid。system等信息
Stringstrsa。getNonConfigurationString(
com。android。internal。R。styleable。AndroidManifestsharedUserId,0);
while((typeparser。next())!XmlPullParser。ENDDOCUMENT
(type!XmlPullParser。ENDTAGparser。getDepth()outerDepth)){
从AndroidManifest。xml中获取标签名
StringtagNameparser。getName();
如果读到AndroidManifest。xml中的tag是application,执行parseBaseApplication()进行解析
if(tagName。equals(TAGAPPLICATION)){
if(foundApp){
。。。
}
foundApptrue;
解析application的信息,赋值给pkg
if(!parseBaseApplication(pkg,res,parser,flags,outError)){
returnnull;
}
。。。
如果标签是permission
elseif(tagName。equals(TAGPERMISSION)){
进行permission的解析
if(!parsePermission(pkg,res,parser,outError)){
returnnull;
}
。。。。
}
}
}
}
上面解析AndroidManifest。xml,
会得到application、overlay、permission、usespermission等信息
我们下面就针对application进行展开分析一下,进入parseBaseApplication()函数
privatebooleanparseBaseApplication(Packageowner,Resourcesres,
XmlResourceParserparser,intflags,String〔〕outError)
while((typeparser。next())!XmlPullParser。ENDDOCUMENT
(type!XmlPullParser。ENDTAGparser。getDepth()innerDepth)){
获取application子标签的标签内容
StringtagNameparser。getName();
如果标签是activity
if(tagName。equals(activity)){
解析Activity的信息,把activity加入Package对象
ActivityaparseActivity(owner,res,parser,flags,outError,cachedArgs,false,
owner。baseHardwareAccelerated);
if(anull){
mParseErrorPackageManager。INSTALLPARSEFAILEDMANIFESTMALFORMED;
returnfalse;
}
hasActivityOrder(a。order!0);
owner。activities。add(a);
}elseif(tagName。equals(receiver)){
如果标签是receiver,获取receiver信息,加入Package对象
ActivityaparseActivity(owner,res,parser,flags,outError,cachedArgs,
true,false);
if(anull){
mParseErrorPackageManager。INSTALLPARSEFAILEDMANIFESTMALFORMED;
returnfalse;
}
hasReceiverOrder(a。order!0);
owner。receivers。add(a);
}elseif(tagName。equals(service)){
如果标签是service,获取service信息,加入Package对象
ServicesparseService(owner,res,parser,flags,outError,cachedArgs);
if(snull){
mParseErrorPackageManager。INSTALLPARSEFAILEDMANIFESTMALFORMED;
returnfalse;
}
hasServiceOrder(s。order!0);
owner。services。add(s);
}elseif(tagName。equals(provider)){
如果标签是provider,获取provider信息,加入Package对象
ProviderpparseProvider(owner,res,parser,flags,outError,cachedArgs);
if(pnull){
mParseErrorPackageManager。INSTALLPARSEFAILEDMANIFESTMALFORMED;
returnfalse;
}
owner。providers。add(p);
}
。。。
}
}
在PackageParser扫描完一个APK后,此时系统已经根据该APK中AndroidManifest。xml,创建了一个完整的Package对象,
下一步就是将该Package加入到系统中。此时调用的函数就是另外一个scanPackageChildLI
6。4〔PackageManagerService。java〕scanPackageChildLI()
调用addForInitLI()在platform初始化时,把Package内容加入到内部数据结构
privatePackageParser。PackagescanPackageChildLI(PackageParser。Packagepkg,
finalParseFlagsintparseFlags,ScanFlagsintscanFlags,longcurrentTime,
NullableUserHandleuser)
throwsPackageManagerException{
。。。
Scantheparent
PackageParser。PackagescannedPkgaddForInitLI(pkg,parseFlags,
scanFlags,currentTime,user);
Scanthechildren
finalintchildCount(pkg。childPackages!null)?pkg。childPackages。size():0;
for(inti0;ichildCount;i){
PackageParser。PackagechildPackagepkg。childPackages。get(i);
在平台初始化期间向内部数据结构添加新包。
在platform初始化时,把Package内容加入到内部数据结构,
addForInitLI(childPackage,parseFlags,scanFlags,
currentTime,user);
}
if((scanFlagsSCANCHECKONLY)!0){
returnscanPackageChildLI(pkg,parseFlags,scanFlags,currentTime,user);
}
}
在addForInitLI()中,进行安装包校验、签名检查、apk更新等操作,把Package加入系统
privatePackageParser。PackageaddForInitLI(PackageParser。Packagepkg,
ParseFlagsintparseFlags,ScanFlagsintscanFlags,longcurrentTime,
NullableUserHandleuser)
throwsPackageManagerException{
判断系统应用是否需要更新
synchronized(mPackages){
更新子应用
if(isSystemPkgUpdated){
。。。
}
if(isSystemPkgBetter){
更新安装包到system分区中
synchronized(mPackages){
justremovetheloadedentriesfrompackagelists
mPackages。remove(pkgSetting。name);
}
。。。
创建安装参数InstallArgs
finalInstallArgsargscreateInstallArgsForExisting(
pkgSetting。codePathString,
pkgSetting。resourcePathString,getAppDexInstructionSets(pkgSetting));
args。cleanUpResourcesLI();
synchronized(mPackages){
mSettings。enableSystemPackageLPw(pkgSetting。name);
}
}
安装包校验
collectCertificatesLI(pkgSetting,pkg,forceCollect,skipVerify);
。。。
try(PackageFreezerfreezerfreezePackage(pkg。packageName,
scanPackageInternalLI)){
如果两个apk签名不匹配,则调用deletePackageLIF方法清除apk文件及其数据
deletePackageLIF(pkg。packageName,null,true,null,0,null,false,null);
}
。。。
更新系统apk程序
InstallArgsargscreateInstallArgsForExisting(
pkgSetting。codePathString,
pkgSetting。resourcePathString,getAppDexInstructionSets(pkgSetting));
synchronized(mInstallLock){
args。cleanUpResourcesLI();
}
}
如果新安装的系统APP会被旧的APP数据覆盖,所以需要隐藏隐藏系统应用程序,并重新扫描dataapp目录
if(shouldHideSystemApp){
synchronized(mPackages){
mSettings。disableSystemPackageLPw(pkg。packageName,true);
}
}
}
回顾一下整个APK的扫描过程:
按照coreappsystemappotherapp优先级扫描APK,解析AndroidManifest。xml文件,得到各个标签内容
解析XML文件得到的信息由Package保存。从该类的成员变量可看出,和Android四大组件相关的信息分别由activites、receivers、providers、services保存。由于一个APK可声明多个组件,因此activites和receivers等均声明为ArrayList。
在PackageParser扫描完一个APK后,此时系统已经根据该APK中AndroidManifest。xml,创建了一个完整的Package对象,下一步就是将该Package加入到系统中
非系统Package扫描失败,删除文件
爱的教育读书笔记800字3篇爱的教育读书笔记800字范文一《爱的教育》是意大利作家亚米契斯的一部著作。这部书很有意思的是作者以1个小学生的名义,经过日记本的形式,讲述了很小的故事,然后将爱的教育融入……
成长的滋味作文450字在成长的历程中,会尝遍酸、甜、苦、辣、咸。但我觉得我的成长应该是快乐美好的。在我升入初中的这一个多月来,我结识了许多新朋友,收获了许多笑脸,与他们进行交流,这让我感到快乐……
有哪些东西是能买二手就可以不用买全新的?人体工学椅,500强大公司淘汰下来的各种万元级产品,顶级产品预期寿命大概是15年左右,大公司一般三五年换一次,能买到二手的直接上,比较值。渔具,当然首先对渔具要了解。有些……
二年级日记照顾爸爸一天早晨刚起床,我和妈妈听到哎呦一声,就向爸爸房间走去。推门一看,见爸爸躺在床上手捂着肚子一脸痛苦的样子。我心疼地拉着爸爸的手说:怎么了?爸爸。爸爸见我进屋了,难受地说:慧慧,……
小学生春游去动物园的作文4篇小学生春游去动物园的作文1又是一个春暖花开的季节,为了丰富学生的生活体验,3月31日天正小学举行了春游活动,来到了红山动物园,让同学们与大自然亲密接触,在大自然中去寻找春……
金融如何更好支持科技创新?业内建议创设专精特新贷试点构建银行近日中央深改委会议审议通过相关工作方案,强调要聚焦金融服务科技创新的短板弱项,完善金融支持创新体系。CF40今日发布的《金融支持科技创新研究》课题研究报告指出,在金融更好……
为快手用户提供虚假点赞关注转发刷量服务,河南一公司被判赔偿1一次控制多部手机为快手用户提供虚假点赞、关注、转发等刷量服务,河南某公司被快手诉诸法院,最终,该公司被判赔偿快手经济损失100万元。4月25日,在世界知识产权日来临之际,河南省……
感恩老师教师节英语作文Teacher’sDaycomesonSeptember10theveryyear。Onthedayweusuallygiveourteacherscardstoshowour……
美丽的仙岳山作文我们经过一段时间的车程,很快就到了仙岳山脚下,到了那里,入眼的是一大片绿色的树木,把仙岳山封的严严实实的,有时候连一缕阳光都进不去。在爬仙岳山的时候,我发现到处都是挺拔粗……
我失明了五年级作文以前,我在马路上、电视上见过盲人,我十分纳闷:盲人怎么生活呢?没想到,今天放学前,老师布置了一项作业:让我们去体验失明,做一回小盲人。清早,我早早地起了床,刷完牙,吃完早……
关于军训的作文军训前ldquo;严格训练,严格要求rdquo;这个口号在操场一次又一次地喊响。我们踏着有力整齐的步伐,在主席台前经过,我们在军训的努力赢得了领导们的掌声。这让我想起军训的许多事情……
无题阳光挣扎着拨开云罅,透过窗棂间水雾未逝的玻璃,我额眉蹙成一个川壑,眼的间缝开始地震,睫毛抖动不住,最后终于地裂开来,顿时阳光倾灌hellip;hellip;我赌气似的怒睁……
GMAT作文考试佳句精选GMAT作文考试佳句精选Perhapsitistimetoreexaminetheideathatcollegedegreesareaguaranteeofintelli……
介绍一个内网穿透,解决方案没有公网IP让我们的网络设备变成了孤岛,这让远程访问网络设备变得困难重重。当你想要访问自己的nas,对不起你没有公网IP;当你要发布自己的网站,对不起你没有公网IP。怎么办?搭……
去e族动漫的初中作文600字为了摆脱醉汉的烦人,我们三个孩子和三个妈妈到了e族动漫去玩,那里的人很多,我们买了一盒子游戏币然后就开始玩了。我们先去玩投篮,我和大姑一起头,我们虽然投的比较准,但是常常……
秋游当我在前些日子得知周五要秋游,真是悲喜交加。因为这虽然是我们期盼了好长时间的一次活动,,也是人生中的最后一次秋游了。所以,这次秋游我也显得分外珍惜。终于,星期五终于到了,我们怀……
研究生入学考试英语小作文DearMr。Wang,Iamwritingtoinformyouaboutmydecisiontoresignfrommycurrentposition。Thereareaf……
醒醒吧,人类我喜欢战争,喜欢鲜血,喜欢疾病。看看那一战、二战创造出的成就是多么的伟大;看看那南京大屠杀创造出的鲜血是多么的鲜艳;看看那艾滋病创造出的悲痛是多么的令人振奋啊!我原本是一……
生命需要勇气作文怎么写当你走在充满荆棘的人生之路时,擦把汗,拭把泪,相信:有勇气,必将有一片海阔天空。以下是小编整理的生命需要勇气作文,欢迎阅读。生命需要勇气作文1树,砍断枝条还能再生;草,烧……
最熟悉的陌生人小学500字作文作文一:最熟悉的陌生人什么时候开始,我们形同陌生人一样。彼此见面不见昔日开玩笑打闹的场景,有的只是淡淡的问候和玩笑,空气中弥漫着尴尬的气氛。我们,到底只是熟悉的陌生人罢了……
又是一年雪飘时1200字作文漫天的雪花飞舞着,在经历了几个暖冬之后,任谁也想不到会有这么一场繁盛的雪,在大江南北,与我们不期而遇。在我短短十七个春秋里,雪却注定是一场少见的风景,真正记忆里铭心刻骨的……
啊忘不了500字作文忘,忘不了!忘不了丁爷爷慈祥的脸庞;忘不了她的一言一行;忘不了他桌上高雅的水仙这一切都忘不了丁爷爷生前是一个看门的,他的本职就是晚上九点关大门,可他总是尽自己的力量为大伙……
我最留恋的地方作文450字留恋,是一种永远的思绪,也是永远的记忆,我最留恋的地方是。。。。。。以下是小编收集的关于《我最留恋的地方作文》,仅供大家阅读参考!我最留恋的地方作文450字我是一个留守女……
意外的收获优秀的作文今天,爸爸约我和我的一个朋友去爬山,可我们一点儿都不情愿,最后爸爸对我说了一句话:今天可能有意外的收获。什么意外的收获,我的心情一下多云转晴,迫不及待的坐上摩托车出发了。……