开发者社区> 西游不取经> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

android批量发送短信

简介: 快过年了,自己写了个android批量发送短信,短信前面添加人名,用来节日发短信,这样别人就不知道我是批量发送的了哈哈哈,当然写的很菜,刚开始玩这个敬请指教  短信发送后去短信列表查看             首先AndroidManifest.
+关注继续查看

快过年了,自己写了个android批量发送短信,短信前面添加人名,用来节日发短信,这样别人就不知道我是批量发送的了害羞哈哈哈,当然写的很菜,刚开始玩这个敬请指教

 短信发送后去短信列表查看            


首先AndroidManifest.xml配置

软件的权限和需要用到达Activity类

    <uses-sdk
        android:minSdkVersion="22"
        android:targetSdkVersion="22" />
	<uses-permission android:name="android.permission.READ_SMS"/>
	<uses-permission android:name="android.permission.READ_CONTACTS"/>
	<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
	<uses-permission android:name="android.permission.RECEIVE_SMS"/>
	<uses-permission android:name="android.permission.SEND_SMS" />
	
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="sendmsg.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="receiver.SMSReceiver"></receiver>
        <activity android:name="sendmsg.ContactListActivity"></activity>
        <activity android:name="sendmsg.SendMsgActivity"></activity>
        
    </application>

创建联系人模型需要用的联系人的字段

package model;

public class ContactBean {
	private int contactId;
	private String desplayName;
	private String phoneNum;
	private String sortKey;
	private Long phoneId;
	private String LookUpKey;
	private int selected = 0;
	private  String formattedNumber;
	private String pinyin;
	
	public int getContactId() {
		return contactId;
	}
	public void setContactId(int contactId) {
		this.contactId = contactId;
	}
	public String getDesplayName() {
		return desplayName;
	}
	public void setDesplayName(String desplayName) {
		this.desplayName = desplayName;
	}
	public String getPhoneNum() {
		return phoneNum;
	}
	public void setPhoneNum(String phoneNum) {
		this.phoneNum = phoneNum;
	}
	public String getSortKey() {
		return sortKey;
	}
	public void setSortKey(String sortKey) {
		this.sortKey = sortKey;
	}
	public Long getPhoneId() {
		return phoneId;
	}
	public void setPhoneId(Long phoneId) {
		this.phoneId = phoneId;
	}
	public String getLookUpKey() {
		return LookUpKey;
	}
	public void setLookUpKey(String lookUpKey) {
		LookUpKey = lookUpKey;
	}
	public int getSelected() {
		return selected;
	}
	public void setSelected(int selected) {
		this.selected = selected;
	}
	public String getFormattedNumber() {
		return formattedNumber;
	}
	public void setFormattedNumber(String formattedNumber) {
		this.formattedNumber = formattedNumber;
	}
	public String getPinyin() {
		return pinyin;
	}
	public void setPinyin(String pinyin) {
		this.pinyin = pinyin;
	}
}


联系人listview界面,全选于取消,List<ContactBean> list,存储联系人信息,

单list不为空时传递给下一个Activity,  intentSendMsg.putExtra("user", userList.toString()); 只接收字符串

public class ContactListActivity extends Activity {
	private ContactListAdapter adapter;
	private ListView contactList;
	private List<ContactBean> list;
	private AsyncQueryHandler asyncQueryHandler;
	private Boolean cancel = true;
	
	private Map<Integer, ContactBean> contactIdMap = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		
		setContentView(R.layout.contact_list_view);
		contactList =  (ListView) findViewById(R.id.contact_list);
		
		final Button selectAll = (Button) findViewById(R.id.select_all);
		Button sureBtn = (Button) findViewById(R.id.select_sure);
		
		asyncQueryHandler = new MyAsyncQueryHandler(getContentResolver());
		
		init();
		
		final Intent intentSendMsg = new Intent(this, SendMsgActivity.class);
		
