文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

实例解析Android系统中的ContentProvider组件用法

2022-06-06 08:35

关注

ContentProvider为Android四大组件之一,主要用来应用程序之间的数据共享,也就是说一个应用程序用ContentProvider将自己的数据暴露出来,其他应用程序通过ContentResolver来对其暴露出来的数据进行增删改查。
ContenProvider与ContentResolver之间的对话同过Uri(通用资源标识符),一个不恰当的比喻就好像浏览器要显示一个网页要有一个东西发送请求,这相当于ContentResolver,你要拿东西就要知道去哪里拿,你就得知道服务器的域名或网址,而这个网址就相当于Uri,当到达服务器的时候服务器要有个东西来处理,这就相当于ContenProvider。

A程序通过ContenProvider来暴露数据的基本步骤:
1、实现一个ContenProvider的子类,并重写query,insert,update,delete等这几个方法,
2、在androidmanifest.xml中注册ContenProvider,指定的android:authorities属性
B程序通过ContentResolver来操作A程序暴露出来的数据的基本步骤
1、通过content的getContentResolver()来获取ContentResolver对象
2、通过ContentResolver对象来query,insert,update,delete来进行操作
在实现query,insert,update,delete时有一个重要的参数Uri类,Uri一个中要的方法Uri.parse(String str)用来解析str字符串,而str字符串格式一般都有A程序提供给B程序,B程序按照指定的格式去请求 。比如:content//:com.android.xiong.ConentProviderTestA.firstContentProvider/xiong 其格式一般分为三个部分:content//:这部分是固定不变的 而com.android.xiong.ConentProviderTestA.firstContentProvider表A程序在androidmanifest.xml注册的android:authorities属性,xiong则表示资源部分


<provider 
      android:name="com.android.xiong.conentprovidertesta.FirstContentProvider" 
      android:authorities="com.android.xiong.ConentProviderTestA.firstContentProvider" 
      android:exported="true" > 
</provider> 


实例如下

2016423110001350.jpg (408×454)

2016423110025072.jpg (399×620)

A程序:
UserInfo.java


package com.android.xiong.conentprovidertesta; 
import android.content.ContentProvider; 
import android.content.ContentValues; 
import android.content.UriMatcher; 
import android.database.Cursor; 
import android.database.sqlite.SQLiteDatabase; 
import android.net.Uri; 
public class FirstContentProvider extends ContentProvider { 
  // UriMatcher类主要用来匹配Uri 
  private static final UriMatcher uriMatcher = new UriMatcher( 
      UriMatcher.NO_MATCH); 
  private MySqlite mysqlite; 
  static { 
    // 注册向外部程序提供的Uri 
    uriMatcher.addURI(UserInfo.AUTOR, "userinfo", 1); 
    uriMatcher.addURI(UserInfo.AUTOR, "userinfoall", 2); 
  } 
  //删除数据 
  @Override 
  public int delete(Uri uri, String selection, String[] selectionArgs) { 
    int number = 0; 
    if (uriMatcher.match(uri) == 1) { 
      // 根据条件删除数据,并获取删除的行数 
      number = mysqlite.getReadableDatabase().delete("user_info", 
          selection, selectionArgs); 
    } 
    // 通知数据已经改变 
    getContext().getContentResolver().notifyChange(uri, null); 
    return number; 
  } 
  @Override 
  public String getType(Uri uri) { 
    return null; 
  } 
  //插入数据 
  @Override 
  public Uri insert(Uri uri, ContentValues values) { 
    String name = values.getAsString(UserInfo.User.NAME).toString(); 
    String age = values.getAsInteger(UserInfo.User.AGE).toString(); 
    String maxId = "select max(id) id from user_info"; 
    Cursor cursor = mysqlite.getReadableDatabase().rawQuery(maxId, null); 
    cursor.moveToFirst(); 
    int userid = cursor.getInt(0) + 1; 
    if (uriMatcher.match(uri) == 1) { 
      mysqlite.getWritableDatabase().execSQL( 
          "insert into user_info values(?,?,?)", 
          new String[] { String.valueOf(userid), name, age }); 
    } 
    return uri; 
  } 
  // 连接数据库 
  @Override 
  public boolean onCreate() { 
    mysqlite = new MySqlite(getContext(), "userinfo.db", null, 1); 
    return true; 
  } 
  //查询数据 
  @Override 
  public Cursor query(Uri uri, String[] projection, String selection, 
      String[] selectionArgs, String sortOrder) { 
    SQLiteDatabase sqlite = mysqlite.getReadableDatabase(); 
    String str = "select name,age from user_info"; 
    if (uriMatcher.match(uri) == 1) { 
      str += " where " + selection; 
    } 
    Cursor cursor = sqlite.rawQuery(str, selectionArgs); 
    return cursor; 
  } 
  //修改数据 
  @Override 
  public int update(Uri uri, ContentValues values, String selection, 
      String[] selectionArgs) { 
    SQLiteDatabase sqlite = mysqlite.getReadableDatabase(); 
    int number = 0; 
    if (uriMatcher.match(uri) == 1) { 
      number = sqlite.update("user_info", values, selection, 
          selectionArgs); 
    } 
    return number; 
  } 
} 

 
FirstContentProvider.java


