Android短彩信源码解析-短信发送流程(二)
转载请注明出处:http://blog.csdn.net/droyon/article/details/11699935
2,短彩信发送framework逻辑
短信在SmsSingleRecipientSender.java中包装了SentIntents,以及DeliveryIntents,信息的内容在message中,信息的目的发送地址在mDest中,然后调用下面的代码进行信息的发送
smsManager.sendMultipartTextMessage(mDest, mServiceCenter, messages, sentIntents, deliveryIntents);
smsMessager对应的类为:SmsManager.java
2.1 进入SmsManager.java
public void sendMultipartTextMessage(
String destinationAddress, String scAddress, ArrayList<String> parts,
ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
}
if (parts == null || parts.size() < 1) {
throw new IllegalArgumentException("Invalid message body");
} if (parts.size() > 1) {
try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
if (iccISms != null) {
iccISms.sendMultipartText(destinationAddress, scAddress, parts,
sentIntents, deliveryIntents);
}
} catch (RemoteException ex) {
// ignore it
}
} else {
PendingIntent sentIntent = null;
PendingIntent deliveryIntent = null;
if (sentIntents != null && sentIntents.size() > 0) {
sentIntent = sentIntents.get(0);
}
if (deliveryIntents != null && deliveryIntents.size() > 0) {
deliveryIntent = deliveryIntents.get(0);
}
sendTextMessage(destinationAddress, scAddress, parts.get(0),
sentIntent, deliveryIntent);
}
}
在这个类中,主要是根据parts的数量,进行跨进程调用ISms服务。如果是多条信息,执行:
iccISms.sendMultipartText(destinationAddress, scAddress, parts,
sentIntents, deliveryIntents);
这里调用的ISms服务的sendMultipartText方法。
如果是单条信息,执行sendTextMessage
public void sendTextMessage(
String destinationAddress, String scAddress, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
if (TextUtils.isEmpty(destinationAddress)) {
throw new IllegalArgumentException("Invalid destinationAddress");
} if (TextUtils.isEmpty(text)) {
throw new IllegalArgumentException("Invalid message body");
} try {
ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
if (iccISms != null) {
iccISms.sendText(destinationAddress, scAddress, text, sentIntent, deliveryIntent);
}
} catch (RemoteException ex) {
// ignore it
}
}
同样的,要调用ISms服务。只不过方法变成了Isms服务的sendText方法
关于iSms服务的注册,它是在初始化phone进程时注册的
public IccSmsInterfaceManagerProxy(IccSmsInterfaceManager
iccSmsInterfaceManager) {
this.mIccSmsInterfaceManager = iccSmsInterfaceManager;
if(ServiceManager.getService("isms") == null) {
ServiceManager.addService("isms", this);
}
}
然后我们进到IccSmsInterfaceManagerProxy.java中
2.2 IccSmsManagerProxy.java
public void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mIccSmsInterfaceManager.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
} public void sendMultipartText(String destAddr, String scAddr,
List<String> parts, List<PendingIntent> sentIntents,
List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
mIccSmsInterfaceManager.sendMultipartText(destAddr, scAddr,
parts, sentIntents, deliveryIntents);
}
代理类中进行实现
2.3 iccSmsManager.java
public void sendMultipartText(String destAddr, String scAddr, List<String> parts,
List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) {
mPhone.getContext().enforceCallingPermission(
"android.permission.SEND_SMS",
"Sending SMS message");
if (Log.isLoggable("SMS", Log.VERBOSE)) {
int i = 0;
for (String part : parts) {
log("sendMultipartText: destAddr=" + destAddr + ", srAddr=" + scAddr +
", part[" + (i++) + "]=" + part);
}
}
mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
(ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents);
}
public void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
mPhone.getContext().enforceCallingOrSelfPermission(
"android.permission.SEND_SMS",
"Sending SMS message");
if (Log.isLoggable("SMS", Log.VERBOSE)) {
log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
" text='"+ text + "' sentIntent=" +
sentIntent + " deliveryIntent=" + deliveryIntent);
}
mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent);
}
最终他们都要调到mDispatcher的sendText或者sendMultipartText方法,mDispatcher的原型类:SMSDispatcher,它有两个子类,GsmSmsDispatcher.java以及CdmaSmsDispatcher.java
2.4,GsmSmsDispatcher.java
protected void sendMultipartText(String destAddr, String scAddr,
ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
ArrayList<PendingIntent> deliveryIntents) { int refNumber = getNextConcatenatedRef() & 0x00FF;
int msgCount = parts.size();
int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; mRemainingMessages = msgCount; TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount];
for (int i = 0; i < msgCount; i++) {
TextEncodingDetails details = calculateLength(parts.get(i), false);
if (encoding != details.codeUnitSize
&& (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN
|| encoding == android.telephony.SmsMessage.ENCODING_7BIT)) {
encoding = details.codeUnitSize;
}
encodingForParts[i] = details;
} for (int i = 0; i < msgCount; i++) {
SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef();
concatRef.refNumber = refNumber;
concatRef.seqNumber = i + 1; // 1-based sequence
concatRef.msgCount = msgCount;
// TODO: We currently set this to true since our messaging app will never
// send more than 255 parts (it converts the message to MMS well before that).
// However, we should support 3rd party messaging apps that might need 16-bit
// references
// Note: It's not sufficient to just flip this bit to true; it will have
// ripple effects (several calculations assume 8-bit ref).
concatRef.isEightBits = true;
SmsHeader smsHeader = new SmsHeader();
smsHeader.concatRef = concatRef; // Set the national language tables for 3GPP 7-bit encoding, if enabled.
if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) {
smsHeader.languageTable = encodingForParts[i].languageTable;
smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable;
} PendingIntent sentIntent = null;
if (sentIntents != null && sentIntents.size() > i) {
sentIntent = sentIntents.get(i);
} PendingIntent deliveryIntent = null;
if (deliveryIntents != null && deliveryIntents.size() > i) {
deliveryIntent = deliveryIntents.get(i);
} sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding,
sentIntent, deliveryIntent, (i == (msgCount - 1)));
} }
遍历所有的短信part,然后执行sendNewSubmitPdu方法,这个方法在SmsDispatcher.java中是abstract的,真正的实现在其子类中:
protected void sendNewSubmitPdu(String destinationAddress, String scAddress,
String message, SmsHeader smsHeader, int encoding,
PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
if (pdu != null) {
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
} else {
Log.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
}
}
实现方式为执行sendRawPdu。
以上是sendMultipartsText,让我们看看sendText方法在SmsDispatcher.java中的实现:
protected abstract void sendText(String destAddr, String scAddr,
String text, PendingIntent sentIntent, PendingIntent deliveryIntent);
SmsDispatcher.java的设计方法为让其子类来决定sendText方法的实现方式,现贴出sendText的源码实现:
protected void sendText(String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
scAddr, destAddr, text, (deliveryIntent != null));
if (pdu != null) {
sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent);
} else {
Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
}
}
我们看到其实现方式为执行sendRawPdu,到这里也就是说,无论是sendText还是sendMultipartsText方法,他们归根结底都是执行sendRawPdu方法,不同的是sendMultipartsText是遍历所有的短信parts,然后在调用sendRawPdu。
在这个方法的前一步会将信息的内容,要发送的目的地址,当前的系统时间等打包成SubmitPdu,关于短信pdu这部分,会稍后介绍,我们现在主要解析一下短信framework发送的主要逻辑。
下面我们看看sendRawPdu的方法实现:
protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
PendingIntent deliveryIntent) {
if (mSmsSendDisabled) {
if (sentIntent != null) {
try {
sentIntent.send(RESULT_ERROR_NO_SERVICE);
} catch (CanceledException ex) {}
}
Log.d(TAG, "Device does not support sending sms.");
return;
} if (pdu == null) {
if (sentIntent != null) {
try {
sentIntent.send(RESULT_ERROR_NULL_PDU);
} catch (CanceledException ex) {}
}
return;
} HashMap<String, Object> map = new HashMap<String, Object>();
map.put("smsc", smsc);
map.put("pdu", pdu); SmsTracker tracker = new SmsTracker(map, sentIntent,
deliveryIntent);
int ss = mPhone.getServiceState().getState(); if (ss != ServiceState.STATE_IN_SERVICE) {
handleNotInService(ss, tracker);
} else {
String appName = getAppNameByIntent(sentIntent);
if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) {
sendSms(tracker);
} else {
sendMessage(obtainMessage(EVENT_POST_ALERT, tracker));
}
}
}
在这个方法里,首先会检查一些状态,例如pdu是否为null,smsSendDisabled等,然后包装一个SmsTracher对象,在发送前还要检查一下Phone进程的状态,是否处于“离线”状态,关于这个状态,moderm会根据当前所处的信号强度,做出改变。
如果当前处于服务中,那么就可以进行我们短信的发送了,发送调用sendMessage或者sendSms进行短信的发送,关于sendMessage这个方法,参数为Message,message生成代码:
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
也就是说这个message是和Handler相关的message。
PS:我不知道大家有没有注意,其实SmsDispatcher.java这个类就是个Handler。
在发送之前,会调用:
mUsageMonitor.check(appName, SINGLE_PART_SMS)
进行检查,所作的事情其实就是检查在一段时间内,等待发送的短信数目不超过MAX值,关于等待时间以及MAX数目:
SmsUsageMonitor.java
/** Default checking period for SMS sent without user permission. */
private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; /** Default number of SMS sent in checking period without user permission. */
private static final int DEFAULT_SMS_MAX_COUNT = 100;
回到短信发送中,在此处,检查通过,故短信发送执行sendSms(tracker)
protected void sendSms(SmsTracker tracker) {
HashMap<String, Object> map = tracker.mData; byte smsc[] = (byte[]) map.get("smsc");
byte pdu[] = (byte[]) map.get("pdu"); Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);
}
其中mCm对象是CommandInterface的引用
protected final CommandsInterface mCm;
很多人对它不是很熟悉,但一定很熟悉它的其中一个子类,那就是RIL.java。
也就是说,短信发送流程,在此处会调用RIL.java,执行sendSMS方法,参数为smsc的pdu字符串,短信内容以及时间等的pdu,以及一个Message,WHAT值为EVENT_SEND_SMS_COMPLETE,tracher对象作为其Object。我们进入到RIL.java中
2.5,RIL.java
public void
sendSMS (String smscPDU, String pdu, Message result) {
RILRequest rr
= RILRequest.obtain(RIL_REQUEST_SEND_SMS, result); rr.mp.writeInt(2);
rr.mp.writeString(smscPDU);
rr.mp.writeString(pdu); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr);
}
关于RIL.java,它会和reference_ril.c进行配合,进行at指令的发送以及接收反馈等信息。reference_ril.c文件会被编译到reference-ril.so中。
在此处,RIL.java会将相应的pdu等信息打包,然后通过socket发送到reference_ril.c中,相关处理如下:
2.6,reference_ril.c
static void requestSendSMS(void *data, size_t datalen, RIL_Token t)
{
int err;
const char *smsc;
const char *pdu;
int tpLayerLength;
char *cmd1, *cmd2;
RIL_SMS_Response response;
ATResponse *p_response = NULL; smsc = ((const char **)data)[0];
pdu = ((const char **)data)[1]; tpLayerLength = strlen(pdu)/2; // "NULL for default SMSC"
if (smsc == NULL) {
smsc= "00";
} asprintf(&cmd1, "AT+CMGS=%d", tpLayerLength);
asprintf(&cmd2, "%s%s", smsc, pdu); err = at_send_command_sms(cmd1, cmd2, "+CMGS:", &p_response); if (err != 0 || p_response->success == 0) goto error; memset(&response, 0, sizeof(response)); /* FIXME fill in messageRef and ackPDU */ RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
at_response_free(p_response); return;
error:
RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
at_response_free(p_response);
}
调用AT命令,cmgs将信息发往moderm,进行相关流程处理。
最后,如果短信成功发送出去,还记得我们在发送时,从GsmSmsDispatcher.java传递到RIL.java中的Message吗,在发送之后,RIL.java会回调此Message,这个Message如下:
Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
前面说了,What = EVENT_SEND_SMS_COMPLETE,Object = tricker。我们之前也说了SmsDispacher.java以及其子类是Handler,那么这个Message在那里处理的那?答案是在SmsDispacher.java中,处理逻辑代码如下:
case EVENT_SEND_SMS_COMPLETE:
// An outbound SMS has been successfully transferred, or failed.
handleSendComplete((AsyncResult) msg.obj);
break;
protected void handleSendComplete(AsyncResult ar) {
SmsTracker tracker = (SmsTracker) ar.userObj;
PendingIntent sentIntent = tracker.mSentIntent; if (ar.exception == null) {
if (false) {
Log.d(TAG, "SMS send complete. Broadcasting "
+ "intent: " + sentIntent);
} if (tracker.mDeliveryIntent != null) {//wanghailu,hailushijie@163.com ,hlwang
// Expecting a status report. Add it to the list.
int messageRef = ((SmsResponse)ar.result).messageRef;
tracker.mMessageRef = messageRef;
deliveryPendingList.add(tracker);
} if (sentIntent != null) {
try {
if (mRemainingMessages > -1) {
mRemainingMessages--;
} if (mRemainingMessages == 0) {
Intent sendNext = new Intent();
sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true);
sentIntent.send(mContext, Activity.RESULT_OK, sendNext);
} else {
sentIntent.send(Activity.RESULT_OK);
}
} catch (CanceledException ex) {}
}
} else {
if (false) {
Log.d(TAG, "SMS send failed");
} int ss = mPhone.getServiceState().getState(); if (ss != ServiceState.STATE_IN_SERVICE) {
handleNotInService(ss, tracker);
} else if ((((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.SMS_FAIL_RETRY) &&
tracker.mRetryCount < MAX_SEND_RETRIES) {
// Retry after a delay if needed.
// TODO: According to TS 23.040, 9.2.3.6, we should resend
// with the same TP-MR as the failed message, and
// TP-RD set to 1. However, we don't have a means of
// knowing the MR for the failed message (EF_SMSstatus
// may or may not have the MR corresponding to this
// message, depending on the failure). Also, in some
// implementations this retry is handled by the baseband.
tracker.mRetryCount++;
Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker);
sendMessageDelayed(retryMsg, SEND_RETRY_DELAY);
} else if (tracker.mSentIntent != null) {
int error = RESULT_ERROR_GENERIC_FAILURE; if (((CommandException)(ar.exception)).getCommandError()
== CommandException.Error.FDN_CHECK_FAILURE) {
error = RESULT_ERROR_FDN_CHECK_FAILURE;
}
// Done retrying; return an error to the app.
try {
Intent fillIn = new Intent();
if (ar.result != null) {
fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode);
}
if (mRemainingMessages > -1) {
mRemainingMessages--;
} if (mRemainingMessages == 0) {
fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true);
} tracker.mSentIntent.send(mContext, error, fillIn);
} catch (CanceledException ex) {}
}
}
}
至此,短信发送流程大致如此,短信接收流程中framework部分的处理大致是发送流程的反方向。
首先检查AsyncResult对象中是否存在异常,如果成功发送的信息,那么不存在异常,如果发送失败,那么是存在Exception的,会进行异常的相应的逻辑处理,大体流程相似,故本处介绍无异常时的流程逻辑。
无论发送成功还是失败,大体都是执行tricker对象中的mSentIntents,这是一个PendingIntent,执行send会发送此广播,那么我们的上层应用Mms中的SmsReceiverService.java会收到这个广播,并进行相应的逻辑处理,逻辑代码大体如下:
private void handleSmsSent(Intent intent, int error) {
Uri uri = intent.getData();
mSending = false;
boolean sendNextMsg = intent.getBooleanExtra(EXTRA_MESSAGE_SENT_SEND_NEXT, false); if (LogTag.DEBUG_SEND) {
Log.v(TAG, "handleSmsSent uri: " + uri + " sendNextMsg: " + sendNextMsg +
" mResultCode: " + mResultCode +
" = " + translateResultCode(mResultCode) + " error: " + error);
} if (mResultCode == Activity.RESULT_OK) {
if (LogTag.DEBUG_SEND || Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "handleSmsSent move message to sent folder uri: " + uri);
}
if (!Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_SENT, error)) {
Log.e(TAG, "handleSmsSent: failed to move message " + uri + " to sent folder");
}
if (sendNextMsg) {
sendFirstQueuedMessage();
} // Update the notification for failed messages since they may be deleted.
MessagingNotification.updateSendFailedNotification(this);
} else if ((mResultCode == SmsManager.RESULT_ERROR_RADIO_OFF) ||
(mResultCode == SmsManager.RESULT_ERROR_NO_SERVICE)) {
if (Log.isLoggable(LogTag.TRANSACTION, Log.VERBOSE)) {
Log.v(TAG, "handleSmsSent: no service, queuing message w/ uri: " + uri);
}
// We got an error with no service or no radio. Register for state changes so
// when the status of the connection/radio changes, we can try to send the
// queued up messages.
registerForServiceStateChanges();
// We couldn't send the message, put in the queue to retry later.
Sms.moveMessageToFolder(this, uri, Sms.MESSAGE_TYPE_QUEUED, error);
mToastHandler.post(new Runnable() {
public void run() {
Toast.makeText(SmsReceiverService.this, getString(R.string.message_queued),
Toast.LENGTH_SHORT).show();
}
});
} else if (mResultCode == SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE) {
mToastHandler.post(new Runnable() {
public void run() {
Toast.makeText(SmsReceiverService.this, getString(R.string.fdn_check_failure),
Toast.LENGTH_SHORT).show();
}
});
} else {
messageFailedToSend(uri, error);
if (sendNextMsg) {
sendFirstQueuedMessage();
}
}
}
主要是根据ResultCode进行一些逻辑处理,比如如果发送成功,那么会首先更新短信由待发送变为已发送状态,并且更新Notification等。
如果需要发送报告,那么相关的逻辑大体相似,不再详细介绍。
Ps:发送成功,回调会从moderm那里带回来一个MessageRef,这是一个int值,很重要噢。
短信发送framework层逻辑大体介绍到这里,framework层的短彩信逻辑还有好多,比如接收信息的流程,或者接收长短信的流程等,不再一一介绍了。
Android短彩信源码解析-短信发送流程(二)的更多相关文章
- Android短彩信源码解析-短信发送流程(三)
3.短信pdu的压缩与封装 相关文章: ------------------------------------------------------------- 1.短信发送上层逻辑 2.短信发送f ...
- Android 开源项目源码解析(第二期)
Android 开源项目源码解析(第二期) 阅读目录 android-Ultra-Pull-To-Refresh 源码解析 DynamicLoadApk 源码解析 NineOldAnimations ...
- Android MIFARE NFCA源码解析
Android MIFARE NFCA源码解析TagTechnology定义了所有标签的共有接口类BasicTagTechnology 实现了TagTechnology的一些接口 再有具体的标签协议继 ...
- 【Android】EventBus 源码解析
EventBus 源码解析 本文为 Android 开源项目实现原理解析 中 EventBus 部分项目地址:EventBus,分析的版本:ccc2771,Demo 地址:EventBus Demo分 ...
- [原创]android开源项目源码解析(一)----CircleImageView的源码解析
CircleImageView的代码很简洁,因此先将此工程作为源码解析系列的第一篇文章. 解析说明都在代码里了. /* * Copyright 2014 - 2015 Henning Dodenhof ...
- Android 数据库 ObjectBox 源码解析
一.ObjectBox 是什么? greenrobot 团队(现有 EventBus.greenDAO 等开源产品)推出的又一数据库开源产品,主打移动设备.支持跨平台,最大的优点是速度快.操作简洁,目 ...
- 史上最详细的Android消息机制源码解析
本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...
- Mybatis 系列10-结合源码解析mybatis 的执行流程
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- 时序数据库 Apache-IoTDB 源码解析之系统架构(二)
上一章聊到时序数据是什么样,物联网行业中的时序数据的特点:存量数据大.新增数据多(采集频率高.设备量多).详情请见: 时序数据库 Apache-IoTDB 源码解析之前言(一) 打一波广告,欢迎大家访 ...
随机推荐
- zoj 3706 Break Standard Weight
/*题意:将两个砝码中的其中一个分成两块,三块组合最多有几种情况(可以只有一块,或者两块). 组合情况 i j m 三块砝码 (i+j)-m=m-(i+j) i+j i-j=j-i i j m (i ...
- ajax的get与post提交方式
Get方式的用户名验证 1.编写html代码 <form> 用户名[GET]:<input id="usernameID" type="text&quo ...
- struts的MVC详细实现
struts中核心类:ActionServlet,其中最主要的操作是RequestProcessor类中的process方法. struts实现mvc的详解图如下: 1.Tomcat一启动,根据web ...
- Jquery对select的操作(附日历天数变化代码)
转载请注明出处. 逃不开传统的四种操作:增.删.改.查. <四处搜刮了jquery对select操作的代码,汇集一下,方便以后查看.日历天数变化代码为原创.> [增]: $("# ...
- 帝国cms7.0 列表模板调用本栏目缩略图
[e:loop={"select classimg from phome_enewsclass where classid='$GLOBALS[navclassid]'",1,24 ...
- 开源项目live555学习心得
推荐:伊朗美女找丈夫比找工作难女人婚前一定要看清三件事 × 登录注册 疯狂少男-IT技术的博客 http://blog.sina.com.cn/crazyboyzhaolei [订阅][手机订 ...
- 关于GROUP BY的应用
前面收藏了别人的SQL语句操作,可是没有实战,也未知学的如何 正好今天有个事需要做一下 (sql server 2000) 三个表:stuInf,sType,sinInf分别为学生信息表,类型表,信息 ...
- 概率图模型(PGM)学习笔记(三)模式判断与概率图流
我们依旧使用"学生网络"作为样例,如图1. 图1 首先给出因果判断(Causal Reasoning)的直觉解释. 能够算出来 即学生获得好的推荐信的概率大约是0.5. 但假设我们 ...
- HDU 4705 Y
Y Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Total Submis ...
- C++设计模式之状态模式(四)
4.状态模式总结 状态模式将一个对象在不同状态下的不同行为封装在一个个状态类中,通过设置不同的状态对象能够让环境对象拥有不同的行为.而状态转换的细节对于client而言是透明的.client不直接操作 ...