		selectAll.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				int len = list.size();
				if (Boolean.valueOf(cancel)) {	//全选
					cancel = false; 					
					selectAll.setText("取消");
					for (int i = 0; i < len; i++) {
						ContactListAdapter.getIsSelected().put(i, true);
					}
				} else {	// 取消
					cancel = true;
					selectAll.setText("全选");
					for (int i = 0; i < len; i++) {
						ContactListAdapter.getIsSelected().put(i, false);
					}
				}
				adapter.notifyDataSetChanged();
			}
		});
		
		sureBtn.setOnClickListener(new OnClickListener() {
			private List<String> userList = new ArrayList<String>();

			@Override
			public void onClick(View v) {
				Set<Entry<Integer, Boolean>> iterator = ContactListAdapter.getIsSelected().entrySet();
				for (Entry<Integer, Boolean> entry : iterator) {
					if (entry.getValue()) {
						int key = entry.getKey();
						ContactBean conBean = list.get(key);
						userList.add(conBean.getDesplayName()+":"+conBean.getPhoneNum());
					}
				}
				if (!userList.isEmpty()) {
					intentSendMsg.putExtra("user", userList.toString());
					startActivity(intentSendMsg);
					userList.clear();
				}
			}
		});
		
	}
	
	
	private void init() {
		Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
		
		String[] projection = {
			ContactsContract.CommonDataKinds.Phone._ID,
			ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
			ContactsContract.CommonDataKinds.Phone.DATA1, "sort_key",
			ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
			ContactsContract.CommonDataKinds.Phone.PHOTO_ID,
			ContactsContract.CommonDataKinds.Phone.LOOKUP_KEY
			
		};
		asyncQueryHandler.startQuery(0, null, uri, projection, 
				null, null, "sort_key COLLATE LOCALIZED asc");

	}
	
	
	
	private class MyAsyncQueryHandler extends AsyncQueryHandler {

		public MyAsyncQueryHandler(ContentResolver cr) {
			super(cr);
		}
		
		@Override
		protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
			if (cursor != null && cursor.getCount() > 0) {
				contactIdMap = new HashMap<Integer, ContactBean>();
				list = new ArrayList<ContactBean>();
				cursor.moveToFirst();
				int count = cursor.getCount();
				for (int i = 0; i < count; i++) {
					cursor.moveToPosition(i);
					String name = cursor.getString(1);
					String number = cursor.getString(2);
					String sortKey = cursor.getString(3);
					int contactId = cursor.getInt(4);
					Long photoId = cursor.getLong(5);
					String lookUpKey = cursor.getString(6);
					
					if (!contactIdMap.containsKey(contactId)) {
						ContactBean contact = new ContactBean();
						contact.setDesplayName(name);
						contact.setPhoneNum(number);
						contact.setSortKey(sortKey);
						contact.setPhoneId(photoId);
						contact.setLookUpKey(lookUpKey);
						list.add(contact);
						
						contactIdMap.put(contactId, contact);
					}
				}
				
				if(list.size() > 0) {
					setAdapter(list);
				}
			}
			super.onQueryComplete(token, cookie, cursor);
		}
		
	}
	
	private void setAdapter(List<ContactBean> list) {
		adapter = new ContactListAdapter(this, list);
		//listview contactList 设置适配器
		contactList.setAdapter(adapter);
	}

}

listview 的适配器, item数据 通过 getIsSelected 向下一个Activity传递数据

public class ContactListAdapter extends BaseAdapter {

	private LayoutInflater inflater;
	private List<ContactBean> list;
	private HashMap<String, Integer> alphaIndexer;
	private String[] sections;
	private Context ctx;
	
	private static HashMap<Integer, Boolean> isSelected;

	@Override
	public int getCount() {
		return list.size();
	}

