CH8

编程入门 行业动态 更新时间:2024-10-20 07:55:29

CH8

CH8

文章目录

  • 目标
  • 一、服务概述
    • 目标
    • 服务的简介
  • 二、服务的创建
    • 目标
    • 注册服务
  • 三、服务的生命周期
    • 目标
    • 服务的启动
  • 四、服务的启动方式
    • 目标
    • 4.1 调用startService()方法启动服务
    • 4.2 调用bindService()方法启动服务
  • 五、服务的通信
    • 目标
    • 5.1 本地服务通信和远程服务通信
    • 5.2 实战演练—仿网易音乐播放器

目标

  • 了解服务的概述,能够说出什么是服务
  • 掌握服务的创建方式,能够独立创建一个服务
  • 熟悉服务的生命周期,能够阐述服务生命周期中的方法
  • 掌握服务的两种启动方式,能够实现服务的启动与关闭功能
  • 掌握服务的通信,能够完成仿网易音乐播放器案例

​ 通常在程序中下载一些大文件时,程序突然退出,此时下载文件的任务会中断。为了避免出现下载任务中断的问题,我们可以使用Android系统提供的服务来下载大文件。服务是一个长期运行在后台的用户组件,没有用户界面。它除了可以在后台下载文件之外,还可以在后台执行很多任务,比如处理网络事务、播放音乐或者与一个内容提供者交互,本章将针对服务进行详细讲解。

一、服务概述

目标

  • 了解服务的概述,能够说出什么是服务

服务的简介

Service(服务)是Android四大组件之一,能够在后台长时间执行操作并且不提供用户界面的应用程序组件。Service可以与其他组件进行交互,一般是由Activity启动,但是并不依赖于Activity。当Activity的生命周期结束时,Service仍然会继续运行,直到自己的生命周期结束为止。

Service还具有较长的时间运行特性,它的应用场景主要有两个,分别是后台运行跨进程访问,具体如下:

  • 后台运行
    • Service可以在后台长时间进行操作而不用提供界面信息,只有当系统必须要回收内存资源时,才会被销毁,否则Service会一直在后台运行。
  • 跨进程访问
    • 当Service被其他应用组件启动时,即使用户切换到其他应用,服务仍将在后台继续运行。
    • Service可以在符合上述两种场景的很多应用中使用,比如播放多媒体时,用户启动了其他Activity,此时程序在后台继续播放,或者程序需要在后台记录地理位置信息的改变等。

二、服务的创建

目标

  • 掌握服务的创建方式,能够独立创建一个服务

​ 服务的创建是选中程序包名,接着右击选择【New】->【Service】->【Service】选项,在弹出窗口中输入服务的名称即可完成创建。

​ 服务创建完成后,Android Studio会自动在AndroidManifest.xml文件中对服务进行注册。

若采用创建Java类继承Service类的方式创建服务,则需要手动在清单文件中对服务进行注册。

注册服务

 <?xml version="1.0" encoding="utf-8"?>
<!--
name		服务的路径
enabled		为ture,表示服务可由系统实例化
exported	为ture,表示该服务能够被其他应用程序组件调用
--><manifest xmlns:android=""…><application …… ><serviceandroid:name=".MyService"android:enabled="true"android:exported="true" ></service></application></manifest>

三、服务的生命周期

目标

  • 熟悉服务的生命周期,能够阐述服务生命周期中的方法

服务的启动

  • 通过startService()方法启动

    • 当通过startService()方法启动服务时,需要自身调用stopSelf()方法或者其他组件调用stopService()方法时服务才能停止。
  • 通过bindService()方法启动

    • 当通过bindService()方法启动服务时,需要调用onUnbind()方法解除绑定之后服务才会被销毁。
  • 使用不同的方法启动服务,其生命周期也是不同的。

使用不同方式启动服务的生命周期,具体如下图所示。

四、服务的启动方式

