步骤
- 系列文章
- 前言
- 1. 简述程序、进程、线程的区别和联系。(10分)
- 2. 什么是UI线程??什么是工作线程?(10分)
- 3. 如何定义1个结构良好可控的工作线程(源代码说明)?(10分)
- 4. 工作线程什么时间需要与UI线程进行通信?又如何与UI线程进行通信(源代码说明)?(10分)
- 5. 手机中MediaPlayer类生成对象的2种方法分别是什么(举例说明)?有什么不同??(10分)
- 6. MediaPlayer类的OnPreparedListener、OnCompletionListener监听器有什么作用??(10分)
- 7. 使用MediaPlayer类如何才能自动播放音乐(源代码说明)?如何修改界面外观(源代码说明)?(10分)
- 8. 请简单解释SeekBar类的3个监听方法。(10分)
- 9. Service类该如何派生出(源代码说明,并解释每个方法的作用)?
- 10. 10. Service后台程序如何与前台Acvtity进行相互(双向)通信(源代码说明)?(10分)
- 总结
系列文章
提示:转到安卓学习专栏,观看更多内容!
点我直达–>安卓学习专栏
前言
大作业—Android本地音乐播放器开发知识点,稍后源代码放出。(MZH-学号19145120)
Android本地音乐播放器源码 https://blog.csdn.net/u011027547/article/details/122376235
1. 简述程序、进程、线程的区别和联系。(10分)
简述程序、进程、线程:
多线程:是指一个应用程序同时执行多个任务,一般来说一个任务就是一个线程 ,而一个应用程序有一个以上的线程我们称之为多线程。
进程:进程是一个正在执行的程序 ,比如QQ,迅雷等 一个进程的运行会向CPU申请在内存中开辟一个内存块。
进程是向CPU申请资源的,进程之间数据相互独立,一个进程至少有一个线程。
线程:线程是进程中的单一的顺序控制流程也可以叫做最小控制单元,线程是进程中执行单元,开启一个线程比开启一个进程更加节省资源。
程序、进程、线程的区别:
笼统的说区别:
1)程序只是一组指令的有序集合,它本身没有任何运行的含义,它只是一个静态的实体。
2)进程是动态的一个独立实体。
3)线程是进程的更小的组成单元,是一个动态的基本单元。一个程序至少有一个进程,一个进程至少有一个线程.。
进程和线程的区别:
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
资源分配给进程,同一进程的所有线程共享该进程的所有资源。 同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。
处理机分给线程,即真正在处理机上运行的是线程。
线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
2. 什么是UI线程??什么是工作线程?(10分)
UI线程
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。
工作线程
没有界面的称之为工作者线程。工作者线程默认是没有消息队列的,比如UI中一个按钮触发一个很耗时间的复杂计算,这个计算过程就可以通过一个工作者线程来实现。
3. 如何定义1个结构良好可控的工作线程(源代码说明)?(10分)
public Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case -100:
moveTaskToBack(true);
}
}
};
private class Work extends Thread{
@Override
public void run() {
try {
sleep(5000);
Message mm=Message.obtain();
mm.what=-100;
handler.sendMessage(mm);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
4. 工作线程什么时间需要与UI线程进行通信?又如何与UI线程进行通信(源代码说明)?(10分)
什么时间需要与UI线程进行通信:
Android多线程的Handler—实现工作线程通知Main线程修改界面。Handler是一套 Android 消息传递机制。在多线程的应用场景中,将工作线程中需更新UI的操作信息传递到UI主线程,从而实现工作线程对UI的更新处理,最终实现异步消息的处理。
只要需要GDI的调用,需要产生UI的变化的时候需要进行通信。工作者线程原本是没有消息队列,但是你可以强制加一个,一般只要你的线程中出现了GDI的调用就会出现一个消息队列,线程中如果调用了GetMessage(),就可以强制加入了一个消息循环,系统就会给该线程加一个消息队列,同样用PeekMessage()也可以强制系统加入一个消息队列.工作者线程的消息传递是通过PostThreadMessage函数发送的。即:工作线程函数里面如果调用了有关消息的函数,操作系统自动为工作线成创建消息队列。
与UI线程进行通信的源代码:
public void run() {
try {
sleep(5000);
Message mm=Message.obtain();
mm.what=-100;
handler.sendMessage(mm);
}catch (InterruptedException e){
e.printStackTrace();
}
}
5. 手机中MediaPlayer类生成对象的2种方法分别是什么(举例说明)?有什么不同??(10分)
生成1个MediaPlayer类的对象
创建MediaPlaer对象有两种方式:
a、直接new出来
MediaPlayer mp = new MediaPlayer( );
b、使用create方式
MediaPlayer mp = MediaPlayer. create( this, R.raw.test );
区别在于第二种方法中R.raw.test:资源中的音乐文件,这时就不用调用setDataSource了
6. MediaPlayer类的OnPreparedListener、OnCompletionListener监听器有什么作用??(10分)
OnPreparedListener:
只有在准备结束后才能调用getDuration()方法,如果在之前调用getDuration()会出现IllegalStateException异常。
player.prepareAsync(); // 当player已准备结束的时候
player.setOnPreparedListener(new OnPreparedListener() {
public void onPrepared(MediaPlayer mp) {
seekbar.setMax(player.getDuration());
player.start();
}
});
OnCompletionListener:
处理播放结束后的操作。由于player占有的系统资源比较大,因此在播放结束后,就应该调用该方法,把player占有的资源给释放掉。示例:
player.setOnCompletionListener(new OnCompletionListener() {
public void onCompletion(MediaPlayer mp) {
player.release();//播放结束后,就自动释放player的资源。
seekbar.setProgress(0);//将seekbar还原。
}
});
7. 使用MediaPlayer类如何才能自动播放音乐(源代码说明)?如何修改界面外观(源代码说明)?(10分)
使用MediaPlayer类自动播放音乐:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
action=intent.getStringExtra("action");
index=intent.getIntExtra("index",0);
progress=intent.getIntExtra("progress",0);
Ok=intent.getBooleanExtra("ok",true);
switch (action){
case "play"://-------启动
int tt=0;
if (mp.isPlaying()==false){
mp.start();
work.ok=true;
tt=1;
}else {
mp.pause();
work.ok=false;
tt=-1;
}
Message mm=Message.obtain();
mm.what=200;
mm.arg1=tt;
MainActivity.hh.sendMessage(mm);
break;
case "click":
Goto();
break;
case "prev":
Goto();
break;
case "next":
Goto();
break;
case "change":
mp.seekTo(progress);
work.ok=Ok;
break;
}
return super.onStartCommand(intent, flags, startId);
}
修改界面外观:
public static Handler hh=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 100:
SendData sendData=(SendData) msg.obj;
progress=sendData.progress;
max=sendData.max;
index=sendData.index;
sb.setMax(max);
sb.setProgress(progress);
tv_time.setText(MP3Utils.timeParse(progress)+"/"+MP3Utils.timeParse(max));
tv_title.setText(list.get(index).getTitle());
break;
case 200:
int tt=msg.arg1;
//设置按钮图片
if (tt==1){
img_play.setImageResource(R.drawable.stop);//点击后设置为stop图片,此时音乐不播放
}
if (tt==-1){
img_play.setImageResource(R.drawable.play);
}
break;
case 300:
max=0;
progress=0;
img_play.setImageResource(R.drawable.stop);//点击后设置为stop图片,此时音乐不播放
sb.setProgress(0);
tv_time.setText("00:00/00:00");//设置时间清零
tv_title.setText("等待加载歌曲信息");//设置歌曲标题清零
break;
}
}
};
public Handler handler=new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case -100:
moveTaskToBack(true);
}
}
};
8. 请简单解释SeekBar类的3个监听方法。(10分)
3个监听方法:
onProgressChanged:进度发生改变时会触发
onStartTrackingTouch:按住SeekBar时会触发
onStopTrackingTouch:放开SeekBar时触发
示例代码:
public class MainActivity extends AppCompatActivity {
private SeekBar sb_normal;
private TextView txt_cur;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = MainActivity.this;
bindViews();
}
private void bindViews() {
sb_normal = (SeekBar) findViewById(R.id.sb_normal);
txt_cur = (TextView) findViewById(R.id.txt_cur);
sb_normal.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
txt_cur.setText("当前进度值:" + progress + " / 100 ");
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
Toast.makeText(mContext, "触碰SeekBar", Toast.LENGTH_SHORT).show();
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Toast.makeText(mContext, "放开SeekBar", Toast.LENGTH_SHORT).show();
}
});
}
}
9. Service类该如何派生出(源代码说明,并解释每个方法的作用)?
Service启动方式:调用Context.startService()
Service停止方式:调用Context.stopService()
或Service.stopSefl()
• 服务管理(startService()启动Service )
public class MyService extends Service{
@Override
public IBinder onBind(Intent intent) { return null; }
@Override
public void onCreate() { super.onCreate(); }
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() { super.onDestroy(); }
}
• 服务管理(注册服务 )
• 服务管理(startService()启动和停止 )
10. 10. Service后台程序如何与前台Acvtity进行相互(双向)通信(源代码说明)?(10分)
10.1绑定服务的方式,实现ServiceConnection接口
bindService() - unbindService()
10.1.1进行数据传递
Service中声明一个Binder类,类中声明用来传递数据的方法。比如向Activity返回Service实例/接受或返回数据。
Activity实现ServiceConnection接口,Activity绑定Service调用bindService()方法。此时onServiceConnected()方法就会被调用。此方法中利用获取的Binder实例,可以调用Service中各个用来传递数据的方法。
这就实现了Activity向Service传递数据,Service接收数据进行数据操作后,再返回给Activity。
10.1.2.Activity监听Service数据变化
Service自定义Callback接口,声明监听数据的抽象函数onDataChange(String data),参数data用来向外部暴露data数据,并设置callback实例的setter方法。再onCreat()方法中,Service利用接收来的data数据,进行耗时操作后(例如开启线程进行data的循环递增),用callback.onDataChange(data + “”)暴露data数据。
Activity在onServiceConnected()方法中,利用获取的Service实例调用setCallback()方法,同时实例化Callback接口,进行接口回调实现onDataChange()函数,此时就监听到了Service中data数据的状态,并利用Handler进行更新UI操作。
@Override
public void onCreate() {
super.onCreate();
try {
mp=new MediaPlayer();
String ss= MainActivity.list.get(index).getUrl();
Uri uri=Uri.parse(ss);
mp.setDataSource(this,uri);
mp.setOnPreparedListener(this);
mp.setOnCompletionListener(this);
mp.setOnErrorListener(this);
mp.prepareAsync();
work=new Work();
work.start();
}catch (IOException e){
e.printStackTrace();
}
}
//--------------------------------------------
private void Goto(){
work.ok=false;
mp.stop();
mp.reset();
if (index>=MainActivity.list.size()){
index=0;
}
if (index<0){
index=MainActivity.list.size()-1;
}
try {
String ss=MainActivity.list.get(index).getUrl();
Uri uri=Uri.parse(ss);
mp.setDataSource(this,uri);
mp.setOnPreparedListener(this);
mp.prepareAsync();
mp.start();
work.ok=true;
}catch (IOException e){
e.printStackTrace();
}
}
10.2利用广播方式
startService() - stopService()
Activity:动态注册广播 。将data存储到intent中,调用startService(intent)开启服务。
Service:重写onStartCommand()方法,利用intent.getXXExtra()获取Activity传来的数据。对数据进行操作后,将data存到intent中并发送广播。
Activity:接收广播,重写onReceive()函数,开启主线程并从intent中取出data数据,进行更新UI操作。
利用广播和intent实现了数据通信和监听服务数据状态。
MyService.java
public class MyService3 extends Service {
private boolean connecting = false;
public static final String COUNTER = "data";
public static final String ACTION_NAME = "com.example.myinterview.service3.COUNTER_ACTION";
private int data;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//从Activity获取data
data = intent.getIntExtra(COUNTER, 0);
final Intent mIntent = new Intent();
mIntent.setAction(ACTION_NAME);
connecting = true;
//开启一个线程,对数据进行处理
new Thread(new Runnable() {
@Override
public void run() {
try {
while (connecting) {
//耗时操作:数据处理并保存,向Activity发送广播
mIntent.putExtra(COUNTER, data);
sendBroadcast(mIntent);
data++;
Thread.sleep(300);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
connecting = false;
}
}
ServiceActivity3.java
public class ServiceActivity3 extends AppCompatActivity implements View.OnClickListener {
private int TransforData;
private TextView textView;
private Intent mIntent;
private MyReceiver myReceiver;
private boolean bind = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service3);
TransforData = 0;
textView = (TextView) findViewById(R.id.textView);
Button startBtn = (Button) findViewById(R.id.mstart);
Button pauseBtn = (Button) findViewById(R.id.pause);
Button clearBt = (Button) findViewById(R.id.clear);
startBtn.setOnClickListener(this);
pauseBtn.setOnClickListener(this);
clearBt.setOnClickListener(this);
mIntent = new Intent(this, MyService3.class);
myReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyService3.ACTION_NAME);
//注册广播
registerReceiver(myReceiver, intentFilter);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.mstart:
if (!bind) {
bind = true;
//向Service传递data
mIntent.putExtra(MyService3.COUNTER, TransforData);
startService(mIntent);
Toast.makeText(this, "Start!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.pause:
//停止服务
stopService(mIntent);
bind = false;
break;
case R.id.clear:
if (!bind) {
TransforData = 0;
textView.setText("0");
Toast.makeText(this, "Pause!", Toast.LENGTH_SHORT).show();
}
break;
default:
break;
}
}
class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, final Intent intent) {
runOnUiThread(new Runnable() {
@Override
public void run() {
//获取从Service中传来的data
TransforData = intent.getIntExtra(MyService3.COUNTER, 0);
//更新UI
textView.setText(String.valueOf(TransforData));
}
});
}
}
@Override
protected void onDestroy() {
stopService(mIntent);
unregisterReceiver(myReceiver);
super.onDestroy();
}
}
总结
大家喜欢的话,给个👍,点个关注!继续跟大家分享敲代码过程中遇到的问题!