package com.android.xiong.conentprovidertesta; 
import android.content.ContentProvider; 
import android.content.ContentValues; 
import android.content.UriMatcher; 
import android.database.Cursor; 
import android.database.sqlite.SQLiteDatabase; 
import android.net.Uri; 
public class FirstContentProvider extends ContentProvider { 
  // UriMatcher类主要用来匹配Uri 
  private static final UriMatcher uriMatcher = new UriMatcher( 
      UriMatcher.NO_MATCH); 
  private MySqlite mysqlite; 
  static { 
    // 注册向外部程序提供的Uri 
    uriMatcher.addURI(UserInfo.AUTOR, "userinfo", 1); 
    uriMatcher.addURI(UserInfo.AUTOR, "userinfoall", 2); 
  } 
  //删除数据 
  @Override 
  public int delete(Uri uri, String selection, String[] selectionArgs) { 
    int number = 0; 
    if (uriMatcher.match(uri) == 1) { 
      // 根据条件删除数据,并获取删除的行数 
      number = mysqlite.getReadableDatabase().delete("user_info", 
          selection, selectionArgs); 
    } 
    // 通知数据已经改变 
    getContext().getContentResolver().notifyChange(uri, null); 
    return number; 
  } 
  @Override 
  public String getType(Uri uri) { 
    return null; 
  } 
  //插入数据 
  @Override 
  public Uri insert(Uri uri, ContentValues values) { 
    String name = values.getAsString(UserInfo.User.NAME).toString(); 
    String age = values.getAsInteger(UserInfo.User.AGE).toString(); 
    String maxId = "select max(id) id from user_info"; 
    Cursor cursor = mysqlite.getReadableDatabase().rawQuery(maxId, null); 
    cursor.moveToFirst(); 
    int userid = cursor.getInt(0) + 1; 
    if (uriMatcher.match(uri) == 1) { 
      mysqlite.getWritableDatabase().execSQL( 
          "insert into user_info values(?,?,?)", 
          new String[] { String.valueOf(userid), name, age }); 
    } 
    return uri; 
  } 
  // 连接数据库 
  @Override 
  public boolean onCreate() { 
    mysqlite = new MySqlite(getContext(), "userinfo.db", null, 1); 
    return true; 
  } 
  //查询数据 
  @Override 
  public Cursor query(Uri uri, String[] projection, String selection, 
      String[] selectionArgs, String sortOrder) { 
    SQLiteDatabase sqlite = mysqlite.getReadableDatabase(); 
    String str = "select name,age from user_info"; 
    if (uriMatcher.match(uri) == 1) { 
      str += " where " + selection; 
    } 
    Cursor cursor = sqlite.rawQuery(str, selectionArgs); 
    return cursor; 
  } 
  //修改数据 
  @Override 
  public int update(Uri uri, ContentValues values, String selection, 
      String[] selectionArgs) { 
    SQLiteDatabase sqlite = mysqlite.getReadableDatabase(); 
    int number = 0; 
    if (uriMatcher.match(uri) == 1) { 
      number = sqlite.update("user_info", values, selection, 
          selectionArgs); 
    } 
    return number; 
  } 
} 

MySqlite.java


package com.android.xiong.conentprovidertesta; 
import android.content.Context; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteDatabase.CursorFactory; 
import android.database.sqlite.SQLiteOpenHelper; 
public class MySqlite extends SQLiteOpenHelper { 
  static final String sql = "create table user_info(id int,name varchar(30),age int)"; 
  public MySqlite(Context context, String name, CursorFactory factory, 
      int version) { 
    super(context, name, factory, version); 
    // TODO Auto-generated constructor stub 
  } 
  @Override 
  public void onCreate(SQLiteDatabase db) { 
    //创建数据表 
    db.execSQL(sql); 
  } 
  @Override 
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    // TODO Auto-generated method stub 
  } 
} 