目标

  • 掌握startService()方法的使用方式,能够实现启动服务的功能
  • 掌握bindService()方法的使用方式,能够实现启动服务的功能

4.1 调用startService()方法启动服务

​ 在程序中通过startService()方法启动的服务,会长期在后台运行,并且启动服务的组件与服务之间没有关联,即使启动服务的组件被销毁,服务依旧会运行。

​ 接下来将通过一个开灯与关灯的案例演示如何通过startService()方法与stopService()方法来启动和关闭服务,本案例的界面效果如下图所示。

放置界面控件 res\layout\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"android:background="#efede0"android:gravity="center"android:orientation="vertical"><Buttonandroid:id="@+id/btn_switch"android:layout_width="100dp"android:layout_height="30dp"android:background="@drawable/btn_close"android:text="开灯"android:textSize="14sp" /><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="50dp"><ImageViewandroid:id="@+id/iv_open"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/img_open" /><ImageViewandroid:id="@+id/iv_close"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/img_close" /></RelativeLayout>
</LinearLayout>

创建MyService服务 switches\MyService.java

package cn.itcast.switches;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;//服务创建完成后,AS会自动在AndroidManifest.xml文件中注册服务
public class MyService extends Service {@Overridepublic void onCreate() {super.onCreate();Log.i("MyService", "创建服务,执行onCreate()方法");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i("MyService", "开启服务,执行onStartCommand()方法");return super.onStartCommand(intent, flags, startId);}//    onBind()是Service子类必须实现的方法,返回一个IBinder对象,应用程序可通过该对象与Service组件通信@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onDestroy() {super.onDestroy();Log.i("MyService", "关闭服务,执行onDestroy()方法");}
}

实现开灯与关灯效果 switches\MainActivity.java

package cn.itcast.switches;import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;public class MainActivity extends AppCompatActivity {private Button btn_switch;private ImageView iv_open, iv_close;//表示存放开灯与关灯状态的变量,为false时表示关灯状态,为true时表示开灯状态private boolean isOpen = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}private void init() {btn_switch = findViewById(R.id.btn_switch);iv_open = findViewById(R.id.iv_open);iv_close = findViewById(R.id.iv_close);btn_switch.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (isOpen) {//说明此时界面上的图显示的是开灯状态btn_switch.setText("开灯");btn_switch.setBackgroundResource(R.drawable.btn_close);iv_open.setVisibility(View.GONE);    //隐藏开灯按钮图片iv_close.setVisibility(View.VISIBLE);//显示关灯按钮图片isOpen=false;//设置为关灯状态//关闭服务Intent intent = new Intent(MainActivity.this, MyService.class);stopService(intent);} else { //此时界面上的图显示的是关灯状态btn_switch.setText("关灯");btn_switch.setBackgroundResource(R.drawable.btn_open);iv_open.setVisibility(View.VISIBLE);//显示开灯按钮图片iv_close.setVisibility(View.GONE);  //隐藏关灯按钮图片isOpen=true; //设置为开灯状态//开启服务Intent intent = new Intent(MainActivity.this, MyService.class);startService(intent);}}});}
}

4.2 调用bindService()方法启动服务

​ 通过bindService()方法启动服务时,服务会与组件绑定。当调用onUnbind()方法时,这个服务就会被销毁。

​ 接下来通过一个绑定服务的案例来演示如何通过bindService()方法与unbindService()方法来绑定与解绑服务,本案例的界面效果如下图所示。

放置界面控件 res\layout\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/bg"><Buttonandroid:id="@+id/btn_unbind"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_marginBottom="80dp"android:layout_marginRight="85dp"android:background="#F5F5DC"android:text="解绑服务"android:textSize="16sp" /><Buttonandroi:id="@+id/btn_call"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/btn_unbind"android:layout_alignLeft="@id/btn_unbind"android:layout_marginBottom="55dp"android:background="#F5F5DC"android:padding="10dp"android:text="调用服务中的方法"android:textSize="16sp" /><Buttonandroid:id="@+id/btn_bind"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_above="@id/btn_call"android:layout_alignLeft="@id/btn_call"android:layout_marginBottom="50dp"android:background="#F5F5DC"android:text="绑定服务"android:textSize="16sp" />
</RelativeLayout>

