OPhone程序开发入门之音乐播放器_第1页
OPhone程序开发入门之音乐播放器_第2页
OPhone程序开发入门之音乐播放器_第3页
OPhone程序开发入门之音乐播放器_第4页
OPhone程序开发入门之音乐播放器_第5页
已阅读5页,还剩14页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

OPhone平台提供了完整的多媒体解决方案。为开发者提供了统一的,简单易用的开发接口。本文首先介绍了OPhone平台的多媒体框架,然后详细介绍了在OPhone平台上开发音乐播放程序所需的基本知识。通过一步一步构建一个简单的音乐播放器示例程序,来帮助读者了解具体的开发过程。该示例涵盖了Application,Activity,Service,Intent,BroadCast Receiver等基本概念,使读者对OPhone程序的开发有一个全面的了解,进一步巩固和熟悉这些基本概念。最后介绍了如何利用MAT工具分析OPhone 程序。 本文适合OPhone平台开发的初学者阅读。(作者:CMRI 孟钊)OPhone平台的多媒体架构 在开始构建我们的示例程序前,先让我们大概了解一下OPhone平台的多媒体框架。图 一 图一是OPhone平台的整体框架结构,从图上我们可以看出OPhone平台大致可以分成以下几个层次:1. 最上层是Application层。它包含了主屏,电话,浏览器,地址本等核心的应用程序。我们将开发的音乐播放器也属于这一层。 2. 第二层是Application Framework层。这一层为开发者提供了完整的编程接口。多媒体部分提供了MediaPlayer, MediaRecorder等接口。同时MediaProvider,MediaScanner等系统服务也对媒体文件的管理提供了支持。本文将重点介绍它们的使用。 3. 第三层是Library层, 它由一系列的c/c+库组成,这些库的能力通过JNI封装成java接口,由Application Framework层提供给开发者。多媒体系统库OpenCore,它是OPhone多媒体的核心,来源于PacketVideo。它非常复杂,提供了完整的多媒体解决方案。 4. 最底层为Linux Kernel和驱动,负责与硬件的数据交互等。 图二说明了在OPhone平台中播放音乐文件时的调用关系。对于应用程序开发者来说,需要重点学习和关注的是如何使用Appliation Framework层提供给开发者的接口。音乐媒体信息的管理在开始构架程序之前,我们需要准备一下必须的基本知识。首先来了解一下在OPhone平台中应该如何获取音乐文件的信息以及如何管理这些信息。OPhone系统提供了MediaScanner,MediaProvider,MediaStore等接口,并且提供了一套数据库表格,通过Content Provider的方式提供给用户。当手机开机或者有SD卡插拔等事件发生时,系统将会自动扫描SD卡和手机内存上的媒体文件,如audio,video,图片等,将相应的信息放到定义好的数据库表格中。在这个程序中,我们不需要关心如何去扫描手机中的文件,只要了解如何查询和使用这些信息就可以了。MediaStore中定义了一系列的数据表格,通过ContentResolver提供的查询接口,我们可以得到各种需要的信息。下面我们重点介绍如何管理SD卡上的音乐文件信息。先来了解一下ContentResolver的查询接口:view plaincopy to clipboardprint?1. Cursorquery(Uriuri,Stringprojection,Stringselection,StringselectionArgs,StringsortOrder);Cursor query(Uri uri, String projection, String selection, String selectionArgs, String sortOrder); Uri:指明要查询的数据库名称加上表的名称,从MediaStore中我们可以找到相应信息的参数,具体请参考开发文档。 Projection: 指定查询数据库表中的哪几列,返回的游标中将包括相应的信息。Null则返回所有信息。 selection: 指定查询条件 selectionArgs:参数selection里有 ?这个符号是,这里可以以实际值代替这个问号。如果selection这个没有?的话,那么这个String数组可以为null。 SortOrder:指定查询结果的排列顺序查询所有歌曲:view plaincopy to clipboardprint?1. Cursorcursor=query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Audio.Media.DEFAULT_SORT_ORDER);Cursor cursor = query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);该命令将返回所有在外部存储卡上的音乐文件的信息,其中常用的信息如下:view plaincopy to clipboardprint?1. MediaStore.Audio.Media._ID:歌曲IDIntid=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);MediaStore.Audio.Media.TITLE:歌曲的名称Stringtilte=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);MediaStore.Audio.Media.ALBUM:歌曲的专辑名Stringalbum=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM);MediaStore.Audio.Media.ARTIST:歌曲的歌手名Stringartist=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST);MediaStore.Audio.Media.DATA:歌曲文件的路径Stringurl=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);MediaStore.Audio.Media.DURATION:歌曲的总播放时长Intduration=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION);MediaStore.Audio.Media.SIZE:歌曲文件的大小Intsize=cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE);MediaStore.Audio.Media._ID:歌曲ID Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID); MediaStore.Audio.Media.TITLE:歌曲的名称 String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE); MediaStore.Audio.Media.ALBUM :歌曲的专辑名 String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM); MediaStore.Audio.Media.ARTIST:歌曲的歌手名 String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST); MediaStore.Audio.Media.DATA:歌曲文件的路径 String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA); MediaStore.Audio.Media.DURATION:歌曲的总播放时长 Int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION); MediaStore.Audio.Media.SIZE: 歌曲文件的大小 Int size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE); 查询歌手信息:view plaincopy to clipboardprint?1. Cursorcursor=query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Audio.Artists.DEFAULT_SORT_ORDER);Cursor cursor = query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Artists.DEFAULT_SORT_ORDER);该命令将返回所有在外部存储卡上的歌手信息,其中常用的信息如下:view plaincopy to clipboardprint?1. MediaStore.Audio.Artists._ID:歌手idIntid=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID);MediaStore.Audio.Artists.ARTIST:歌手姓名Stringname=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST);MediaStore.Audio.Artists.NUMBER_OF_ALBUMS:共有多少该歌手的专辑IntnumOfAlbum=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS);MediaStore.Audio.Artists.NUMBER_OF_TRACKS:共有多少该歌手的歌曲IntnumOfSong=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS);MediaStore.Audio.Artists._ID:歌手id Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID); MediaStore.Audio.Artists.ARTIST :歌手姓名 String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST); MediaStore.Audio.Artists.NUMBER_OF_ALBUMS: 共有多少该歌手的专辑 Int numOfAlbum = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS); MediaStore.Audio.Artists.NUMBER_OF_TRACKS: 共有多少该歌手的歌曲 Int numOfSong = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS); 查询专辑信息:view plaincopy to clipboardprint?1. Cursorcursor=query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, null, null,null, MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); 该命令将返回所有在外部存储卡上的专辑信息,其中常用的信息如下:view plaincopy to clipboardprint?1. MediaStore.Audio.Albums._ID:专辑idIntid=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums._ID);MediaStore.Audio.Albums.ALBUM:专辑名称Stringname=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM);MediaStore.Audio.Albums.NUMBER_OF_SONGS:共用多少歌曲属于该专辑IntnumOfSong=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS);MediaStore.Audio.Albums._ID :专辑id Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums._ID); MediaStore.Audio.Albums.ALBUM:专辑名称 String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM); MediaStore.Audio.Albums.NUMBER_OF_SONGS:共用多少歌曲属于该专辑 Int numOfSong = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS); 查询播放列表view plaincopy to clipboardprint?1. Cursorcursor=query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,null,null,null,MediaStore.Audio.Playlists.DATE_ADDED+asc);Cursor cursor = query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Playlists.DATE_ADDED + asc);该命令将返回所有在外部存储卡上的专辑信息,其中常用的信息如下:view plaincopy to clipboardprint?1. MediaStore.Audio.Playlists._ID:播放列表idIntid=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID);MediaStore.Audio.Playlists.NAME:播放列表名称Stringname=cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME);MediaStore.Audio.Playlists.DATE_ADDED:添加时间longdateAdded=cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_ADDED);MediaStore.Audio.Playlists.DATE_MODIFIED:修改时间longdateModified=cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_MODIFIED);MediaStore.Audio.Playlists._ID :播放列表id Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID); MediaStore.Audio.Playlists.NAME:播放列表名称 String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME); MediaStore.Audio.Playlists.DATE_ADDED :添加时间 long dateAdded = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_ADDED); MediaStore.Audio.Playlists.DATE_MODIFIED :修改时间 long dateModified = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_MODIFIED); 通过组合这些查询结果,指定查询条件,用户可以很方便的查询指定的媒体信息,比如:查询属于指定歌手(歌手id 为 aid)的歌曲:view plaincopy to clipboardprint?1. query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,MediaStore.Audio.Media.ARTIST_ID+=+aid,null,MediaStore.Audio.Media.TITLE);query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Audio.Media.ARTIST_ID + = + aid, null, MediaStore.Audio.Media.TITLE); 查询属于指定专辑(专辑id 为 aid)的歌曲:view plaincopy to clipboardprint?1. returnquery(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,null,MediaStore.Audio.Media.ALBUM_ID+=+aid,null,MediaStore.Audio.Media.TITLE);return query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, MediaStore.Audio.Media.ALBUM_ID + = + aid, null, MediaStore.Audio.Media.TITLE);以上我们重点介绍了音乐媒体信息的查询方法,对于媒体信息的增删改等操作主要集中在对播放列表的管理上,也是通过Content Resolver的insert,update,delete等接口来实现的。只要搞清楚了各个参数的含义,相应URI以及各个字段的义,很容易实现。由于篇幅原因,我们不再详细介绍,有兴趣的朋友可以查看OPhone开发文档。音乐播放音乐文件的播放功能是由MediaPlayer类实现的,MediaPlayer提供了常用的接口,比如播放,暂停,停止,快速定位等。 播放音乐文件的基本调用流程:1. 生成MediaPlayer实例。 2. 设置播放源(文件) 3. 准备播放 4. 开始播放 view plaincopy to clipboardprint?1. MediaPlayermp=newMediaPlayer();mp.setDataSource(file_to_play);mp.prepare();mp.start();MediaPlayer mp = new MediaPlayer(); mp.setDataSource(file_to_play); mp.prepare(); mp.start();以上代码即可以完成最简单的音乐播放功能。 除了MediaPlayer类,我们还需要注意几个播放器件Listener的使用,它们提供了播放器的更多的状态信息。 1.MediaPlayer.OnBufferingUpdateListener 当播放网络上的媒体文件或者流媒体时 MediaPlayer.OnBufferingUpdateListener 的onBufferingUpdate(MediaPlayer mp, int percent)接口函数会被回调,通知当前的缓冲进度信息。 通过setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) 函数来注册该Listener 2.MediaPlayer.OnCompletionListener 当前歌曲播放结束后,MediaPlayer.OnCompletionListener的 onCompletion(MediaPlayer mp) 接口会被回调,通知歌曲结束事件。 通过setOnCompletionListener(MediaPlayer.OnCompletionListener listener) 函数来注册该监听器 3.MediaPlayer.OnErrorListener 当由于某种原因,MediaPlayer进入错误状态时,MediaPlayer.OnBufferingUpdateListener的onError(MediaPlayer mp, int what, int extra)接口会被回调,通知错误信息。此时MediaPlayer 应该调用reset()函数,将MediaPlayer重新置于idle状态。如果发生无法回复的错误,需要重新获取MediaPlayer的实例。 4.MediaPlayer.OnPreparedListener 当播放网络媒体文件或流媒体时,播放器的准备时间较长,播放器准备完毕可以开始播放时,MediaPlayer.OnPreparedListener的onPrepared(MediaPlayer mp)接口会被回调,通知该信息。当播放器需要支持播放流媒体或者网络媒体文件时,建议使用prepareAsync()接口调用来准备播放器,同时通过MediaPlayer.OnPreparedListener来监听prepared信息。这样可以避免因为网络等因素造成的MediaPlayer准备时间过长进而导致程序长时间无响应。构建音乐播放器程序 在学习了媒体信息管理和媒体播放的基本内容后,我们现在可以开始动手构建我们的简单播放器示例程序了。一创建工程 在Eclipse开发环境中创建一个新的Android Project.File New Android Project.设置工程名为MusicPlayerDemo, 设置packages名为 com.ophone二指定程序的Application,添加MusicPlayerDemoApp 添加MusicPlayerDemoApp类,它继承自 android.app.Application。Application类用来存储程序的状态,它存在于整个程序的生命周期之中。修改AndroidManifest.xml如下,指定MusicPlayerDemoApp为示例程序的Application.view plaincopy to clipboardprint?1. 我们需要注意Application的两个函数: onCreate() 和 onTerminate(). 当程序开始运行时,onCreate()函数会首先被调用,此时没有任何其他的对象在运行,在这里我们可以进行一些初始化的工作。当程序结束时, onTerminate()函数会被调用,程序进程将会退出,我们可以在此做一些最终的清理工作。需要注意的是,当因为系统资源紧张等问题,程序被系统kill的时候,onTerminate()不会被调用到,程序将直接退出。稍后我们再来修改MusicPlayerDemoApp,先往下继续。三管理音乐信息的类MusicDBController为了使接口整洁,便于管理和使用,我们将在第三章介绍的查询管理音乐信息的方法统一封装在MusicDBController类中。 view plaincopy to clipboardprint?1. publicstaticMusicDBControllergetInstance(MusicPlayerDemoAppapp)if(sInstance=null)sInstance=newMusicDBController(app);returnsInstance;privateMusicDBController(MusicPlayerDemoAppapp)mApp=app;privateCursorquery(Uri_uri,Stringprjs,Stringselections,StringselectArgs,Stringorder)ContentResolverresolver=mApp.getContentResolver();if(resolver=null)returnnull;returnresolver.query(_uri,prjs,selections,selectArgs,order);public static MusicDBController getInstance(MusicPlayerDemoApp app) if(sInstance = null) sInstance = new MusicDBController(app); return sInstance; private MusicDBController(MusicPlayerDemoApp app) mApp = app; private Cursor query(Uri _uri, String prjs, String selections, String selectArgs, String order) ContentResolver resolver = mApp.getContentResolver(); if (resolver = null) return null; return resolver.query(_uri, prjs, selections, selectArgs, order);MusicDBController采用单例模式,使程序中只有唯一的实例。我们传入MusicPlayerDemoApp 作为Context生成Content Resolver,用来查询媒体库。现在,我们修改MusicPlayerDemoApp,添加一个MusicDBController的成员,并在onCreate()中初始化它。view plaincopy to clipboardprint?1. privateMusicDBControllermDBContorller=null;publicvoidonCreate()/TODOAuto-generatedmethodstubsuper.onCreate();/initMusicDBControllermDBContorller=MusicDBController.getInstance(this);并且提供一个获取MusicDBController的接口:publicMusicDBControllergetMusicDBController()returnmDBContorller;private MusicDBController mDBContorller = null; public void onCreate() / TODO Auto-generated method stub super.onCreate(); / init MusicDBController mDBContorller = MusicDBController.getInstance(this); 并且提供一个获取MusicDBController的接口: public MusicDBController getMusicDBController() return mDBContorller;这样程序中的任何Activity和Serivce都可以通过getApplicatio()函数得到MusicPlayerDemoApp,再通过getMusicDBController()接口获取MusicDBController,进而获取所需要的媒体信息。四展示媒体库MusicListActivity 和 MusicListAdapter。 首先添加MusicListAdapter,它继承自SimpleCursorAdapter。通过重载bindView()函数, 把媒体库信息绑定到指定的ListView上。我们使用android.R.layout.cmcc_list_5作为ListView的layout,它的布局定义如下: android.R.layout.cmcc_list_5: android.R.id.listicon1 图片 android.R.id.text1 左上文字 android.R.id.text2 左下文字 android.R.id.text3 右下文字view plaincopy to clipboardprint?1. publicvoidbindView(Viewview,Contextcontext,Cursorcursor)super.bindView(view,context,cursor);TextViewtitleView=(TextView)view.findViewById(android.R.id.text1);TextViewartistView=(TextView)view.findViewById(android.R.id.text2);TextViewdurationView=(TextView)view.findViewById(android.R.id.text3);ImageViewimageView=(ImageView)view.findViewById(android.R.id.listicon1);/SeticonimageView.setImageResource(R.drawable.cmcc_list_music);/SettracknametitleView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE);/SetartistnameartistView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST);/Setdurationintduration=cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION);durationView.setText(makeTimeString(duration);public void bindView(View view, Context context, Cursor cursor) super.bindView(view, context, cursor); TextView titleView = (TextView) view.findViewById(android.R.id.text1); TextView artistView = (TextView) view.findViewById(android.R.id.text2); TextView durationView = (TextView) view.findViewById(android.R.id.text3); ImageView imageView = (ImageView) view.findViewById(android.R.id.listicon1); / Set icon imageView.setImageResource(R.drawable.cmcc_list_music); / Set track name titleView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE); / Set artist name artistView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST); / Set duration int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION); durationView.setText(makeTimeString(duration); 注意,上面这段代码中的android.R.id.text1,android.R.id.text2,android.R.id.text3 和 android.R.id.listicon1是在我们传入中的ListView(android.R.layout.cmcc_list_5)的layout中定义的。如果你使用了自己定义的layout,请把它们替换成你自己定义的widget id。现在可以来添加我们的第一个Activity MusicListActivity,它以List的形式展示了所有歌曲。MusicListActivity继承自ListActivity。在onCreate()中获取MusicDBController的实例,为获取歌曲信息做准备。view plaincopy to clipboardprint?1. privateMusicDBControllermDBController=null;/*Calledwhentheactivityisfirstcreated.*/OverridepublicvoidonCreate(BundlesavedInstanceState)super.onCreate(savedInstanceState);setContentView(R.layout.main);mDBController=(MusicPlayerDemoApp)getApplication().getMusicDBController();通过MusicListAdapter,我们将从MusicDBController中拿到的媒体库信息,绑定到ListView,我们在onResume()完成这个工作。protectedvoidonResume()super.onResume();mCursor=mDBController.getAllSongs();MusicListAdapteradapter=newMusicListAdapter(this,android.R.layout.cmcc_list_5,mCursor,newString,newint);setListAdapter(adapter);将MusicListActivity添加到AndroidManifest.xml中private MusicDBController mDBController = null; /* Called when the activity is first created. */ Override public void onCreate(Bundle save

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论