MainActivity.java


package com.android.xiong.conentprovidertesta; 
import android.content.Context; 
import android.database.sqlite.SQLiteDatabase; 
import android.database.sqlite.SQLiteDatabase.CursorFactory; 
import android.database.sqlite.SQLiteOpenHelper; 
public class MySqlite extends SQLiteOpenHelper { 
  static final String sql = "create table user_info(id int,name varchar(30),age int)"; 
  public MySqlite(Context context, String name, CursorFactory factory, 
      int version) { 
    super(context, name, factory, version); 
    // TODO Auto-generated constructor stub 
  } 
  @Override 
  public void onCreate(SQLiteDatabase db) { 
    //创建数据表 
    db.execSQL(sql); 
  } 
  @Override 
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
    // TODO Auto-generated method stub 
  } 
} 

activity_main.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:orientation="vertical" 
  tools:context=".MainActivity" > 
  <TextView 
    android:id="@+id/txt1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:gravity="center_horizontal" 
    android:text="添加信息" 
    android:textSize="20dp" /> 
  <EditText 
    android:id="@+id/ed1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:hint="添加name" /> 
  <EditText 
    android:id="@+id/ed2" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:hint="添加age" 
    android:inputType="number" /> 
  <Button 
    android:id="@+id/bt1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:text="提交数据" /> 
    <ListView 
      android:id="@+id/lists" 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" > 
    </ListView> 
</LinearLayout> 

item.xml


<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:orientation="horizontal" > 
  <TextView  
    android:id="@+id/item_txt1" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"/> 
    <TextView  
    android:id="@+id/item_txt2" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"/> 
</LinearLayout> 

B程序
UserInfo.java


package com.android.xiong.contentprovidertestb; 
//向外部程序提供一个工具类 
public class UserInfo { 
  // 获取ContentProvider的“域名” 
  public static final String AUTOR = "com.android.xiong.ConentProviderTestA.firstContentProvider"; 
  //定义一个静态内部类,提供ContentProvider可操作的列 
  public static final class User { 
    public static final String ID="id"; 
    public static final String NAME="name"; 
    public static final String AGE="age"; 
    //定义该content提供服务的一个Uri 
    public static final String uri="content://"+AUTOR+"/userinfo"; 
    public static final String uriall="content://"+AUTOR+"/userinfoall"; 
  } 
} 

MainActivity.java