创建MyService服务 bindservice\MyService.java

package cn.itcast.bindservice;import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;public class MyService extends Service {//创建服务的代理,调用服务中的方法class MyBinder extends Binder {public void callMethodInService() {methodInService();}}public void methodInService() {Log.i("MyService", "执行服务中的methodInService()方法");}@Overridepublic void onCreate() {Log.i("MyService", "创建服务,执行onCreate()方法");super.onCreate();}@Overridepublic IBinder onBind(Intent intent) {Log.i("MyService", "绑定服务,执行onBind()方法");return new MyBinder();}@Overridepublic boolean onUnbind(Intent intent) {Log.i("MyService", "解绑服务,执行onUnbind()方法");return super.onUnbind(intent);}
}

实现按钮的点击事件 bindservice\MainActivity.java

package cn.itcast.bindservice;import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;public class MainActivity extends AppCompatActivity implements View.OnClickListener {private MyService.MyBinder myBinder;private MyConn myconn;private Button btn_bind, btn_call, btn_unbind;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();}private void init() {btn_bind = findViewById(R.id.btn_bind);btn_call = findViewById(R.id.btn_call);btn_unbind = findViewById(R.id.btn_unbind);//设置3个按钮的点击监听事件btn_bind.setOnClickListener(this);btn_call.setOnClickListener(this);btn_unbind.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.btn_bind:             //“绑定服务”按钮点击事件if (myconn == null) {myconn = new MyConn(); //创建连接服务的对象}Intent intent = new Intent(MainActivity.this, MyService.class);bindService(intent, myconn, BIND_AUTO_CREATE); //绑定服务break;case R.id.btn_call:    //“调用服务中的方法”按钮点击事件myBinder.callMethodInService(); //调用服务中的方法break;case R.id.btn_unbind: //“解绑服务”按钮点击事件if (myconn != null) {unbindService(myconn); //解绑服务myconn = null;}break;}}/*** 创建MyConn类,用于实现连接服务*/private class MyConn implements ServiceConnection {/*** 当成功绑定服务时调用的方法,该方法获取MyService中的IBinder对象*/@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {myBinder = (MyService.MyBinder) iBinder;Log.i("MainActivity", "服务成功绑定, 内存地址为:" + myBinder.toString());}/*** 当服务失去连接时调用的方法*/@Overridepublic void onServiceDisconnected(ComponentName componentName) {}}
}

五、服务的通信

目标

  • 掌握服务的通信,能够完成仿网易音乐播放器案例

5.1 本地服务通信和远程服务通信

5.2 实战演练—仿网易音乐播放器

​ 接下来通过一个仿网易音乐播放器的案例来演示如何使用服务进行本地通信,本案例的界面效果如下图所示。

