要求:
1.实验报告要完整,即要有实验要求与目的、实验内容与步骤(必要流程图或活动图、代码)、运行结果与分析、实验体会与收获;
2.代码要有必要的注释;
3.标题为四号黑体,其他为宋体小四号,行间距为单倍行间距,纸张大小为A4;
4.考核项目
考核项目 | 格式与完成性 | 实验内容 | 实验结果与分析 | 实验体会与收获 | 总成绩 |
权重 | 20% | 60% | 10% | 10% | 100% |
得分 |
-
实验目的
1.了解服务的概述,能够说出什么是服务;
2.掌握服务的创建方式,能够独立创建一个服务;
3. 熟悉服务的生命周期,能够阐述服务生命周期中的方法;
4.掌握服务的两种启动方式,能够实现服务的启动与关闭功能;
5.掌握服务的通信,能够完成仿网易音乐播放器案例。
。
-
实验工具
Android studio。
-
实验内容
运行效果图如下:
-
完成上图的布局;
-
点击“播放”按钮、“暂停”按钮、“继续”按钮完成相应功能。
-
实验过程(含代码)
activity_main.xml: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center" android:background="@drawable/music_bg" tools:context=".MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="160dp"> <RelativeLayout android:layout_width="300dp" android:layout_height="70dp" android:id="@+id/rl_title" android:layout_centerHorizontal="true" android:background="@drawable/title_bg" android:gravity="center_horizontal" android:paddingLeft="80dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tv_music_title" android:layout_marginTop="8dp" android:text="体面" android:textSize="12sp" android:textStyle="bold" android:textColor="@color/black"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tv_type" android:layout_marginTop="4dp" android:layout_below="@id/tv_music_title" android:layout_alignLeft="@id/tv_music_title" android:text="流行音乐" android:textSize="10dp"/> <SeekBar android:layout_width="150dp" android:layout_height="wrap_content" android:id="@+id/sb" android:layout_below="@+id/rl_time" android:layout_alignParentBottom="true" android:thumb="@null"/> <RelativeLayout android:layout_width="150dp" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:id="@+id/rl_time" android:layout_below="@id/tv_type"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tv_progress" android:text="00:00" android:textSize="10dp"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/tv_total" android:layout_alignParentRight="true" android:text="00:00"/> </RelativeLayout> </RelativeLayout> <LinearLayout android:layout_width="340dp" android:layout_height="90dp" android:layout_below="@id/rl_title" android:layout_centerHorizontal="true" android:background="@drawable/btn_bg" android:gravity="center_vertical" android:paddingLeft="120dp" android:paddingRight="10dp"> <Button android:layout_width="0dp" android:layout_height="55dp" android:layout_margin="4dp" android:id="@+id/btn_play" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="播放" android:textSize="10sp"/> <Button android:layout_width="0dp" android:layout_height="55dp" android:id="@+id/btn_pause" android:layout_margin="4dp" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="暂停" android:textSize="10sp"/> <Button android:layout_width="0dp" android:layout_height="55dp" android:id="@+id/btn_continue_play" android:layout_margin="4dp" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="继续" android:textSize="10sp"/> <Button android:layout_width="0dp" android:layout_height="55dp" android:id="@+id/btn_exit" android:layout_margin="4dp" android:layout_weight="1" android:background="@drawable/btn_bg_selector" android:text="退出" android:textSize="10sp"/> </LinearLayout> <ImageView android:layout_width="100dp" android:layout_height="100dp" android:id="@+id/iv_music" android:layout_centerVertical="true" android:layout_marginLeft="35dp" android:layout_marginBottom="50dp" android:src="@drawable/img_music"/> </RelativeLayout> </LinearLayout>
btn_bg_selector.xml: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" > <shape android:shape="rectangle"> <corners android:radius="3dp"/> <solid android:color="#d4d4d4"/> </shape> </item> <item android:state_pressed="false" > <shape android:shape="rectangle"> <corners android:radius="3dp"/> <solid android:color="#ffffff" /> </shape> </item> </selector>
MainActivity.java: package com.example.musicdemo; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.view.View; import android.view.animation.LinearInterpolator; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static SeekBar sb; private static TextView tv_progress,tv_total; private ObjectAnimator animator; private MusicService.MusicControl musicControl; MyServiceConn conn; Intent intent; private boolean isUnbind=false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); } private void init(){ tv_progress=findViewById(R.id.tv_progress); tv_total=findViewById(R.id.tv_total); sb=findViewById(R.id.sb); findViewById(R.id.btn_play).setOnClickListener(this); findViewById(R.id.btn_pause).setOnClickListener(this); findViewById(R.id.btn_continue_play).setOnClickListener(this); findViewById(R.id.btn_exit).setOnClickListener(this); intent=new Intent(this,MusicService.class); conn=new MyServiceConn(); bindService(intent,conn,BIND_AUTO_CREATE); sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { //滑动条进度改变调用此方法,滑动条到末端结束动画 if (progress==seekBar.getMax()){ animator.pause(); //停止播放动画 } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { int progress=seekBar.getProgress(); musicControl.seekTo(progress); } }); ImageView iv_music = (ImageView) findViewById(R.id.iv_music); animator = ObjectAnimator.ofFloat(iv_music, "rotation", 0f, 360.0f); animator.setDuration(10000); //动画旋转一周的时间为10秒 animator.setInterpolator(new LinearInterpolator()); animator.setRepeatCount(-1); //-1表示设置动画无限循环 } public static Handler handler=new Handler(){ @Override public void handleMessage(@NonNull Message msg) { Bundle bundle=msg.getData(); int duration=bundle.getInt("duration"); int currentPosition=bundle.getInt("currentPosition"); sb.setMax(duration); sb.setProgress(currentPosition); int minute=duration/1000/60; int second=duration/1000%60; String strMinute=null; String strSecond=null; if (minute<10){ strMinute="0"+minute;// //如果歌曲时间中的分钟数小于10在分钟前+0 }else { strMinute=minute+""; } if (second<10){ strSecond="0"+second;// //如果歌曲时间中的秒数小于10在秒钟前+0 }else { strSecond=second+""; } tv_total.setText(strMinute+":"+strSecond); minute=currentPosition/1000/60; second=currentPosition/1000%60; if (minute<10){ strMinute="0"+minute; }else { strMinute=minute+""; } if (second<10){ strSecond="0"+second; }else { strSecond=second+""; } tv_progress.setText(strMinute+":"+strSecond); super.handleMessage(msg); } }; class MyServiceConn implements ServiceConnection{ @Override public void onServiceConnected(ComponentName name, IBinder service) { musicControl= (MusicService.MusicControl) service; } @Override public void onServiceDisconnected(ComponentName name) { } } private void unbind(boolean isUnbind){ if (!isUnbind){ musicControl.pausePlay(); unbindService(conn); stopService(intent); } } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_play: musicControl.play(); animator.start(); break; case R.id.btn_pause: musicControl.pausePlay(); animator.pause(); break; case R.id.btn_continue_play: musicControl.continuePlay(); animator.start(); break; case R.id.btn_exit: unbind(isUnbind); isUnbind=true; finish(); break; } } @Override protected void onDestroy() { super.onDestroy(); unbind(isUnbind); } }
AndroidMainfest.xml: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.musicdemo"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Musicdemo"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".MusicService" android:enabled="true" android:exported="true"/> </application> </manifest>
自定义MediaServer.java类,继承Server,然后在AndroidManifest里添加
MusicService.java: package com.example.musicdemo; import android.app.Service; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.Message; import androidx.annotation.Nullable; import java.util.Timer; import java.util.TimerTask; public class MusicService extends Service { private MediaPlayer player; private Timer timer; public MusicService(){ } @Nullable @Override public IBinder onBind(Intent intent) { return new MusicControl(); } @Override public void onCreate() { super.onCreate(); player=new MediaPlayer(); //创建音乐播放对象 } public void addTimer(){ //添加计时器用于设置音乐播放器中的播放进度条 if (timer==null){ timer=new Timer(); //创建计时对象 TimerTask task=new TimerTask() { @Override public void run() { if (player==null)return; int duration=player.getDuration();//获取歌曲总时长 int currentPosition=player.getCurrentPosition();//获取播放进度 Message msg=MainActivity.handler.obtainMessage();//创建消息对象 Bundle bundle=new Bundle(); bundle.putInt("duration",duration); bundle.putInt("currentPosition",currentPosition); msg.setData(bundle); MainActivity.handler.sendMessage(msg); } }; timer.schedule(task,5,500);
//开始计时任务5毫秒后,第一次执行task任务,以后每500毫秒执行一次 } } class MusicControl extends Binder{ public void play(){ try{ player.reset(); //重置音乐播放器 player=MediaPlayer.create(getApplicationContext(),R.raw.music); addTimer(); }catch (Exception e){ e.printStackTrace(); } } public void pausePlay(){ player.pause(); //暂停播放 } public void continuePlay(){ player.start(); //继续播放 } public void seekTo(int progress){ player.seekTo(progress); } } @Override public void onDestroy() { super.onDestroy(); if (player==null)return; if (player.isPlaying())player.stop(); //停止播放 player.release(); //释放占用资源 player=null; //player置为空 } }
-
运行结果与分析
播放中:
暂停:
继续:
退出:
-
实验体会与收获
本实验学习了什么是服务,服务种类有哪些,怎么实现服务,了解服务的两种启动方式并采用第二种服务启动方式
Service:
是android四大组件之一,主要用于在后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。而Service是运行在主线程里的,如果直接在Service中处理一些耗时的逻辑,就会导致程序ANR,所以需要另外开启子线程来处理。
两种模式: startService()/bindService()
.Service的第一种启动方式
采用start的方式开启服务
使用Service的步骤:
1.定义一个类继承Service
2.在Manifest.xml文件中配置该Service
3.使用Context的startService(Intent)方法启动该Service
4.不再使用时,调用stopService(Intent)方法停止该服务
使用这种start方式启动的Service的生命周期如下:
onCreate() --> onStartCommand() (onStart()方法已过时) --> onDestory()
Service的第二种启动方式
采用bind的方式开启服务
使用Service的步骤:
1.定义一个类继承Service
2.在Manifest.xml文件中配置该Service
3.使用Context的bindService(Intent,ServiceConnection,int)方法启动该Service
4.不再使用时,调用unbindService(ServiceConnection)方法停止该服务
使用这种start方式启动的Service的生命周期如下:
onCreate() -->> onBind() --> onUnbind() -->> onDestory()
标签:播放器,网易,layout,Service,public,import,android,id,模仿 From: https://blog.csdn.net/2301_80950699/article/details/141714435