	@Override
	public Object getItem(int position) {
		return list.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder;
		
		if (convertView == null) {
			//找到item布局文件
			//listview 里 inflate 第二个参数 可以写成null 或者 第三个参数 false
			convertView = inflater.inflate(R.layout.contact_list_item, parent, false);
			holder = new ViewHolder();
			holder.alpha = (TextView) convertView.findViewById(R.id.alpha);
			holder.name = (TextView) convertView.findViewById(R.id.name);
			holder.number = (TextView) convertView.findViewById(R.id.number);
			holder.cb = (CheckBox) convertView.findViewById(R.id.checkUser);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}
		
		
		ContactBean contact = list.get(position);
		String name = contact.getDesplayName();
		String number = contact.getPhoneNum();

		holder.name.setText(name);
		holder.number.setText(number);

		String currentStr = getAlpha(contact.getSortKey());

		String previewStr = (position - 1) >= 0 ? getAlpha(list.get(
				position - 1).getSortKey()) : "";

		if (!previewStr.equals(currentStr)) {
			holder.alpha.setVisibility(View.VISIBLE);
			holder.alpha.setText(currentStr);
		} else {
			holder.alpha.setVisibility(View.GONE);
		}
		
		final int pst = position;
		
		holder.cb.setChecked(getIsSelected().get(position));
		//单个联系人选择 
		holder.cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				getIsSelected().put(pst, isChecked);
			}
		});
		return convertView;
	}

	public ContactListAdapter(Context context, List<ContactBean> list) {
		this.ctx = context;
		this.inflater = LayoutInflater.from(context); //创建视图  设置上下文
		this.list = list;
		this.alphaIndexer = new HashMap<String, Integer>();
		
		isSelected = new HashMap<Integer, Boolean>();
		
		for (int i = 0; i < list.size(); i++) {
			String name = getAlpha(list.get(i).getSortKey());
			if (!alphaIndexer.containsKey(name)) {
				alphaIndexer.put(name, i);
			}
			getIsSelected().put(i, false);
		}
		
		Set<String> sectionLetters = alphaIndexer.keySet();
		ArrayList<String> sectionList = new ArrayList<String>(sectionLetters);
		Collections.sort(sectionList);

		sections = new String[sectionList.size()];
		sectionList.toArray(sections);
	}
	//listview item 需要展示的数据
	private static class ViewHolder {
		TextView alpha;
		TextView name;
		TextView number;
		CheckBox cb;
	}

	// 字母
	private String getAlpha(String str) {
		if (str == null) {
			return "#";
		}

		if (str.trim().length() == 0) {
			return "#";
		}

		char c = str.trim().substring(0, 1).charAt(0);
		
		Pattern pattern = Pattern.compile("^[A-Za-z]+$");
		if (pattern.matcher(c + "").matches()) {
			return (c + "").toUpperCase();
		} else {
			return "#";
		}

	}

	public static HashMap<Integer, Boolean> getIsSelected() {
		return isSelected;
	}

	public static void setIsSelected(HashMap<Integer, Boolean> isSelected) {
		ContactListAdapter.isSelected = isSelected;
	}
	
}

短信发送界面

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
	android:layout_height="wrap_content"
    >
        <ListView 
            android:id="@+id/usermsg_list"
            android:layout_width="wrap_content"
            android:layout_height="170dip"
            android:cacheColorHint="#000000"
     	    android:divider="#887d7d"
     	    android:dividerHeight="4dip"
     	    android:fadingEdge="none"
     	    android:scrollbars="vertical"
     	    android:scrollingCache="false"
     	    android:visibility="visible"/>
        <ScrollView 
           android:id="@+id/sv"
           android:layout_width="match_parent"
           android:layout_height="149dip"
           android:layout_below="@id/usermsg_list"
           android:background="@drawable/stbottom"
           android:paddingTop="4dip"
           android:orientation="vertical" >
           <LinearLayout 
               android:id="@+id/sms_linear"
               android:layout_height="wrap_content"
               android:layout_width="match_parent"
               android:orientation="vertical"/>
        </ScrollView>
        <LinearLayout 
            android:layout_width="match_parent"
			android:layout_height="wrap_content"
			android:orientation="vertical"
			android:layout_alignParentBottom="true"
			android:background="#ffffff"
            >
			<EditText 
			    android:id="@+id/edit"
			    android:layout_width="match_parent"
			    android:layout_height="150dip"
			    android:layout_marginTop="1dip"
			 	android:layout_marginBottom="1dip"
			 	android:padding="5dip"
			 	android:gravity="top"
			 	android:background="@drawable/phone_border"/>
			<Button 
			    android:id="@+id/send_btn"
			    android:layout_width="match_parent"
			    android:layout_height="wrap_content"
			    android:text="发送"
			    android:background="@drawable/sendbtn"
			    android:textColor="#ffffff"/>
        </LinearLayout>

</RelativeLayout>

短信发送界面listview中 item 用户名和电话 删除按钮

<?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/usermsg"
        android:layout_height="24dip"
        android:layout_width="wrap_content"
        android:layout_marginTop="4dip"
        android:layout_marginBottom="4dip"
        android:textSize="10pt"/>
	<Button 
	    android:id="@+id/user_del"
	    android:layout_width="26dip"
	    android:layout_height="26dip"
	    android:background="@drawable/del"
	    android:textColor="#d88822"
	    android:textSize="6pt"
	    android:gravity="center"
	    android:text="X"/>
</LinearLayout>

 main.xml里 注册的广播接 短信发送的状态

<receiver android:name="receiver.SMSReceiver"></receiver>
public class SMSReceiver extends BroadcastReceiver{
	public List<String> name = new ArrayList<String>();
	private int id = 0;
	private ScrollView sv;
	private Context ctx;
	private LinearLayout stateList;
	private Handler mHandler = new Handler();
	
