文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Android Service服务详细介绍及使用总结

2022-06-06 05:41

关注

Android Service服务详解

一.Service简介

       Service是android 系统中的四大组件之一(Activity、Service、BroadcastReceiver、 ContentProvider),它跟Activity的级别差不多,但不能页面显示只能后台运行,并且可以和其他组件进行交互。service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后台的,例如,一个service可能处理网络 事物,播放音乐,执行文件I/O,或与一个内容提供者交互,所有这些都在后台进行。
       我们一定要知道的是这里Service的后台运行并不是子线程。Service的运行是在主线程中进行的,只是它没有界面显示而已,它的耗时操作同样需要开启子线程,否者会跟Activity一样出现ANR(application not response–程序没有响应)。
       我们要知道的是主线程的内容包括UI和后台。只要程序中的UI或后台其中一个在跑,程序都算是在运行状态。

(一)Service的创建和注册1.Service服务的创建

必须要实现重写其中的onBind方法,可以在里面做各种操作,也可以接收传递过来的Intent的数据做处理。


public class MyService extends Service {
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    System.out.println("MyService.onBind");
    return null;
  }
 }

2.Service服务的注册,在AndroidManifest中注册

<service android:name=".MyService" />

       服务的注册是四大组件中最简单的一个,一般只要设置name属性就可以了。但是如果有其他需求还是要设置其他的属性值的。

       对Service服务做好创建和注册后,就可以操作服务了。

(二)Service两种启动模式

Service的启动有两种方式:Context.startService() 和 Context.bindService()。这里的Context是上下文的意思。

1.startService()方式启动时的生命周期回调方法

(1)启动服务startService : –>onCreate()–> onStart()
(2)停止服务stopService : –>onDestroy()
       如果调用者直接退出而没有停止Service,则Service 会一直在后台运行。这里的退走只是关闭了UI界面。
       startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方 法,接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用 startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用 startService()方法启动的服务,只能调用stopService()方法结束服务,服务结束时 会调用生命周期的onDestroy()方法。

2.bindService()方式启动时的生命周期回调方法

(1)绑定bindService : –> onCreate() –> onBind()
(2)解绑unbindService: –>onUnbind()
(3)正常停止程序服务的方法是先解绑unbindService,再停止服务stopService。
(4)如果绑定后调用stopService 方法,这时是不能停止服务的,如果这时再调用解绑unbindService,程序后先解绑,后停止服务。
       用bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate() 方法,接着调用onBind()方法。这个时候调用者和服务绑定在一起,调用者退出了,系统就会
先调用服务的onUnbind()方法,接着调用onDestroy()方法。如果调用       bindService()方法前服务已经被绑定,多次调用bindService()方法并不会导致多次创建服务及绑定(也就是说 onCreate()和onBind()方法并不会被多次调用)。如果调用者希望与正在绑定的服务解除绑 定,可以调用unbindService()方法,调用该方法也会导致系统调用服务的onUnbind()->onDestroy()方法。

绑定Service方法:bindService(intent, conn, Service.BIND_AUTO_CREATE);
三个参数的说明:
第一个:Intent对象
第二个:ServiceConnection对象,创建该对象要实现它的onServiceConnected()和 on ServiceDisconnected()来判断连接成功或者是断开连接
第三个:创建Service的模式,一般指定绑定的时候自动创建

(三)Service的五个生命周期的回调方法

1.周期命名

(1)onCreate()
(2)onStart()
(3)onBind()
(4)onUnBind()
(5)onDestroy()

2.生命周期图解

sheng
上面展示的是没有绑定服务和有绑定服务的生命周期的不同情况的过程。

3.关于几个方法的说明

(1)onCreate()说明服务第一次被创建
(2)onStartComand()说明服务开始工作
(3)onBind()说明服务已经绑定
(4)onUnBind()说明服务已经解绑
(5)onDestroy()说明服务已经停止
       正如上面说的启动服务有两种方式,一个是使用startService,另一个方法是使用bindService方法;使用bindService方法没有回调到startCommand方法;
       也可以先启动服务用startService,再绑定服务用bindService,这时的Service的回调方法的顺序是:
       –>onCreate()–>onStartCommand()–>onBind()