package com.android.xiong.contentprovidertestb; 
import java.util.ArrayList; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import android.app.Activity; 
import android.content.ContentValues; 
import android.database.Cursor; 
import android.net.Uri; 
import android.os.Bundle; 
import android.view.Menu; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.EditText; 
import android.widget.ListView; 
import android.widget.ScrollView; 
import android.widget.SimpleAdapter; 
import android.widget.Toast; 
public class MainActivity extends Activity { 
  private Button bt1, bt2, bt3, bt4; 
  private EditText ed1, ed2; 
  private ListView list1; 
  private ScrollView sc1; 
  @Override 
  protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 
    bt1 = (Button) findViewById(R.id.bt1); 
    bt2 = (Button) findViewById(R.id.bt2); 
    bt3 = (Button) findViewById(R.id.bt3); 
    bt4 = (Button) findViewById(R.id.bt4); 
    ed1 = (EditText) findViewById(R.id.ed1); 
    ed2 = (EditText) findViewById(R.id.ed2); 
    list1 = (ListView) findViewById(R.id.list); 
    // 显示所有数据 
    list1.setAdapter(adapter(0)); 
    sc1 = (ScrollView) findViewById(R.id.scr1); 
    // 向添加ContentProviderA应用的数据 
    bt1.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
        String eds1 = ed1.getText().toString(); 
        String eds2 = ed2.getText().toString(); 
        ContentValues content = new ContentValues(); 
        if (!eds1.equals("") && !eds2.equals("")) { 
          content.put(UserInfo.User.NAME, eds1); 
          content.put(UserInfo.User.AGE, eds2); 
          MainActivity.this.getContentResolver().insert( 
              Uri.parse(UserInfo.User.uri), content); 
          Toast.makeText(MainActivity.this, "数据插入成功", 
              Toast.LENGTH_LONG).show(); 
          // 刷新ListView界面 
          list1.setAdapter(adapter(0)); 
        } else { 
          Toast.makeText(MainActivity.this, "name和age不能为空", 
              Toast.LENGTH_LONG).show(); 
        } 
      } 
    }); 
    // 根据条件删除ContentProviderA应用的数据 
    bt2.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
        String eds1 = ed1.getText().toString(); 
        String eds2 = ed2.getText().toString(); 
        if (!eds1.equals("") || !eds2.equals("")) { 
          HashMap<String, String[]> wheres = wheres(eds1, eds2); 
          String sql = wheres.get("sql")[0]; 
          String[] selectags = wheres.get("selectages"); 
          MainActivity.this.getContentResolver().delete( 
              Uri.parse(UserInfo.User.uri), sql, selectags); 
        } else { 
          Toast.makeText(MainActivity.this, "请输入删除条件", 
              Toast.LENGTH_LONG).show(); 
        } 
        // 刷新ListView界面 
        list1.setAdapter(adapter(0)); 
      } 
    }); 
    // 修改数据 
    bt3.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
        String eds1 = ed1.getText().toString(); 
        String eds2 = ed2.getText().toString(); 
        ContentValues values = new ContentValues(); 
        // 根据条件将列修改为xiong,23 
        values.put(UserInfo.User.NAME, "xiong"); 
        values.put(UserInfo.User.AGE, "23"); 
        if (!eds1.equals("") || !eds2.equals("")) { 
          HashMap<String, String[]> wheres = wheres(eds1, eds2); 
          String sql = wheres.get("sql")[0]; 
          String[] selectags = wheres.get("selectages"); 
          int i=MainActivity.this.getContentResolver().update( 
              Uri.parse(UserInfo.User.uri), values, sql, 
              selectags); 
        } else { 
          Toast.makeText(MainActivity.this, "请输入删除条件", 
              Toast.LENGTH_LONG).show(); 
        } 
        // 刷新ListView界面 
        list1.setAdapter(adapter(0)); 
      } 
    }); 
    // 根据条件查询ContentProviderA应用的数据 
    bt4.setOnClickListener(new OnClickListener() { 
      @Override 
      public void onClick(View v) { 
        if (!ed1.getText().toString().equals("") 
            || !ed2.getText().toString().equals("")) 
          list1.setAdapter(adapter(1)); 
        else 
          list1.setAdapter(adapter(0)); 
      } 
    }); 
  } 
  // 用来判别条件 
  public HashMap<String, String[]> wheres(String eds1, String eds2) { 
    HashMap<String, String[]> where = new HashMap<String, String[]>(); 
    if (!eds1.equals("") && !eds2.equals("")) { 
      String[] sql = { UserInfo.User.NAME + "=? and " + UserInfo.User.AGE 
          + " =?" }; 
      String[] selectages = { eds1, eds2 }; 
      where.put("sql", sql); 
      where.put("selectages", selectages); 
    } 
    if (!eds1.equals("") && eds2.equals("")) { 
      String[] sql = { UserInfo.User.NAME + "=? " }; 
      String[] selectages = { eds1 }; 
      where.put("sql", sql); 
      where.put("selectages", selectages); 
    } 
    if (eds1.equals("") && !eds2.equals("")) { 
      String[] sql = { UserInfo.User.AGE + " =?" }; 
      String[] selectages = { eds2 }; 
      where.put("sql", sql); 
      where.put("selectages", selectages); 
    } 
    return where; 
  } 
  // 用来显示数据 
  public SimpleAdapter adapter(int i) { 
    Cursor cs = MainActivity.this.getContentResolver().query( 
        Uri.parse(UserInfo.User.uriall), null, null, null, null); 
    String eds1 = ed1.getText().toString(); 
    String eds2 = ed2.getText().toString(); 
    if (i == 1) { 
      if (!eds1.equals("") || !eds2.equals("")) { 
        HashMap<String, String[]> wheres = wheres(eds1, eds2); 
        String sql = wheres.get("sql")[0]; 
        String[] selectags = wheres.get("selectages"); 
        cs = MainActivity.this.getContentResolver().query( 
            Uri.parse(UserInfo.User.uri), null, sql, selectags, 
            null); 
      } 
    } 
    List<Map<String, Object>> lists = new ArrayList<Map<String, Object>>(); 
    while (cs.moveToNext()) { 
      Map<String, Object> map = new HashMap<String, Object>(); 
      map.put("name", cs.getString(0)); 
      map.put("age", cs.getString(1)); 
      lists.add(map); 
    } 
    SimpleAdapter simepl = new SimpleAdapter(MainActivity.this, lists, 
        R.layout.item, new String[] { "name", "age" }, new int[] { 
            R.id.item_txt1, R.id.item_txt2 }); 
    return simepl; 
  } 
  @Override 
  public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.main, menu); 
    return true; 
  } 
} 