	public SMSReceiver(ScrollView sv, Context ctx) {
		this.sv = sv;
		this.ctx = ctx;
	}
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	
	public List<String> getName() {
		return this.name;
	}
	
	public void setName(String name) {
		this.name.add(name);
	}
	
	private void setTextView(LinearLayout list) {
		int id = getId();
		// ScrollView item 设置
		LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		params.setMargins(0, 0, 10, 10);
		params.gravity = Gravity.END;
		TextView text = new TextView(ctx);
		
		text.setText("成功发送给:" + getName().get(id));
		setId(id + 1);
		
		text.setBackgroundResource(R.drawable.msg_bg);
		text.setPadding(5, 5, 100, 5);
		text.setSingleLine(false);
		text.setLayoutParams(params);
		// 添加到了LinearLayout里
		list.addView(text);
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				// ScrollView 短信发送成功后向下滚动
				sv.fullScroll(ScrollView.FOCUS_DOWN);
			}
		});
	}
	
	@Override
	public void onReceive(Context ctx, Intent intent) {
		if (stateList == null) {
			stateList = (LinearLayout) sv.findViewById(R.id.sms_linear);
		}
		
		if (intent.getAction().equals("SMS_SEND_ACTIOIN")) {
			switch (getResultCode()) {
			//发送成功
			case Activity.RESULT_OK:
				setTextView(stateList);
				break;
			default:
				break;
			}
		}
	}
	
	
	

}

短信发送

public class SendSMS {
	private static String SMS_SEND_ACTIOIN  = "SMS_SEND_ACTIOIN";
	private SMSReceiver SendSMSReceiver;
	private Context ctx;
	private ScrollView sv;
	private String SMSShort;
	
	public SendSMS(Context ctx, ScrollView sv) {
		this.ctx = ctx;
		this.sv = sv;
		registerReceiver();
	}
	
	public String getSMSShort() {
		return SMSShort;
	}

	public void setSMSShort(String sMSShort) {
		SMSShort = sMSShort;
	}



	//注册
	public void registerReceiver() {
		IntentFilter filter = new IntentFilter(SMS_SEND_ACTIOIN);
		SendSMSReceiver = new SMSReceiver(sv, ctx);
		ctx.registerReceiver(SendSMSReceiver, filter);
	}
	//取消注册
	public void unRegisterReceiver() {
		if (SendSMSReceiver != null) {
			ctx.unregisterReceiver(SendSMSReceiver);
		}
	}
	
	public int sendSMS(String sms, List<SendItemBean> list) {
		int size = 0;
		SmsManager smsManager = SmsManager.getDefault();
		Iterator<SendItemBean> it = list.iterator();
		Intent sendIT = new Intent(SMS_SEND_ACTIOIN);
		//注册广播
		PendingIntent sendPI = PendingIntent.getBroadcast(ctx, 0, sendIT, 0);
		//遍历电话号码
		while (it.hasNext()) {
			SendItemBean item = it.next();
			String[] user = item.getUserMsg().split(":");
			String name = user[0];
			String strSms = name + sms;
			SendSMSReceiver.setName(strSms);
			//短信发送
			smsManager.sendTextMessage(user[1], null, strSms, sendPI, null);
			size = item.getId() + 1;
		}
		return size;
	}
	
}


发送短信Activity界面
public class SendMsgActivity extends Activity{
	private static final int OVER_SUCCESS = 0;
	private static final int OVER_FAILUER = 1;
	
	private SendMsgAdapter adapter;
	private List<SendItemBean> list;
	private ListView userMsgList;
	private SendSMS sendSMS;
	private Thread mThread;
	private Button sendBtn;
	private EditText ed;
	private String SMS;
	
	//Handler ui更新, 只能在子线程中
	private Handler mHandler = new Handler(){
		public void handleMessage(Message msg) {
			//根据消息, 设置ui样式
			switch (msg.what) {
				case OVER_SUCCESS:
					sendBtn.setBackgroundColor(Color.parseColor("#f90000"));
					sendBtn.setText("发送");
					sendBtn.setClickable(true);
					mHandler.removeCallbacks(runnable);
					break;
				default:
					sendBtn.setBackgroundColor(Color.parseColor("#968e8e"));
					sendBtn.setText("短信批量发送中");
					sendBtn.setClickable(false);
					break;
			}
		}
	};
	