二.IntentService

       普通的Service要创建一个线程去完成耗时操作,因为其运行在主线程,並且要手動停止IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程 来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完 后,IntentService会自动停止,而不需要我们去手动控制。
       另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。 而且,所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。
       那么,用IntentService有什么好处呢?
       首先,我们省去了在Service中手动开线程的麻烦,
       第二,当操作完成时,我们不用手动停止Service IntentService,一个方便我们处理业务流程的类,它是一个Service,但是比Service更智能

三.查看Service的生命周期回调方法的简单示例

本示例只是用来看看Service在服务开启时,停止时,绑定时,解绑时,生命周期方法的回调情况加深对Service生命周期的印象。

(一)创建MyService类(继承Service)


package com.lwz.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
  
public class MyService extends Service {
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    System.out.println("MyService.onBind");
    return null;
  }
  @Override
  public void onCreate() {
    System.out.println("MyService.onCreate");
    super.onCreate();
  }
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    System.out.println("MyService.onStartCommand");
    return super.onStartCommand(intent, flags, startId);
  }
  @Override
  public void onDestroy() {
    System.out.println("MyService.onDestroy");
    super.onDestroy();
  }
  @Override
  public boolean onUnbind(Intent intent) {
    System.out.println("MyService.onUnbind");
    return super.onUnbind(intent);
  }
}

(二)在AndroidManifest中注册服务

<service android:name=".MyService" />

(三)设计控制服务开启、停止、绑定、解绑状态的代码


package com.lwz.service;
import android.app.Service;
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.view.View;

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }
  //开启服务
  public void startService(View view) {
    //开启服务需要Intent对象,和Activity跳转类似
    startService(new Intent(this, MyService.class));
  }
  //停止服务
  public void stopService(View view) {
    //停止服务的方法
    stopService(new Intent(this, MyService.class));
  }
  //绑定服务
  public void bindService(View view) {
    //绑定服务
    bindService(new Intent(this, MyService.class), conn, flags);
  }
  //解绑服务
  public void unBindService(View view) {
    //防止在没有绑定的情况下,去解除绑定,抛出异常
    try {
      //解除绑定
      unbindService(conn);
    } catch (Exception e) {
      System.out.println("MainActivity.unBindService" + e);
    }
  }
  //服务绑定的连接对象
  private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
  };
  //服务绑定的标识
  //BIND_AUTO_CREATE 绑定的同时,启动Service
  private int flags = Service.BIND_AUTO_CREATE;
}

上面是使用四个按钮来实现服务的几种状态的改变。

(四)布局文件的设计


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical">
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="Service" />
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="startService"
    android:text="启动服务" />
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="stopService"
    android:text="停止服务" />
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="bindService"
    android:text="绑定服务" />
  <Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="unBindService"
    android:text="解绑服务" />
</LinearLayout>

上面布局文件代码比较简单的,只是用四个按钮搞定!

程序运行后显示的界面:
s1

点击“启动服务”按钮后的Log信息:
s2
这时有两个回调方法执行。onCreate和onStartCommand.

点击“停止服务”按钮后的Log信息:
s3
执行了一个回调方法:onDestroy;这时服务已经停止,相当于程序刚运行的状态!

点击“绑定服务”按钮后的Log信息:
s4
这里执行了两个回调方法,先启动服务,再绑定服务!
在绑定服务的情况下是不能停止服务的,要解绑服务才能停止服务。
在程序的服务启动/绑定了的情况下,再点击启动服务,只会回调onStartCommand方法,也就是说一个服务在一个生命周期内只会回调一次onCreate方法。

点击“解绑服务”按钮后显示的Log信息:
s5
执行了两个回调方法:onUnBind和onDestroy

       如果是用服务做简单 的事情,使用第一种方法来启动服务就可以了,但是如果需要涉及到比较复杂的数据处理和操作就用绑定服务的方法来启动服务。