activity.xml


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  xmlns:tools="http://schemas.android.com/tools" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:orientation="vertical" 
  tools:context=".MainActivity" > 
  <ScrollView 
    android:id="@+id/scr1" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" > 
    <LinearLayout 
      android:layout_width="match_parent" 
      android:layout_height="wrap_content" 
      android:orientation="vertical" > 
      <Button 
        android:id="@+id/bt1" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:text="添加" /> 
      <Button 
        android:id="@+id/bt2" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:text="删除" /> 
      <Button 
        android:id="@+id/bt3" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:text="更改" /> 
      <Button 
        android:id="@+id/bt4" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:text="查询" /> 
      <EditText 
        android:id="@+id/ed1" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:hint="输入name条件进行增删改查" /> 
      <EditText 
        android:id="@+id/ed2" 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:hint="输age条件进行增删改查" /> 
    </LinearLayout> 
  </ScrollView> 
  <ListView 
    android:id="@+id/list" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" > 
  </ListView> 
</LinearLayout> 

 
item.xml


<?xml version="1.0" encoding="utf-8"?> 
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  android:layout_width="match_parent" 
  android:layout_height="match_parent" 
  android:orientation="horizontal" > 
  <TextView  
    android:id="@+id/item_txt1" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"/> 
    <TextView  
    android:id="@+id/item_txt2" 
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content"/> 
</LinearLayout> 


监听ContentProvider数据改变
当程序A在执行insert、update、delete时,通过getContext().getContentResolver().notifyChange(uri, null)方法来告诉所有注册在该Uri的监听者数据发生改变


//删除数据 
  @Override 
  public int delete(Uri uri, String selection, String[] selectionArgs) { 
    int number = 0; 
    if (uriMatcher.match(uri) == 1) { 
      // 根据条件删除数据,并获取删除的行数 
      number = mysqlite.getReadableDatabase().delete("user_info", 
          selection, selectionArgs); 
    } 
    // 通知数据已经改变 
    getContext().getContentResolver().notifyChange(uri, null); 
    return number; 
  } 

 
在B程序通过registerContentObserver(Uri uri,boolean notifyForDescendents, ContentObserver observer)方法来监听A程序的数据改变


@Override 
protected void onCreate(Bundle savedInstanceState) { 
  super.onCreate(savedInstanceState); 
  setContentView(R.layout.activity_main); 
  // 为uri的数据改变注册监听器 
  getContentResolver().registerContentObserver( 
      Uri.parse("content://com.android.xiong.ConentProviderTestA.firstContentProvider/userinfo"), true, 
      new SmsObserver(new Handler())); 
} 

registerContentObserver中第三个参数为监听实例 ,需要通过继承ContentObserver类,并重写onChange(booleab selfChange)方法,该方法在监听Uri的ContentProvider的数据发生改变时触发该方法


// 提供方自定义的ContentOberver监听器 
private final class Observer extends ContentObserver { 
  public SmsObserver(Handler handler) { 
    super(handler); 
  } 
  @Override 
  public void onChange(boolean selfChange, Uri uri) { 
    // 查询发送邮箱中的短息(处于正在发送状态的短信放在发送箱) 
    Cursor cursor = getContentResolver().query( 
        Uri.parse("content://com.android.xiong.ConentProviderTestA.firstContentProvider/userinfo"), null, null, null, null); 
    } 
  } 
} 
您可能感兴趣的文章:Android 中自定义ContentProvider与ContentObserver的使用简单实例Android 中ContentProvider的实例详解Android ContentProvider实现获取手机联系人功能Android数据持久化之ContentProvider机制详解Android中ContentProvider和ContentResolver详解Android开发教程之ContentProvider数据存储android基础总结篇之八:创建及调用自己的ContentProviderAndroid学习笔记之ContentProvider和Uri详解Android7.0中关于ContentProvider组件详解


阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

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

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

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

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

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

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

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