最近做个业务系统,需要在android手机上通过代码后台发送个报告。其实一开始是用短信实现的,很简单,网上源码多的是。但有个问题:短信的长度是受限的,全是半角的话最多140个字符,如果有汉字或者全角字符,就变成63个。一份报告,会被拆分成好几条短信。但这还不是最严重的问题,更让人不可忍受的是:报告被拆分成几条短信后,虽然是顺序发送(这里指的是代码按拆分顺序将短信依次压入系统短信数据库),但其实发送的时候并不按照顺序来,或者说,从表象上看,接收方收到的短信基本不按顺序来,这使阅读报告时非常别扭,得人肉恢复顺序!这个确实不能忍。
后来想了想,大概有以下几种办法解决:
第一种:使用传说中的“长短信”协议发送短信。好像这玩意儿还得接收方支持。这个只听说过,没实际用过,直接pass了。
第二种:在接收方手机上内置一个专门的短信接收程序,说白了就是实现一套自己的“长短信”发送和接收协议。发送的时候在短信内部包含了短信标识(用此标识将用于发送报告的短信和普通短信区分开)、短信总数和短信编号。接收程序监听收到短信事件并对短信标识进行判断,只拦截用于发送报告的短信。在感兴趣的所有短信都收齐之后将内容提取出来生成完整的报告。这个也pass了。一来以前实现过这玩意儿,感觉也不是很好玩,另外,部署系统的时候还需要多安装一个程序,也不太喜欢。
第三种:用彩信发送报告。这个在长度上就基本没什么限制了,而且还能带多媒体数据。虽然目前只发送文本数据,但保不齐将来想添上图片、视频啥的。所以就决定用彩信了。
用彩信发送报告。动手之后才发现,事情远远不像想象的那么简单。主要是因为android从4.0(好像是)以后,已经不允许应用获得系统权限,这个限制造成要想发送彩信,只能通过系统自带的短信发送程序来实现。其实这个跟发短信就一样了,自己的代码生成彩信,调用api将其压入系统彩信数据库。系统中内置的彩信发送应用轮询到以后将彩信取出然后发送出去。也就是说,所谓的“后台发送”,只能在4.0以下实现,更高版本都报“没有权限”的错误。网上的帖子都是这个路子。
在csdn上发帖子问了几天,可能因为是过年吧,没人理我。自己每天上去顶一次,也没效果,呵呵,就只能自力更生了。
google想办法,这种事儿,baidu就无能为力了:)。弄了几天终于基本上实现了。下面大概说下细节。
开发环境用的是eclipse+adt。
实现方法参考了几个帖子:
http://www.cnblogs.com/rayray/archive/2013/03/18/Android_MMS.html
这些帖子中涉及到以下几个库、项目:
droidprism.jar
jsoup.jar
GitHub上的项目https://github.com/klinker41/android-smsmms(这里简称为‘项目X’)
从目前个人的理解上,这种方法的核心在于彩信实际上是一种带有特殊格式的email,因此,需要做的就是正确构造这个email,并且能够获取发送通道和目的地。因为细节还没完全弄清楚,所以就只说明怎么做了。
1、创建一个android app项目(就叫SendMMS吧),弄个主Activity,放一个Send按钮上去之类的。这属于准备工作了。
2、将项目X的源码下载下来并导入到eclipse中。需要注意的是导入后其实是两个项目,都是库。将它们都加入SendMMS项目中。
3、在SendMMS项目的构建路径上添加droidprism.jar和jsoup.jar,并在“排序和导出”选项卡中将它们打上勾。
4、给SendMMS项目添加权限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_MMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_MMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.provider.Telephony.SMS_RECEIVED" />
5、import部分
import com.droidprism.APN;
import com.droidprism.Carrier;
import com.klinker.android.send_message.Message;
import com.klinker.android.send_message.Settings;
import com.klinker.android.send_message.Transaction;
6、发送代码。以下是用一个线程发送彩信的代码,直接在Send按钮的点击事件中调用即可。
注意:
1、不需要参数imagePath、audioPath时,不要用null,而要用“”。其实,我还没试验过这俩参数......
2、要把手机“移动网络”中的“启用数据网络”打开。如果没打开,那么彩信将无法立即发送。打开后会自动发送出去;
private void sendMMS(final Context context, final String phone, final String subject, final String message, String imagePath, String audioPath)
{
new Thread()
{
@Override
public void run()
{
try
{
Carrier carrier = null;
APN apn = null;
Settings sendSettings = new Settings();
TelephonyManager tel = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = tel.getNetworkOperator();
if (networkOperator != null)
{
int mcc = Integer.parseInt(networkOperator.substring(0, 3));
String s = networkOperator.substring(3);
int mnc = Integer.parseInt(s);
carrier = Carrier.getCarrier(mcc, mnc);
carrier.getsmsemail();
apn = carrier.getAPN();
}
sendSettings.setMmsc(apn.mmsc);
sendSettings.setProxy(apn.proxy);
sendSettings.setPort(Integer.valueOf(apn.port).toString());
sendSettings.setGroup(true);
sendSettings.setDeliveryReports(false);
sendSettings.setSplit(false);
sendSettings.setSplitCounter(false);
sendSettings.setStripUnicode(false);
sendSettings.setSignature("");
sendSettings.setSendLongAsMms(true);
sendSettings.setSendLongAsMmsAfter(3);
sendSettings.setRnrSe(null);
Transaction sendTransaction = new Transaction(instance, sendSettings);
Message mMessage = new Message(message, phone);
mMessage.setSubject(subject);
mMessage.setType(Message.TYPE_SMSMMS);
sendTransaction.sendNewMessage(mMessage, 0);
} catch (Exception e)
{
e.printStackTrace();
}
};
}.start();
}
遗留问题:
1、从项目X的说明中看,它目前还处于beta状态,不是很稳定。使用中也发现似乎缺一些异常处理的部分,所以发送时会报错,出那个“应用已经停止运行”的框框,但经过几十次测试,虽然报错但不影响发送。猜测可能会影响对发送结果的检测,这个还没仔细研究;
2、目前还没研究获取发送结果的代码,同时,发送后会在发件箱里留有一个副本。如果要自动删除副本的话,需要在获取发送结果后进行操作;
3、还没仔细研究引用库和项目的License;
4、还在寻找更好的办法;