四.IntentService的使用示例

程序设计:查找手机SD卡中的所有图片显示在UI界面上。


//(一)布局文件activity_main.xml文件实际
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/activity_main"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <ListView
    android:id="@+id/main_lv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"></ListView>
</RelativeLayout>

非常简单的布局设计,使用List View来显示所有的图片

(二)遍历文件的工具类的设计


package com.lwz.intentservice;
import android.os.Environment;
import android.os.StatFs;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class FileUtils {  
  
  public static final ArrayList<File> getAllPicture(File dir) {
    ArrayList<File> files = getAllFile(dir);
    ArrayList<File> imgList = new ArrayList<>();
    for (File file : files) {
      if (file.getName().endsWith(".png") || file.getName().endsWith(".jpg"))
        imgList.add(file);
    }
    return imgList;
  }
  
  public static final void getFileFromDir(File dir, List<File> fileList) {
    File[] files = dir.listFiles();
    if (files == null)
      return;
    for (File file : files) {
      if (file.isDirectory())
        getFileFromDir(file, fileList);
      fileList.add(file);
    }
  }   
  
  public static final ArrayList<File> getAllPicture() {
    return getAllPicture(Environment.getExternalStorageDirectory());
  }  
}

(三)简化BaseAdapter的一个工具类


package com.lwz.intentservice;
import android.content.Context;
import android.widget.BaseAdapter;
import java.util.ArrayList;
import java.util.List;

public abstract class ListItemAdapter<T> extends BaseAdapter {
  List<T> list = new ArrayList<>();
  Context context;
  ListItemAdapter(Context context, List<T> list) {
    this.context = context;
    this.list = list;
  }
  ListItemAdapter(Context context, T[] list) {
    this.context = context;
    for (T t : list) {
      this.list.add(t);
    }
  }
  @Override
  public int getCount() {
    return list == null ? 0 : list.size();
  }
  @Override
  public T getItem(int position) {
    return list == null ? null : list.get(position);
  }
  @Override
  public long getItemId(int position) {
    return position;
  }
}

(四)MyIntentService的创建


package com.lwz.intentservice;
import android.app.IntentService;
import android.content.Intent;
import android.os.Message;
import android.util.Log;
import java.io.File;
import java.util.ArrayList;

public class MyIntentService extends IntentService {
  
  public MyIntentService() {
    super("myService");
  }
  
  @Override
  protected void onHandleIntent(Intent intent) {
    Log.e("TAG", "子线程开始工作");
    //遍历文件夹获取图片
    ArrayList<File> list = FileUtils.getAllPicture();
    //使用handler发送信息
    Message msg = Message.obtain();
    //这里给handler对象传递一个对象
    msg.obj = list;
    //发送广播来传递数据
    Intent intent1 = new Intent("filefinish");
    intent1.putExtra("file", list);
    sendBroadcast(intent1);
  }
  @Override
  public void onCreate() {
    super.onCreate();
    Log.e("TAG", "onCreate");
  }
  @Override
  public void onDestroy() {
    super.onDestroy();
    Log.e("TAG", "onDestroy");
  }
}

这里的Intent Service的注册和Intent的注册是一样的。

(五)MainActivity中的代码设计