放置界面控件 res\layout\activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android=""android:layout_width="match_parent"android:layout_height="match_parent"android:background="@drawable/music_bg"android:gravity="center"android:orientation="vertical"><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="160dp"><RelativeLayoutandroid:id="@+id/rl_title"android:layout_width="300dp"android:layout_height="70dp"android:layout_centerHorizontal="true"android:background="@drawable/title_bg"android:gravity="center_horizontal"android:paddingLeft="80dp"><TextViewandroid:id="@+id/tv_music_title"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="8dp"android:text="体面"android:textSize="12sp"android:textStyle="bold"android:textColor="@android:color/black"/><TextViewandroid:layout_marginTop="4dp"android:id="@+id/tv_type"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/tv_music_title"android:layout_alignLeft="@id/tv_music_title"android:text="流行音乐"android:textSize="10sp" /><SeekBarandroid:id="@+id/sb"android:layout_width="150dp"android:layout_height="wrap_content"android:layout_below="@id/rl_time"android:layout_alignParentBottom="true"android:thumb="@null" /><RelativeLayoutandroid:layout_marginTop="4dp"android:id="@+id/rl_time"android:layout_width="150dp"android:layout_height="wrap_content"android:layout_below="@id/tv_type"><TextViewandroid:id="@+id/tv_progress"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="00:00"android:textSize="10sp" /><TextViewandroid:id="@+id/tv_total"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:text="00:00"android:textSize="10sp" /></RelativeLayout></RelativeLayout><LinearLayoutandroid: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"><Buttonandroid:id="@+id/btn_play"android:layout_width="0dp"android:layout_height="25dp"android:layout_margin="4dp"android:layout_weight="1"android:background="@drawable/btn_bg_selector"android:text="播放"android:textSize="10sp" /><Buttonandroid:id="@+id/btn_pause"android:layout_width="0dp"android:layout_height="25dp"android:layout_margin="4dp"android:layout_weight="1"android:background="@drawable/btn_bg_selector"android:text="暂停"android:textSize="10sp" /><Buttonandroid:id="@+id/btn_continue_play"android:layout_width="0dp"android:layout_height="25dp"android:layout_margin="4dp"android:layout_weight="1"android:background="@drawable/btn_bg_selector"android:text="继续"android:textSize="10sp" /><Buttonandroid:id="@+id/btn_exit"android:layout_width="0dp"android:layout_height="25dp"android:layout_margin="4dp"android:layout_weight="1"android:background="@drawable/btn_bg_selector"android:text="退出"android:textSize="10sp" /></LinearLayout><ImageViewandroid:id="@+id/iv_music"android:layout_width="100dp"android:layout_height="100dp"android:layout_centerVertical="true"android:layout_marginLeft="35dp"android:layout_marginBottom="50dp"android:src="@drawable/img_music" /></RelativeLayout>
</LinearLayout>

创建背景选择器 res\drawable\btn_bg_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android=""><!--state_pressed       表示按钮的点击事件,true为按钮被按下,false为按钮弹起shape               表示形状,rectangle为矩形corners             表示定义矩形的四个角为圆角radius              用于设置圆角半径solid               指定矩形内部的填充颜色--><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>

创建MusicService服务 musicplayer\MusicService.java

