思路:将当前个人位置(经纬度信息)存储到手机数据库(SQLITE)中,然后遍历数据库中的所有位置信息,并绘制到MAP图层上。
1.数据库帮助类:
package com.example.touchpoint; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class MysqliteHelper extends SQLiteOpenHelper{ private static final String DATABASE_NAME = "my.db"; private static final int DATABASE_VERSION = 1; public static final String COLUMN_ID = "_id"; //必须的 public static final String COLUMN_LATITUDE = "latitude"; public static final String COLUMN_LONGITUDE = "longitude"; public static final String COLUMN_UPLOAD_TIME = "upload_time"; //暂时没用 public static final String[] allColumns = {COLUMN_ID,COLUMN_LATITUDE,COLUMN_LONGITUDE}; private static final String CREATE_TABLE = "create table if not exists routlist" + "(" + COLUMN_ID + " integer primary key autoincrement, " + COLUMN_LATITUDE + " REAL not null, " + COLUMN_LONGITUDE + " REAL not null " + ");"; public MysqliteHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { Log.d("CREATE_TABLE",CREATE_TABLE); db.execSQL(CREATE_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("drop table " + CREATE_TABLE); onCreate(db); } }
2.数据库DAO
package com.example.touchpoint; import java.util.ArrayList; import java.util.List; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.util.Log; public class SqliteDAO { private SQLiteDatabase db; private MysqliteHelper helper; public SqliteDAO(Context context) { helper = new MysqliteHelper(context); } public void open() { db = helper.getWritableDatabase(); } public void close() { helper.close(); } public void writePositionToDB(double latitude,double longitude) { ContentValues values = new ContentValues(); values.put(MysqliteHelper.COLUMN_LATITUDE, latitude); values.put(MysqliteHelper.COLUMN_LONGITUDE, longitude); db.insert("routlist", null, values); } public List<LocationPoint> getAllPoints() { List<LocationPoint> points = new ArrayList<LocationPoint>(); Cursor cursor = db.query("routlist", MysqliteHelper.allColumns, null, null, null, null, null); cursor.moveToFirst(); while (!cursor.isAfterLast()) { LocationPoint point = cursorToPoint(cursor); points.add(point); cursor.moveToNext(); } // 记得关闭cursor对象 cursor.close(); return points; } private LocationPoint cursorToPoint(Cursor cursor) { LocationPoint point = new LocationPoint(); point.set_id(cursor.getLong(0)); point.setLatitude(cursor.getDouble(1)); point.setLongitutde(cursor.getDouble(2)); Log.d("route",point.toString()); return point; } }
3.POJO类:
package com.example.touchpoint; public class LocationPoint { private long _id; private double latitude; private double longitutde; public long get_id() { return _id; } public void set_id(long _id) { this._id = _id; } public double getLatitude() { return latitude; } public void setLatitude(double latitude) { this.latitude = latitude; } public double getLongitutde() { return longitutde; } public void setLongitutde(double longitutde) { this.longitutde = longitutde; } @Override public String toString() { return "LocationPoint [_id=" + _id + ", latitude=" + latitude + ", longitutde=" + longitutde + "]"; } }
4.ItemizedOverlay类
package com.example.touchpoint; import java.util.ArrayList; import java.util.List; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.util.Log; import com.amap.mapapi.core.GeoPoint; import com.amap.mapapi.core.OverlayItem; import com.amap.mapapi.map.ItemizedOverlay; import com.amap.mapapi.map.MapView; import com.amap.mapapi.map.Projection; public class RouteItemizedOverlay extends ItemizedOverlay<OverlayItem>{ private List<OverlayItem> list = new ArrayList<OverlayItem>(); public RouteItemizedOverlay(Drawable d) { super(boundCenterBottom(d)); } @Override protected OverlayItem createItem(int id) { return list.get(id); } @Override public int size() { return list.size(); } @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { Projection proj = mapView.getProjection(); Path path = new Path(); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setColor(Color.BLUE); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(3); for (int i = 0; i < this.size(); i++) { OverlayItem item = (OverlayItem)createItem(i); GeoPoint gp = item.getPoint(); Log.d("aa",gp.getLatitudeE6() +" " + gp.getLongitudeE6()); Point mPoint = new Point(); proj.toPixels(gp, mPoint); if(i == 0) { path.moveTo(mPoint.x, mPoint.y); Log.d("path","[" + mPoint.x + "," + mPoint.y+ "]"); }else{ path.lineTo(mPoint.x, mPoint.y); Log.d("path","[" + mPoint.x + "," + mPoint.y+ "]"); } //canvas.drawText("ca", mPoint.x, mPoint.y, new Paint()); } path.close(); canvas.drawPath(path, paint); super.draw(canvas, mapView, shadow); } public void addMyRoute(List<LocationPoint> points) { for(LocationPoint point : points) { GeoPoint gp = new GeoPoint((int)(point.getLatitude()*1E6),(int)(point.getLongitutde()*1E6)); OverlayItem myItem = new OverlayItem(gp, "title", "snippet"); list.add(myItem); Log.d("add","add an item"); populate(); } } }
5.MainActivity类:
package com.example.touchpoint; import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Point; import android.graphics.drawable.Drawable; import android.location.Location; import android.location.LocationListener; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import com.amap.mapapi.core.GeoPoint; import com.amap.mapapi.location.LocationManagerProxy; import com.amap.mapapi.location.LocationProviderProxy; import com.amap.mapapi.map.MapActivity; import com.amap.mapapi.map.MapController; import com.amap.mapapi.map.MapView; import com.amap.mapapi.map.MyLocationOverlay; import com.amap.mapapi.map.Overlay; import com.amap.mapapi.map.Projection; public class MainActivity extends MapActivity implements LocationListener { private MapView mMapView; private MapController controller; private List<Overlay> mapOverlayList; private LocationManagerProxy locationProxy; private SqliteDAO dao; private RouteItemizedOverlay routeOverlay; private MyOverlay overlay; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mMapView = (MapView) findViewById(R.id.main_mapView); mMapView.setBuiltInZoomControls(true); // 开启缩放控件 mMapView.setSatellite(true); // 设置为卫星模式 controller = mMapView.getController(); controller.setZoom(3); mapOverlayList = mMapView.getOverlays(); locationProxy = LocationManagerProxy.getInstance(this); Drawable d = getResources().getDrawable(R.drawable.new_func_dot); routeOverlay = new RouteItemizedOverlay(d); dao = new SqliteDAO(this); } @Override protected void onPause() { locationProxy.removeUpdates(this); dao.close(); super.onPause(); } @Override protected void onResume() { locationProxy.requestLocationUpdates( LocationProviderProxy.MapABCNetwork, 0, 0, this); dao.open(); super.onResume(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.showroute: showRoutlist(); break; case R.id.closeroute: break; default: break; } return super.onOptionsItemSelected(item); } private void showRoutlist() { mapOverlayList.remove(overlay); locationProxy.removeUpdates(this); // 取消位置监听(数据库不会有更新) routeOverlay.addMyRoute(dao.getAllPoints()); mapOverlayList.add(routeOverlay); } public void closeRoute() { mapOverlayList.clear(); // 清空路线 Location last = locationProxy .getLastKnownLocation(LocationProviderProxy.MapABCNetwork); mapOverlayList.add(new MyOverlay(this, new GeoPoint((int) (last .getLatitude() * 1E6), (int) (last.getLongitude() * 1E6)))); locationProxy.requestLocationUpdates(LocationManagerProxy.GPS_PROVIDER, 0, 0, this); // 开始监听位置变化 } public void onLocationChanged(Location location) { mapOverlayList.clear(); if (location != null) { double latitude = location.getLatitude(); double longitude = location.getLongitude(); dao.writePositionToDB(latitude, longitude); // 写进数据库 GeoPoint gp = new GeoPoint((int) (latitude * 1E6), (int) (longitude * 1E6)); overlay = new MyOverlay(this, gp); mapOverlayList.add(overlay); } } public void onStatusChanged(String provider, int status, Bundle extras) { } public void onProviderEnabled(String provider) { } public void onProviderDisabled(String provider) { } } class MyOverlay extends Overlay { private Context mContext; private GeoPoint gp; private Bitmap bitmap; public MyOverlay(Context mContext, GeoPoint gp) { this.mContext = mContext; this.gp = gp; bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.new_func_dot); } @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { Projection proj = mapView.getProjection(); Point mPoint = proj.toPixels(gp, null); canvas.drawBitmap(bitmap, mPoint.x, mPoint.y, null); super.draw(canvas, mapView, shadow); } }
用到的图片:
项目文件在:http://download.csdn.net/detail/czjuttsw/4861690
[Symptom from logcat]
11-26 22:02:37.859 421 487 D ConnectivityService: ConnectivityChange for mobile: DISCONNECTED/DISCONNECTED
11-26 22:02:37.859 421 682 D NetdConnector: RMV <- {200 39372 Route added to default table}
11-26 22:02:37.859 421 484 D StateMachine: handleMessage: E msg.what=131085
11-26 22:02:37.859 421 484 D StateMachine: processMsg: DriverUnloadedState
11-26 22:02:37.859 421 484 D WifiStateMachine: DriverUnloadedState{ what=131085 when=-1ms }
11-26 22:02:37.859 421 487 D ConnectivityService: Attempting to switch to WIFI
11-26 22:02:37.869 421 484 D StateMachine: processMsg: DefaultState
11-26 22:02:37.869 421 484 D WifiStateMachine: DefaultState{ what=131085 when=-5ms }
11-26 22:02:37.869 421 484 D StateMachine: handleMessage: X
11-26 22:02:37.869 421 484 D StateMachine: handleMessage: E msg.what=131147
11-26 22:02:37.869 421 484 D StateMachine: processMsg: DriverUnloadedState
11-26 22:02:37.869 421 484 D WifiStateMachine: DriverUnloadedState{ what=131147 when=-8ms }
11-26 22:02:37.869 421 484 D StateMachine: processMsg: DefaultState
11-26 22:02:37.869 421 484 D WifiStateMachine: DefaultState{ what=131147 when=-11ms }
11-26 22:02:37.869 421 484 D StateMachine: handleMessage: X
11-26 22:02:37.869 421 487 D ConnectivityService: Attempting to switch to BLUETOOTH_TETHER
11-26 22:02:37.869 421 487 D ConnectivityService: handleConnectivityChange: changed linkProperty[0]: doReset=true resetMask=3
11-26 22:02:37.869 421 487 D ConnectivityService: curLp=InterfaceName: rmnet0 LinkAddresses: [192.168.35.117/30,] Routes: [0.0.0.0/0 -> 192.168.35.118,] DnsAddresses: [192.168.212.10,]
11-26 22:02:37.869 421 487 D ConnectivityService: newLp= null
11-26 22:02:37.879 421 682 D ConnectivityService: Adding 192.168.23.5/32 -> 172.11.208.57 for interface rmnet1
11-26 22:02:37.919 499 499 D StatusBar.NetworkController: onDataConnectionStateChanged: state=0 type=0
11-26 22:02:37.929 499 499 E StatusBar.NetworkController: updateDataNetType NETWORK_TYPE_UNKNOWN
11-26 22:02:38.069 421 487 W dalvikvm: threadid=44: thread exiting with uncaught exception (group=0x40c07498)
11-26 22:02:38.149 421 487 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: ConnectivityServiceThread
11-26 22:02:38.149 421 487 E AndroidRuntime: java.util.ConcurrentModificationException
11-26 22:02:38.149 421 487 E AndroidRuntime: at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService.modifyRoute(ConnectivityService.java:1553)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService.modifyRoute(ConnectivityService.java:1528)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService.removeRoute(ConnectivityService.java:1476)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService.updateRoutes(ConnectivityService.java:2252)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService.handleConnectivityChange(ConnectivityService.java:2179)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService.handleDisconnect(ConnectivityService.java:1824)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService.access$1100(ConnectivityService.java:128)
11-26 22:02:38.149 421 487 E AndroidRuntime: at com.android.server.ConnectivityService$MyHandler.handleMessage(ConnectivityService.java:2687)
11-26 22:02:38.149 421 487 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:99)
11-26 22:02:38.149 421 487 E AndroidRuntime: at android.os.Looper.loop(Looper.java:137)
11-26 22:02:38.149 421 487 E AndroidRuntime: at android.os.HandlerThread.run(HandlerThread.java:60)
11-26 22:02:38.189 421 487 W DropBoxManagerService: Dropping: system_server_crash (1098 > 0 bytes)
11-26 22:02:38.209 421 487 E ActivityManager: handleApplicationCrash happeded!! and crash level = 2, trigger panic!!
11-26 22:02:38.209 421 487 E ActivityManager: TriggerKernelPanic IN
[Analysis]
modifyRoute(...) @ ConnectivityService.java
1505 private boolean modifyRoute(String ifaceName, LinkProperties lp, RouteInfo r, int cycleCount,
1506 boolean doAdd, boolean toDefaultTable) {
1507 if ((ifaceName == null) || (lp == null) || (r == null)) {
1508 if (DBG) log("modifyRoute got unexpected null: " + ifaceName + ", " + lp + ", " + r);
1509 return false;
1510 }
1511
1512 if (cycleCount > MAX_HOSTROUTE_CYCLE_COUNT) {
1513 loge("Error modifying route - too much recursion");
1514 return false;
1515 }
1516
1517 if (r.isHostRoute() == false) {
1518 RouteInfo bestRoute = RouteInfo.selectBestRoute(lp.getRoutes(), r.getGateway());
1519 if (bestRoute != null) {
1520 if (bestRoute.getGateway().equals(r.getGateway())) {
1521 // if there is no better route, add the implied hostroute for our gateway
1522 bestRoute = RouteInfo.makeHostRoute(r.getGateway());
1523 } else {
1524 // if we will connect to our gateway through another route, add a direct
1525 // route to it's gateway
1526 bestRoute = RouteInfo.makeHostRoute(r.getGateway(), bestRoute.getGateway());
1527 }
1528 modifyRoute(ifaceName, lp, bestRoute, cycleCount+1, doAdd, toDefaultTable);
1529 }
1530 }
1531 if (doAdd) {
1532 if (VDBG) log("Adding " + r + " for interface " + ifaceName); ----- logout line "11-26 22:02:37.879 421 682 D ConnectivityService: Adding 192.168.23.5/32 -> 172.11.208.57 for interface rmnet1"
1533 try {
1534 if (toDefaultTable) {
1535 mAddedRoutes.add(r); // only track default table - only one apps can effect
1536 if (VDBG) log("Routes in main table - [ " + mAddedRoutes + " ]");
1537 mNetd.addRoute(ifaceName, r);
1538 } else {
1539 mNetd.addSecondaryRoute(ifaceName, r);
1540 }
1541 } catch (Exception e) {
1542 // never crash - catch them all
1543 if (DBG) loge("Exception trying to add a route: " + e);
1544 return false;
1545 }
1546 } else {
1547 // if we remove this one and there are no more like it, then refcount==0 and
1548 // we can remove it from the table
1549 if (toDefaultTable) {
1550 // modified begin
1551 //mAddedRoutes.remove(r);
1552 for (Iterator<RouteInfo> it = mAddedRoutes.iterator(); it.hasNext();) {
1553 RouteInfo thisRI = it.next();
1554 if ( thisRI.equals(r) ) {
1555 it.remove();
1556 }
1557 }
1558 // modified end
1559 if (VDBG) log("Routes in main table - [ " + mAddedRoutes + " ]");
1560 if (mAddedRoutes.contains(r) == false) {
1561 if (VDBG) log("Removing " + r + " for interface " + ifaceName);
1562 try {
1563 mNetd.removeRoute(ifaceName, r);
1564 } catch (Exception e) {
1565 // never crash - catch them all
1566 if (VDBG) loge("Exception trying to remove a route: " + e);
1567 return false;
1568 }
1569 } else {
1570 if (VDBG) log("not removing " + r + " as it's still in use");
1571 }
1572 } else {
1573 if (VDBG) log("Removing " + r + " for interface " + ifaceName);
1574 try {
1575 mNetd.removeSecondaryRoute(ifaceName, r);
1576 } catch (Exception e) {
1577 // never crash - catch them all
1578 if (VDBG) loge("Exception trying to remove a route: " + e);
1579 return false;
1580 }
1581 }
1582 }
1583 return true;
1584 }
We can see that adding a new item into mAddedRoutes in thread 682 and remove a item from mAddedRoutes in thread 487 at same time.
[JRE document Reference]
public class ConcurrentModificationException
extends RuntimeException
This exception may be thrown by methods that have detected concurrent modification of an object when such modification is not permissible.
For example, it is not generally permssible for one thread to modify a Collection while another thread is iterating over it. In general, the results of the iteration are undefined under these circumstances. Some Iterator implementations (including those of all the collection implementations provided by the JRE) may choose to throw this exception if this behavior is detected. Iterators that do this are known as fail-fast iterators, as they fail quickly and cleanly, rather that risking arbitrary, non-deterministic behavior at an undetermined time in the future.
Note that this exception does not always indicate that an object has been concurrently modified by a different thread. If a single thread issues a sequence of method invocations that violates the contract of an object, the object
may throw this exception. For example, if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will thow this exception.
Note that fail-fast behavior cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast operations throw ConcurrentModificationException on a best-effort basis.
Therefore, it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.
The modification status of a container will be recored during one iterator of this container being initialized.
Any modification that is not operated by that iterator of this container will lead to a "java.util.ConcurrentModificationException" if the iterator is still valid and active.
[Reference solution]
Adding a synchronization mechanism for mAddedRoutes such as synchronized(mAddedRoutes) to keep its modification thread-safe.
它帮助拯救了无数生命,也曾毁灭婚姻,创造了一种新的语言,并为移动运营商创造数十亿美元利润。不过智能手机和数据服务的崛起使得免费短信服务出现,手机短信面临衰退。
北京时间12月3日消息,英国《卫报》日前撰文回顾了短信服务20年的发展,并指出在智能手机时代,随着免费短信服务出现,手机短信不可避免地面临衰退。
以下为文章主要内容:
它帮助拯救了无数生命,也曾毁灭婚姻,创造了一种新的语言并为移动运营商创造数十亿美元利润。随着手机短信庆祝自己的第20个生日,有人疑问在智能手机和无限量网络数据的世界,手机短信还能生存多长时间。
短信发送的数量逐年大幅增加,不过有迹象表明在几个国家和地区已经达到顶峰,包括西班牙、荷兰、芬兰、香港和澳大利亚。新一代的用户曾经使用短信,但是现在免费使用WhatsApp或者BlackBerry Messenger等用户数据服务。
Enders Analysis 公司电信分析师本尼迪克特·埃文斯表示:“现在有很多这样服务,我已经统计了有25种,据报告账户总数已经达到25亿,不过很多这些账户都是重复的。可能有10项服务拥有超过1亿用户,BBM并不在其中,只有6000万用户。”
历 史上发送的第一条短信是在1992年12月3日,当时一位22岁的英国工程师尼尔·帕普沃思(Neil Papworth)利用自己的电脑向沃达丰公司经理理查德·贾维斯(Richard Jarvis)发送短信,祝福后者“圣诞节快乐”,当时接收短信的手机是欧比特901。帕普沃思并没有收到回复,因为当时手机并不能发送短信。我们需要等 到诺基亚在1993年推出的首款手机。
第一批短信都是免费的,只可以在同一个网络的用户间发送,但是在1994年,沃达丰(当时英国2家移动网络之一)推出一个股价提醒系统。Tegic系统(即T9短信输入系统)在1995年推出,大大缩短了文字输入的时间,意味着短信业务可以快速起飞。
商 业服务很快跟随而至,尽管这些服务起初是免费的——因为运营商并没有想到如何进行收费,但是随着短信数量大幅增加,他们很快意识到可以从中赚钱。在 2001年2月,英国每个月的短信数量达到10亿条,如果按照标准的每条短信10便士计算,这项业务每个月能带来约1亿英镑收入。
一条短信的数据流量非常小,只有128字节。按照同样的收费计算,650M的音乐CE将收费超过6万英镑。
在同一年,短信成为了人们生活的重要部分,14名英国游客在巴厘岛外的龙目海峡(Lombok Strait)搁浅,好在一位游客发短信给自己在英格兰的男友,他们才能全部获救;一位登山者凭借一条短信联系到了登山救援队,从而安全获救。
由于短信最多只能发动160个字母并且通过数字键盘输入单词较为耗费时间,因而“短信文”( Text language)快速出现。"l8r"、"gr8" 和"b4"等缩写很快令大人们迷糊,他们抱怨说孩子们已经丧失了正确拼写单词的能力。
到 2003年,改卷人员对于考生答案中使用“短信文”的问题越来越担忧:一位13岁的女孩以短信缩写的方式写了一篇文章,其中她写道“My smmr hols wr CWOT. B4, we used 2go2 NY 2C my bro, his GF & thr 3 :- kids FTF. ILNY, it's a gr8 plc”,实际内容为:“My summer holidays were a complete waste of time. Before, we used to go to New York to see my brother, his girlfriend and their three (!) kids face to face. I love New York, it's a great place”(我的暑假过得非常浪费。之前,我们通常是去纽约看望哥哥、他女朋友还有他们三个孩子。我喜欢纽约,那是个非常不错的地方。)
不过,现在短信比英语面临更大的威胁。智能手机和数据服务的崛起意味着发送数据的价格大幅降低,这已经导致免费服务的出现,甚至在你的手机没有数据的情况下你也可以发送数据。
今年1月,芬兰移动网络Sonera报告称,2011年圣诞节前夕发送短信的数量为850万条,低于上一年的1090万条。在香港,短信发送数量减少14%。
不过这并不意味着短信将消失,或者移动运营商将突然破产。短信已经带来超过5000亿美元收入,并且市场预测在未来7年仍然产生1万亿元美元,因为人们在非洲和印度用短信进行移动银行业务,此外其他地方也使用短信进行慈善捐赠和政治捐献。
埃文斯表示,随着短信数量减少,运营商将调整他们的价格模式。“他们将开始收取更多费用以传输更多数据。传送500M数据的费用超过传输500分钟语音通话的费用。因此他们将重新调整价格。”
但他表示不同设备间发送短信的主意将继续保持:“Twitter被认为是一项短信服务,这就是为什么每条Twitter消息都这么短。短信发送的机制将改变,不过人们将发现相互间发送短信变得更便利。”
时间线
1992年 首条短信发出
1995年 T9系统推出,使短信输入更快捷
2001年 英国一个月的短信量达到10亿条
2001年 短信用于帮助组织推翻菲律宾总统约瑟夫·埃斯特拉达(Joseph Estrada)的示威活动。
2002年 一项名为Text2TV的服务称可以让你向电视机发短信并通过你的遥控器进行回复。但是这项服务并没有得到大幅应用
2003年 球星大卫·贝克汉姆(David Beckham)向自己的个人助理丽贝卡·卢斯(Rebecca Loos)发送一系列暧昧短信;这些短信之后被公开,几乎导致贝克汉姆的婚姻终结。
2004年 托尼·布莱尔(Tony Blair)参加一次直播的短信互动聊天
2005年 欧洲歌唱大赛(Eurovision)加入短信投票
2008年 尼尔森报告称,平均每位美国手机用户每月发送和接收短信数量超过打电话数量——357 V 204
2009年 免费短信服务WhatsApp创立。
2011年 芬兰、香港、西班牙和荷兰圣诞节发送短信数量同比下滑
2012年 Ofcom报告称,短信是亲友间日常沟通联系最普遍使用的办法——58%的英国成年人每天至少发一条短信给亲友。
2012 年 丽贝卡·布鲁克斯(Rebekah Brooks)透露戴维·卡梅伦(David Cameron)向她发送签名为“LOL”的短信,因为他认为这意味着“很多爱”(lots of love),但该缩写通常的意思是“放声大笑”(laughing out loud)。