package com.lwz.intentservice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ListView;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
  //定义布局内的控件
  ListView listView;
  //定义适配器的数据的集合
  //一定要static???
  static ArrayList<File> fileList;
  static MyBaseAdapter adapter;
  MyBroadcastReceiver mbcr;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mbcr = new MyBroadcastReceiver();
    //动态注册一个广播
    IntentFilter filter = new IntentFilter();
    filter.addAction("filefinish");
    registerReceiver(mbcr, filter);// 注册
    //创建适配器的对象
    adapter = new MyBaseAdapter(this, fileList);
    //实例化布局内的控件
    listView = (ListView) findViewById(R.id.main_lv);
    //给listView设置适配器
    listView.setAdapter(adapter);
    //启动服务
    startService(new Intent(this, MyIntentService.class));
  }
  //创建适配器的类
  class MyBaseAdapter extends ListItemAdapter<File> {
    MyBaseAdapter(Context context, List<File> list) {
      super(context, list);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ImageView image = null;
      if (convertView == null) {
        image = new ImageView(getBaseContext());
        convertView = image;
      } else {
        image = (ImageView) convertView;
      }
      //设置图片资源和属性
      image.setImageURI(Uri.fromFile(fileList.get(position)));
      image.setScaleType(ImageView.ScaleType.FIT_XY);
      image.setAdjustViewBounds(true);
      return image;
    }
  }
  //停止服务
  public void stop() {
    stopService(new Intent(MainActivity.this, MyIntentService.class));
  }
  @Override
  protected void onDestroy() {
    super.onDestroy();
    //即使之前停止了服务,再次停止服务也是不会报错的
    stop();
    //解除广播
    unregisterReceiver(mbcr);
  }
  //动态创建广播接收者
  class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      //对接收到的广播进行处理,intent里面包含数据
      fileList = (ArrayList<File>) intent.getSerializableExtra("file");
      //刷新适配器
      adapter.notifyDataSetChanged();
      //停止服务,它的子线程也会停止
      stop();
    }
  }
}

      程序运行前还记得加上SD卡的访问权限;
      上面程序功能还是有点问题!遍历完文件后。页面没有马上更新?退出程序再进来,页面上马上显示SD卡的图片。
      一般的说遍历文件夹也不算是耗时操作,这里只是简单示范。
      一般的耗时操作是从网络下载数据,或本地移动大文件等等。

五.同一个程序中Service和Activity通信的一些方式,这里展示主要代码。

这里组件不要忘记在AndroidManifest中注册

(一)使用Intent来传递数据1.MainActivity中的代码,发送数据

Intent intent = new Intent(this, MyService.class); 

intent.putExtra("msg", "activity向service传递一个hello service");

 startService(intent);

2.MyService中的代码,接收数据

      这里要使用onStartCommand的方法来接收Intent的数据,如果上面使用的是bind的方法来启动服务,这里可以在onBind方法中接收数据。


@Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    Log.e("onStartCommand", intent.getStringExtra(msg));
    return super.onStartCommand(intent, flags, startId);
  }

(二)使用单例模式来传递数据

      这个方法算是有点麻烦的吧!这里要在MyService类中先做好单例,然后在Activity中调用MyService对象的方法

1.MyService中的代码


//定义一个静态的类变量,单例的使用准备
  private static MyService instance;
  //静态方法,返回的是一个本类对象
  //为了能让另一边的类调用Myservice的方法
  public static MyService getInstance() {
    return instance;
  }
  @Override
  public void onCreate() {
    super.onCreate();
    //单例模式变量赋值
    instance = this;
  }
public void print(String msg) {
    Log.e("service", msg);
  }

      其中print方法是在Activity中调用的,可以达到传送数据给MyService,但是这里要先启动过服务后才能使用单例,因为这里是在MyService的onCreate方法中把对象赋值给instance,之后才能实现单例。

2.MainActivity中的代码:



      //必须保证Myservice对象不能为null
    //静态的变量,最后释放(不用的时候,手动将static变量=null)
    if (MyService.getInstance() != null) {
      MyService.getInstance().print("使用单例从activity中调用service的方法");
    }

(三)广播传参传数据

      弄两个广播接收者相互传数据。 

      这里要在MyService和MyService中分别动态的创建广播接收者和动态注册广播接收者,然后在MainActivity中发送广播,在MyService中接收到广播传来递数据后,在发送广播,让MainActivity接收广播数据!

1.MyService中的代码:


@Override
  public void onCreate() {
    super.onCreate();
    //动态注册广播接收者,要定义好接收的action属性值
    IntentFilter filter = new IntentFilter("service");
    registerReceiver(serviceReceiver, filter);
  }
 //定义一个广播接收者BroadcastReceiver
  BroadcastReceiver serviceReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      Log.e("service", "接收到了activity发送的广播:" + intent.getStringExtra("msg"));
