上一篇请参见
(5条消息) 移动开发技术【Android】:【part 2】——RecyclerView实现列表_With_Zero的博客-CSDN博客https://blog.csdn.net/m0_61067829/article/details/123850913
在上一篇中我们说到如何使用RecyclerView实现列表功能,自此我们熟悉了Android中的Activity组件,那么接下来我们就要开始学习如何来写一个服务。本篇中主要是来写一个音乐服务,同时最后一个广播式启动音乐服务,则是通过系统模拟接收到Message后,跳转到我们的MainActivity中并播放音乐。
目录
一、进度说明(此项说明本功能的实现使用文件及前期准备)
1、做音乐服务的页面
二、非绑定/绑定式启动音乐
1.非绑定式(UnbindMusic)
2.绑定式(BindMusic)
3.BindMusic和UnbindMusic比较
三、系统广播式启动音乐服务
总结
一、进度说明(此项说明本功能的实现使用文件及前期准备)
(功能实现说明从第二步开始)
【首先】先看一下我们的目录结构
在此目录中,我们是根据之前一直所积累所做的,但是在我们说明的时候,会依照怎么做去说明,但是希望可以清清楚我们主要用到了那些文件。
1、做音乐服务的页面【fragment_friend】UI界面
可以看到,此界面有UnbindMusic和BindMusic两个模块:
UnbindMusic :非绑定启动音乐服务
BindMusic:绑定式启动音乐服务
【主要代码】
view = inflater.inflate(R.layout.fragment_fridend, container, false);
tab05 = view.findViewById(R.id.friendtab05);
tab06 = view.findViewById(R.id.friendtab06);
tab05.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MainActivity main = (MainActivity) getActivity();
main.show(5);
}
});
tab06.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MainActivity main = (MainActivity) getActivity();
main.show(6);
}
});
return view;
这里的显示show()主要使用了MainActivity之前写的方法。详情可以参看爬取gitee源代码见方法实现。
【音乐启动页面】
明白我们的逻辑后,下面我们进入具体实现 *** 作!
二、非绑定/绑定式启动音乐 1.非绑定式(UnbindMusic)
private View view;
private LinearLayout unbindtab01,unbindtab02;
Service1.Mybinder binder;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.d("friend","uuuuuuuuuuuuu");
view = inflater.inflate(R.layout.fragment_unbind_music,container,false);
unbindtab01 = view.findViewById(R.id.unbindtab01);
unbindtab02 = view.findViewById(R.id.unbindtab02);
Intent intentservices = new Intent(getActivity(),Service1.class);
unbindtab01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getActivity().startService(intentservices);
}
});
unbindtab02.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getActivity().stopService(intentservices);
}
});
return view;
// return inflater.inflate(R.layout.fragment_unbind_music, container, false);
}
这里我们看到,我们通过id找到两个Layout(unbindtab01、unbindtab02)后进行点击动作监听。我们通过Intent,使得跳转到Service1进行启动服务。
//目的进入Service1服务 Intent intentservices = new Intent(getActivity(),Service1.class);然后我们在unbindtab01(启动)中startService(intentservices)
在unbindtab02(停止)中stopService(intentservices)
【Service1】
public class Service1 extends Service {
MediaPlayer mediaPlayer;
public class Mybinder extends Binder{
public void myplay(){
mediaPlayer=MediaPlayer.create(getApplicationContext(),R.raw.lovevisemusic);
mediaPlayer.start();
}
}
//绑定非绑定都会启动此方法
@Override
public void onCreate() {
super.onCreate();
Log.d("service1","service1 is oncreate");
mediaPlayer=MediaPlayer.create(getApplicationContext(),R.raw.yuxitanmusic);
}
//邦定式服务不启动此方法
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("service1","service1 is onStartCommand");
mediaPlayer=MediaPlayer.create(this,R.raw.yuxitanmusic);
mediaPlayer.start();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d("service1","service1 is onDestroy");
mediaPlayer.stop();
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new Mybinder();
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public boolean stopService(Intent name) {
Log.d("service1","service1 is stop");
return super.stopService(name);
}
}
其中我们通过这一句绑定音乐文件。
mediaPlayer=MediaPlayer.create(getApplicationContext(),R.raw.yuxitanmusic);(注意:将你的音乐文件放在res/raw中,这里raw文件夹自己新建一下。)
另外,service还要在AndroidManifest.xml中标记一下。
到此,非绑定的服务书写结束
2.绑定式(BindMusic)
private View view;
private LinearLayout tab01,tab02;
Service1.Mybinder binder;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.d("friend","bbbbbbbbbbbbbb");
view = inflater.inflate(R.layout.fragment_bind_music,container,false);
Intent intentservices = new Intent(getActivity(),Service1.class);
ServiceConnection connection= new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
binder = (Service1.Mybinder)iBinder;
binder.myplay();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
binder=null;
}
};
tab01 = view.findViewById(R.id.bindtab01);
tab02 = view.findViewById(R.id.bindtab02);
tab01.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getActivity().bindService(intentservices,connection, Context.BIND_AUTO_CREATE);//绑定服务
}
});
tab02.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
getActivity().unbindService(connection);
}
});
return view;
// return inflater.inflate(R.layout.fragment_bind_music, container, false);
}
在这里我们做了什么呢?注意,我们这里加了一个属性ServiceConnection connection,来建立与Service1的连接,连接谁呢?这里我们注意是不是在Service1中有个Myplay方法(见下图)?这个方法是我们自定义的,用来启动音乐播放服务,其中两行分别就是:绑定音乐、开启服务。
除此之外,和非绑定式一样,都是利用了Service1服务,但是注意,我们利用binder绑定服务,在解除绑定时我们则是直接让binder=null。至此,非绑定式服务写完了。
3.BindMusic和UnbindMusic比较
我们如何比较两者差异呢?那我们将通过生命周期和效果来看。
(1)生命周期
【UnbindMusic】
Logcat日志记录:
2022-04-19 19:22:42.514 9475-9475/? D/service1: bbbbbbbbbbbbbbb
2022-04-19 19:22:42.531 9475-9475/? D/service1: uuuuuuuuuuuuuuuuuuu
2022-04-19 19:24:20.043 9475-9475/com.example.mywork D/service1: onclick to start unbind......
2022-04-19 19:24:32.162 9475-9475/com.example.mywork D/service1: unbind start music....
2022-04-19 19:24:32.169 9475-9475/com.example.mywork D/service1: service1 is oncreate
2022-04-19 19:24:32.189 9475-9475/com.example.mywork D/service1: service1 is onStartCommand
2022-04-19 19:24:53.138 9475-9475/com.example.mywork D/service1: unbind stop music....
2022-04-19 19:24:53.140 9475-9475/com.example.mywork D/service1: service1 is onDestroy
前两项是BindMusicFragment和UnbindMusicFragment启动的提示,我们打了Log都设为service字段记录。
UnbindMusic:Oncreate ->OnSraerCommand -> OnDestory (生命周期)
【bindMusic】
Logcat日志记录:
2022-04-19 19:31:20.761 9475-9475/com.example.mywork D/service1: onclic to start bind.....
2022-04-19 19:31:25.810 9475-9475/com.example.mywork D/service1: bind start music....
2022-04-19 19:31:25.813 9475-9475/com.example.mywork D/service1: service1 is oncreate
2022-04-19 19:31:25.831 9475-9475/com.example.mywork D/service1: Mybinder is working....
2022-04-19 19:31:33.274 9475-9475/com.example.mywork D/service1: bind stop music....
2022-04-19 19:31:33.276 9475-9475/com.example.mywork D/service1: service1 is onDestroy
这里就和非绑定的服务产生差别了,生命周期有了变化。
BindMusic: Oncreate -> Mybinder -> OnDestory
(2)效果不同
非绑定式的服务,在启动后,返回桌面后,并不会停止,而是在后台继续播放音乐,即在离开当前Activity后,不会影响服务。
绑定式服务,在启动后,返回桌面后,服务就会停止,即在离开当前Activity后服务就结束了。当然,假设你进入第二个Activity,第一个Activity并没有Destory的话,服务也自然不会停止。
这里我们说明一下,因为我们只写了一个Activity,其他Fragment都是依赖于这个Activity的,因此我们选择以返回界面来检测差异。
三、系统广播式启动音乐服务
首先说明,这部分功能实现效果:在我们利用编译器给自己发信息后,我们会启动MainActivity,同时会启动我们绑定的服务,即播放音乐。
【MyReceiver】广播接收
package com.example.mywork;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class MyReceiver1 extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Log.d("onReceiver","get receiver");
Intent receiverintent1 = new Intent(context,Service1.class);
context.startService(receiverintent1);
Intent receiverintent2 = new Intent(context,MainActivity.class);
receiverintent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(receiverintent2);
}
}
在我们监听接收到系统消息后,我们会启动MainActivity和Service1。
但是在这里我们要注意,接收功能写完后,我们需要去注册驱动并且授权。这里就要说到,授权分为两种:静态授权、动态授权。这里我们进行动态授权,静态授权可能会出现问题不能启动。
【AndroidManifest.xml】
我们需要把上面几行加进去,注意receiver对应你自己的name,其他不用变,你创建Brocastreceiver应该会自动生成,注意修改一下就好。
【动态授权】
//动态授权
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Log.d("aaa","??????");
switch (requestCode){
case 1:
if(grantResults[0]!=PackageManager.PERMISSION_GRANTED){
Toast.makeText(this, "未授权,无法实现预定的功能!", Toast.LENGTH_SHORT).show();
finish();
} else {
Toast.makeText(this, "请发一条短信验证!", Toast.LENGTH_SHORT).show();
}
}
}
//授权
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{"android.permission.RECEIVE_SMS"}, 1);
}
如图这里我是这样写的,把这两段分别找到为止加入你要写的地方,因为我们要跳转到MainActivity,所以我们在MainActivity中编辑。
这样我们就把这个服务写完了,然后我们试一下效果:
———————————————————————————————————————————
首先我们假若不注册:则未授权无法进入
我们恢复注册后:会提示是否允许接收信息,选择allow
然后它会说请发一条信息(只有第一次会d出)
点击这里的三个小圆点,选择Phone,就可以模拟发送消息给模拟器
那么现在你应该收到消息了
最后服务就启动了,音乐响起!注意我们启动的是Service1,所以要停止可以直接使用UnbindMusic中的停止按钮,不要用BindMusic,可能会有bug。
总结
至此,本次的所有实验都结束了,主要就是初步学习一下如何写一个服务,观察服务的生命周期等重要属性。当然本次过程中也遇到bug:
1、MyReceiver拷贝过来无法接收消息:这里可能和编译器环境因素有关系,自己重新创建文件复制代码就可以解决了,一定要关注编译器自身是否存在缓存、无法及时更新等问题,这种问题往往最难发现。
2、在最后一项广播启动服务中,我们先启动项目,然后后台关闭,再发消息,就会启动服务,但是这里软件并未启动。
最后附上本篇gitee:Android_Studion_Mobile: 针对移动开发的学习代码记录
With_Zero 2022.04.19
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)