实验目的
- 学会使用MediaPlayer
- 学会简单的多线程编程,使用Handler更新UI
- 学会使用Service进行后台工作
- 学会使用Service与Activity进行通信
效果预览
布局
进度条的布局
如何实现让进度条占满当前时间
和全部时间
中间的部分呢?
-
如果使用
match_parent
,右边的全部时间
又显示不了 -
如果使用
wrap_content
,又不能填充满 -
如果自定义
dp
值,不同尺寸显示又会不一样
这就利用了LinearLayout
的特点,只需设置中间进度条的layout_weight = 1
就好了,它就会自动延伸到右边最远处(不占据别的控件)
圆形ImageView
这里使用了github
上的开源控件:链接
-
添加依赖
implementation 'de.hdodenhof:circleimageview:2.2.0'
-
在
xml
中使用<de.hdodenhof.circleimageview.CircleImageView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/profile_image" android:layout_width="match_parent" android:layout_height="290dp" android:layout_marginTop="30dp" android:src="@drawable/img" app:layout_constraintTop_toTopOf="parent" />
Service的使用
Service
(服务)是一种可以在后台执行长时间运行操作而没有用户界面的应用组件。
服务可由其他应用组件启动(如Activity
),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity
)
已销毁也不受影响。
如何启动Service
-
通过
startService
启动startService()
启动和stopService()
关闭服务,Service
与访问者之间基本不存在太多关联,因此Service
和访问者之间无法通讯和数据交换。 -
通过
bindService
启动用于
Service
和访问者之间需要进行方法调用或数据交换的情况
注册Service
在manifests
里面的application
里,添加
<service android:name=".MusicService" android:exported="true"/>
创建Service
右键 -> New
-> Service
-> Service
,取名为MusicService
(跟注册时一致)
在Service
里添加成员
//用来跟Activity进行绑定
public final IBinder binder = new MyBinder();
//媒体播放类
public MediaPlayer mp = new MediaPlayer();
//对Service控制的不同数字码
private final int PLAY_CODE = 1, STOP_CODE = 2, SEEK_CODE = 3, NEWMUSIC_CODE = 4, CURRENTDURATION_CODE = 5, TOTALDURATION = 6;
//重写onTransact方法,对不同的CODE做出不同的反应
public class MyBinder extends Binder {
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
//service solve
case PLAY_CODE:
play_pause();
break;
case STOP_CODE:
stop();
break;
case SEEK_CODE:
mp.seekTo(data.readInt());
break;
case NEWMUSIC_CODE:
newMusic(Uri.parse(data.readString()));
reply.writeInt(mp.getDuration());
case TOTALDURATION:
reply.writeInt(mp.getDuration());
break;
case CURRENTDURATION_CODE:
reply.writeInt(mp.getCurrentPosition());
break;
}
return super.onTransact(code, data, reply, flags);
}
}
这里的
Environment.getExternalStorageDirectory()
指的外部存储不是扩展卡中的存储,而是相对程序来讲,不是程序的InternalStorage
,而是本机的通用存储
重写onBind
方法
@Override
public IBinder onBind(Intent intent) {
try {
mp.setDataSource(Environment.getExternalStorageDirectory() + "/data/山高水长.mp3");
mp.prepare();
} catch (IOException e) {
Log.e("prepare error", "getService: " + e.toString());
}
return binder;
}
这里返回的
binder
就相当于一个Service
组件所返回的代理对象,Service
允
许客户端通过该IBinder
对象来访问Service
内部的数据,实现客户端与Service
之间的通信
编写一些简单的控制方法
public void play_pause() {
if (mp.isPlaying()) {
mp.pause();
} else {
mp.start();
}
}
public void stop() {
if (mp != null) {
mp.stop();
try {
mp.prepare();
mp.seekTo(0);
} catch (Exception e) {
Log.d("stop", "stop: " + e.toString());
}
}
}
public void newMusic(Uri uri){
try{
mp.reset();
mp.setDataSource(this, uri);
mp.prepare();
}
catch (Exception e){
Log.d("New Music", "new music: " + e.toString());
}
}
重写onDestory
方法
@Override
public void onDestroy() {
super.onDestroy();
if(mp!= null){
//release之前一定要reset,不然会报下面的错
//W/MediaPlayer(7564): mediaplayer went away