//发送广播给MainActivity
      sendBroadcast(new Intent("activity").putExtra("msg", "发送给activity的消息"));
    }
  };

2.MainActivity中的代码:


@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //注册本类内的广播,定义好action的属性值
    IntentFilter filter = new IntentFilter("activity");
    registerReceiver(activityReceiver, filter);
  }
 
  public void sendBroadcast(View view) {
    //指明action属性值
    Intent intent = new Intent("service");
    intent.putExtra("msg", "activity向广播传递一个hello broadcast");
    sendBroadcast(intent);
  }
 //定义一个内部类的广播接收者,用于接收MyService传递过来的数据
  BroadcastReceiver activityReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      Log.e("activity", intent.getStringExtra("msg"));
    }
  };

(四)MyService实例调用方法

      这个方法是最最麻烦的方法了!涉及到一个Binder类的使用!这里要被调用的方法其实不是MyService中的方法,而是里面的内部接口的抽象方法,需要在MainActivity中去实现这个方法!但是,实际这个方法实在MyService中执行的。

1.MyService中的代码:


//定义一个接口
  interface Callback {
    //定义两个要实现的方法
    void call();
    void start();
  }
  //定义一个接口对象
  Callback callback;
  
  class Mybinder extends Binder {
    public MyService getService() {
      return MyService.this;
    }
    //设置回调方法
    public void setCallback(Callback callback) {
      MyService.this.callback = callback;
    }
  }
  //定义一个模拟开始音乐播放的方法
  //需要重写start里面的方法来开始播放音乐
  public void startMusic() {
    //播放
    Toast.makeText(this, "音乐开始播放", Toast.LENGTH_SHORT).show();
    callback.start();
  }
 @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    //要传递一个MyBinder对象给MainActivity
    return new MyBinder();
  }
//定义一个模拟开始音乐播放的方法
  //需要重写start里面的方法来开始播放音乐
  public void startMusic() {
    //播放
    Toast.makeText(this, "音乐开始播放", Toast.LENGTH_SHORT).show();
//在MainActivity中实例化callback对象
    callback.start();
  }

      上面的代码中要开始播放音乐要调用startMusic方法,并且要实例化里面的callback对象,而要实例化callback对象必须要调用内部类Mybinder的set Callback方法,而实现这个方法又必须实现这个接口的方法!

2.MainActivity中的代码:



//定义一个MyService对象
MyService myService;
  public void bindService(View view) {
    bindService(new Intent(this, MyService.class), conn, BIND_AUTO_CREATE);
 myService.startMusic();
  }
//创建ServiceConnection对象
 ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      //连接服务后,操作。。。
      //获取IBinder的bind对象,从MyService的onBinder中传递过来的
      MyService.Mybinder bind = (MyService.Mybinder) service;
      //通过bind对象获取Service对象
      myService = bind.getService();
      //设置监听事件的回调方法,并实现里面的两个方法
//这里的回调方法不是MyService中的,而是内部类Mybinder中的
      bind.setCallback(new MyService.Callback() {
        @Override
        public void call() {
          Log.e("activity", "Service回调Activity");
        }
        @Override
        public void start() {
          //比如在后台播放音乐;开始播放音乐
          Log.e("action", "正在播放音乐");
          //关闭页面
          finish();
        }
      });

      上面的关系确实是有点乱,我发现我有些注解还是有点问题的!! 

      上面就是Service中的个方面的总结。 

      Service还可以用来做进程间的数据传递,这里就涉及到AIDL(Android Interface Definition Language,安卓接口定义语言)进程通信。这个相对来说比较复杂,另作总结!

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

您可能感兴趣的文章:Android Service绑定过程完整分析Android Service总结及详细介绍Android开发入门之Service用法分析Android Service中方法使用详细介绍Android中Service的全面总结浅谈Android中Service的注册方式及使用


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