	Runnable runnable = new Runnable() {
		@Override
		public void run() {
			mHandler.obtainMessage(OVER_FAILUER).sendToTarget();
			// 联系人数组 传递给短信发送的方法
			int size = sendSMS.sendSMS(SMS, list);
			try {
				Thread.sleep(2000);
				if (size == list.size()) {
					//发送短信状态 发送成功与否的 消息
					mHandler.obtainMessage(OVER_SUCCESS).sendToTarget();
				} 
				
			} catch (Exception e) {
				mHandler.obtainMessage(OVER_FAILUER).sendToTarget();
			}
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.send_msg);
		
		ed = (EditText) findViewById(R.id.edit);
		sendBtn = (Button) findViewById(R.id.send_btn);
		userMsgList = (ListView) findViewById(R.id.usermsg_list);
		ScrollView scrollView = (ScrollView) findViewById(R.id.sv);
		
		
		Intent intent = getIntent();
		CharSequence userStr = intent.getStringExtra("user");
		
		sendSMS = new SendSMS(this, scrollView);
		list = new ArrayList<SendItemBean>();
		// 联系人数组
		String[] userAry = userStr.toString().replaceAll("\\[|\\]", " ").split(",");
		
		int len = userAry.length;
		for (int i = 0; i < len; i++) {
			SendItemBean sendItemBean = new SendItemBean();
			sendItemBean.setId(i);
			sendItemBean.setUserMsg(userAry[i]);
			list.add(sendItemBean);	
		}
		setAdapeter(list);
		sendBtnClick();
		
	}
	//确定发送
	public void sendBtnClick() {
		sendBtn.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				SMS = ed.getText().toString().trim();
				if (SMS.isEmpty()) {
					Toast.makeText(SendMsgActivity.this, "短信不能为空", Toast.LENGTH_SHORT).show();
				} else {
					mThread = new Thread(runnable);
					mThread.start();
				}
				
			}
		});
	}
	
	private void setAdapeter(List<SendItemBean> list) {
		adapter = new SendMsgAdapter(this, list);
		//listview 适配器
		userMsgList.setAdapter(adapter);
	}
	
	@Override
	protected void onPause() {
		super.onPause();
		//注册的广播销毁 退出当前activity时
		sendSMS.unRegisterReceiver();
		list.clear();
		adapter.notifyDataSetChanged();
	}
}
短信发送界面  联系人listview

public class SendMsgAdapter extends BaseAdapter {
	private List<SendItemBean> list;
	private LayoutInflater inflater;
	private Context ctx;
	
	private static String[] userItem;
	
	@Override
	public int getCount() {
		return list.size();
	}

	@Override
	public Object getItem(int position) {
		return list.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	public void removeItem(int position) {
		list.remove(position);
		//联系人单个删除 后 为空是 退出当前Activity
		if (list.size() == 0) {
			/*Intent intent = new Intent();
			intent.setClass(ctx, ContactListActivity.class);
			ctx.startActivity(intent);*/
			((Activity) ctx).finish();
		}
		this.notifyDataSetChanged();
	}
	
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if (holder == null) {
			convertView = inflater.inflate(R.layout.send_msg_item, null);
			holder = new ViewHolder();
			holder.userMsg = (TextView) convertView.findViewById(R.id.usermsg);
			holder.del = (Button) convertView.findViewById(R.id.user_del);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}
		
		SendItemBean sendItemBean = list.get(position);
		String userMsg = sendItemBean.getUserMsg();
		
		holder.userMsg.setText(userMsg);
		
		final int pst = position;
		//联系人单个删除
		holder.del.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				removeItem(pst);
			}
		});
		
		return convertView;
	}
	
	public SendMsgAdapter(Context context, List<SendItemBean> list) {
		this.ctx = context;
		this.inflater = LayoutInflater.from(context);
		this.list = list;
	}
	
	private static class ViewHolder {
		TextView userMsg;
		Button del;
	}
	
	public static String[] getUserItem() {
		return userItem;
	}
	public static void setUserItem(String[] userItem) {
		SendMsgAdapter.userItem = userItem;
	}
	
}


菜鸟一个,额,大致如上了, 有不对的 地方敬请指教,求调教哦!奋斗




版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Android开发之Activity转场动画
引子 相信开发过iOS的程序员都知道iOS ViewController之间的跳转动画非常多,很酷对不对?这让开发Android的羡慕不已,曾几何时,Android中的Activity跳转是何等的生硬,But,在Android 5.0以后,Google也为Activity的转场设计了更加友好的动画效果。
767 0
+关注
西游不取经
其实呢,我只是一个前端
42
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载