package cn.itcast.musicplayer;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 java.util.Timer;
import java.util.TimerTask;public class MusicService extends Service {private MediaPlayer player;private Timer timer;public MusicService() {}//    将MusicControl对象返回给MainActivity,从而实现两者之间的通信@Overridepublic IBinder onBind(Intent intent) {return new MusicControl();}@Overridepublic void onCreate() {super.onCreate();player = new MediaPlayer();//创建音乐播放器对象}public void addTimer() {        //添加计时器用于设置音乐播放器中的播放进度条if (timer == null) {timer = new Timer();     //创建计时器对象
//            创建一个TimerTask任务,该任务表示在指定时间内执行的任务taskTimerTask task = new TimerTask() {@Overridepublic void run() {if (player == null) return;int duration = player.getDuration();                 //获取歌曲总时长int currentPosition = player.getCurrentPosition();//获取播放进度Message msg = MainActivity.handler.obtainMessage();//创建消息对象//将音乐的总时长duration和播放进度currentPosition封装至消息对象msg中Bundle bundle = new Bundle();bundle.putInt("duration", duration);bundle.putInt("currentPosition", currentPosition);msg.setData(bundle);//将消息发送到主线程的消息队列MainActivity.handler.sendMessage(msg);}};//开始计时任务后的5毫秒,第一次执行task任务,以后每500毫秒执行一次timer.schedule(task, 5, 500);}
}class MusicControl extends Binder {public void play() {try {player.reset();//重置音乐播放器//加载多媒体文件player = MediaPlayer.create(getApplicationContext(), R.raw.music);player.start();//播放音乐addTimer();     //添加计时器} catch (Exception e) {e.printStackTrace();}}public void pausePlay() {player.pause();           //暂停播放音乐}public void continuePlay() {player.start();           //继续播放音乐}public void seekTo(int progress) {player.seekTo(progress);//设置音乐的播放位置}}@Overridepublic void onDestroy() {super.onDestroy();if (player == null) return;if (player.isPlaying()) player.stop();//停止播放音乐player.release();                         //释放占用的资源player = null;                            //将player置为空}
}

编写界面交互代码 musicplayer\MainActivity.java

package cn.itcast.musicplayer;import android.animation.ObjectAnimator;
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.support.v7.app.AppCompatActivity;
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;//记录服务是否被解绑@Overrideprotected 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);  //绑定服务MusicService//为SeekBar滑动条添加事件监听sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, booleanfromUser) {                          //滑动条进度改变时,会调用此方法if (progress == seekBar.getMax()) { //当滑动条滑到末端时,结束动画animator.pause();                   //停止图片转圈的动画效果}}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {//滑动条开始滑动时调用}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) { //滑动条停止滑动时调用//根据拖动的进度改变音乐播放进度int progress = seekBar.getProgress();   //获取滑动条seekBar的进度musicControl.seekTo(progress);         //改变播放进度}});ImageView iv_music = findViewById(R.id.iv_music);
//        设置该图片控件的动画效果为顺时针360°旋转
//        iv_music      图片控件
//        rotation      设置该动画为旋转动画
//        0f            动画的起始旋转弧度
//        360.0f        动画的结束旋转弧度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() {//创建消息处理器对象//在主线程中处理从子线程发送过来的消息@Overridepublic void handleMessage(Message msg) {Bundle bundle = msg.getData(); //获取从子线程发送过来的音乐播放进度int duration = bundle.getInt("duration");                  //歌曲的总时长int currentPostition = bundle.getInt("currentPosition");//歌曲当前进度sb.setMax(duration);                //设置SeekBar的最大值为歌曲总时长sb.setProgress(currentPostition);//设置SeekBar当前的进度位置//歌曲的总时长int minute = duration / 1000 / 60;int second = duration / 1000 % 60;String strMinute = null;String strSecond = null;if (minute < 10) {              //如果歌曲的时间中的分钟小于10strMinute = "0" + minute; //在分钟的前面加一个0} else {strMinute = minute + "";}if (second < 10) {             //如果歌曲的时间中的秒钟小于10strSecond = "0" + second;//在秒钟前面加一个0} else {strSecond = second + "";}tv_total.setText(strMinute + ":" + strSecond);//歌曲当前播放时长minute = currentPostition / 1000 / 60;second = currentPostition / 1000 % 60;if (minute < 10) {             //如果歌曲的时间中的分钟小于10strMinute = "0" + minute;//在分钟的前面加一个0} else {strMinute = minute + "";}if (second < 10) {               //如果歌曲的时间中的秒钟小于10strSecond = "0" + second;  //在秒钟前面加一个0} else {strSecond = second + "";}tv_progress.setText(strMinute + ":" + strSecond);}};class MyServiceConn implements ServiceConnection { //用于实现连接服务@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {musicControl = (MusicService.MusicControl) service;}@Overridepublic void onServiceDisconnected(ComponentName name) {}}private void unbind(boolean isUnbind) {if (!isUnbind) {                  //判断服务是否被解绑musicControl.pausePlay(); //没有解绑,就暂停播放音乐unbindService(conn);      //解绑服务stopService(intent);      //停止服务}}@Overridepublic 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;}}@Overrideprotected void onDestroy() {super.onDestroy();unbind(isUnbind); //解绑服务}
}

更多推荐

CH8

本文发布于:2024-03-08 19:12:26,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1721985.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!