该系统有两个应用,比较繁琐。但是内容提供者是android里非常非常重要的一个内容,我们得好好学习下哦。先看效果图,我们提供了四个按钮,点击按钮便会进行相应的操作。

我们先看内容提供者所在的应用,代码结构:

activity代码:

  1. package cn.com.contentProvider;
  2. import android.app.Activity;
  3. import android.os.Bundle;
  4. import android.widget.TextView;
  5. public class ContentProviderAcitivity extends Activity {
  6. @Override
  7. public void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.main);
  10. }
  11. }

MyContentProvider.java代码

  1. package cn.com.contentProvider;
  2. import android.content.ContentProvider;
  3. import android.content.ContentUris;
  4. import android.content.ContentValues;
  5. import android.content.Context;
  6. import android.content.UriMatcher;
  7. import android.database.Cursor;
  8. import android.database.sqlite.SQLiteDatabase;
  9. import android.database.sqlite.SQLiteOpenHelper;
  10. import android.database.sqlite.SQLiteDatabase.CursorFactory;
  11. import android.net.Uri;
  12. /**
  13. *
  14. * @author chenzheng_java
  15. * @description 自定义的内容提供者.
  16. *  总结下访问内容提供者的主要步骤:
  17. * 第一:我们要有一个uri,这就相当于我们的网址,我们有了网址才能去访问具体的网站
  18. * 第二:我们去系统中寻找该uri中的authority(可以理解为主机地址),
  19. *      只要我们的内容提供者在manifest.xml文件中注册了,那么系统中就一定存在。
  20. * 第三:通过内容提供者内部的uriMatcher对请求进行验证(你找到我了,还不行,我还得看看你有没有权限访问我呢)。
  21. * 第四:验证通过后,就可以调用内容提供者的增删查改方法进行操作了
  22. */
  23. public class MyContentProvider extends ContentProvider {
  24. // 自己实现的数据库操作帮助类
  25. private MyOpenHelper myOpenHelper;
  26. // 数据库相关类
  27. private SQLiteDatabase sqLiteDatabase;
  28. // uri匹配相关
  29. private static UriMatcher uriMatcher;
  30. // 主机名称(这一部分是可以随便取得)
  31. private static final String authority = "cn.com.chenzheng_java.hello";
  32. // 注册该内容提供者匹配的uri
  33. static {
  34. uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  35. /*
  36. * path_chenzheng部分的字符串是随便取得,1代表着如果请求的uri与当前加入
  37. * 的匹配uri正好吻合,uriMathcher.match()方法返回的值.#代表任意数字,*代表任意字符串
  38. */
  39. uriMatcher.addURI(authority, "path_chenzheng", 1);// 代表当前表中的所有的记录
  40. uriMatcher.addURI(authority, "path_chenzheng/#", 2);// 代表当前表中的某条特定的记录,记录id便是#处得数字
  41. }
  42. // 数据表中的列名映射
  43. private static final String _id = "id";
  44. private static final String name = "name";
  45. private static final String age = "age";
  46. private static final String isMan = "isMan";
  47. /**
  48. * @description 当内容提供者第一次创建时执行
  49. */
  50. @Override
  51. public boolean onCreate() {
  52. try {
  53. myOpenHelper = new MyOpenHelper(getContext(), DB_Name, null,
  54. Version_1);
  55. } catch (Exception e) {
  56. return false;
  57. }
  58. return true;
  59. }
  60. /**
  61. * @description 对数据库进行删除操作的时候执行
  62. *              android.content.ContentUri为我们解析uri相关的内容提供了快捷方便的途径
  63. */
  64. @Override
  65. public int delete(Uri uri, String selection, String[] selectionArgs) {
  66. int number = 0;
  67. sqLiteDatabase = myOpenHelper.getWritableDatabase();
  68. int code = uriMatcher.match(uri);
  69. switch (code) {
  70. case 1:
  71. number = sqLiteDatabase
  72. .delete(Table_Name, selection, selectionArgs);
  73. break;
  74. case 2:
  75. long id = ContentUris.parseId(uri);
  76. /*
  77. * 拼接where子句用三目运算符是不是特烦人啊? 实际上,我们这里可以用些技巧的.
  78. * if(selection==null||"".equals(selection.trim())) selection =
  79. * " 1=1 and "; selection+=_id+"="+id;
  80. * 拼接where子句中最麻烦的就是and的问题,这里我们通过添加一个1=1这样的恒等式便将问题解决了
  81. */
  82. selection = (selection == null || "".equals(selection.trim())) ? _id
  83. + "=" + id
  84. : selection + " and " + _id + "=" + id;
  85. number = sqLiteDatabase
  86. .delete(Table_Name, selection, selectionArgs);
  87. break;
  88. default:
  89. throw new IllegalArgumentException("异常参数");
  90. }
  91. return number;
  92. }
  93. /**
  94. *@description 获取当前内容提供者的MIME类型 集合类型必须添加前缀vnd.android.cursor.dir/(该部分随意)
  95. *              单条记录类型添加前缀vnd,android.cursor.item/(该部分随意)
  96. *              定义了该方法之后,系统会在第一次请求时进行验证,验证通过则执行crub方法时不再重复进行验证,
  97. *              否则如果没有定义该方法或者验证失败,crub方法执行的时候系统会默认的为其添加类型验证代码。
  98. */
  99. @Override
  100. public String getType(Uri uri) {
  101. int code = uriMatcher.match(uri);
  102. switch (code) {
  103. case 1:
  104. return "vnd.android.cursor.dir/chenzheng_java";
  105. case 2:
  106. return "vnd.android.cursor.item/chenzheng_java";
  107. default:
  108. throw new IllegalArgumentException("异常参数");
  109. }
  110. }
  111. /**
  112. * @description 对数据表进行insert时执行该方法
  113. */
  114. @Override
  115. public Uri insert(Uri uri, ContentValues values) {
  116. sqLiteDatabase = myOpenHelper.getWritableDatabase();
  117. int code = uriMatcher.match(uri);
  118. switch (code) {
  119. case 1:
  120. sqLiteDatabase.insert(Table_Name, name, values);
  121. break;
  122. case 2:
  123. long id = sqLiteDatabase.insert(Table_Name, name, values);
  124. // withAppendId将id添加到uri的最后
  125. ContentUris.withAppendedId(uri, id);
  126. break;
  127. default:
  128. throw new IllegalArgumentException("异常参数");
  129. }
  130. return uri;
  131. }
  132. /**
  133. * 当执行查询时调用该方法
  134. */
  135. @Override
  136. public Cursor query(Uri uri, String[] projection, String selection,
  137. String[] selectionArgs, String sortOrder) {
  138. Cursor cursor = null;
  139. sqLiteDatabase = myOpenHelper.getReadableDatabase();
  140. int code = uriMatcher.match(uri);
  141. switch (code) {
  142. case 1:
  143. cursor = sqLiteDatabase.query(Table_Name, projection, selection,
  144. selectionArgs, null, null, sortOrder);
  145. break;
  146. case 2:
  147. // 从uri中解析出ID
  148. long id = ContentUris.parseId(uri);
  149. selection = (selection == null || "".equals(selection.trim())) ? _id
  150. + "=" + id
  151. : selection + " and " + _id + "=" + id;
  152. cursor = sqLiteDatabase.query(Table_Name, projection, selection,
  153. selectionArgs, null, null, sortOrder);
  154. break;
  155. default:
  156. throw new IllegalArgumentException("参数错误");
  157. }
  158. return cursor;
  159. }
  160. /**
  161. * 当执行更新操作的时候执行该方法
  162. */
  163. @Override
  164. public int update(Uri uri, ContentValues values, String selection,
  165. String[] selectionArgs) {
  166. int num = 0;
  167. sqLiteDatabase = myOpenHelper.getWritableDatabase();
  168. int code = uriMatcher.match(uri);
  169. switch (code) {
  170. case 1:
  171. num = sqLiteDatabase.update(Table_Name, values, selection, selectionArgs);
  172. break;
  173. case 2:
  174. long id = ContentUris.parseId(uri);
  175. selection = (selection == null || "".equals(selection.trim())) ? _id
  176. + "=" + id
  177. : selection + " and " + _id + "=" + id;
  178. num = sqLiteDatabase.update(Table_Name, values, selection, selectionArgs);
  179. break;
  180. default:
  181. break;
  182. }
  183. return num;
  184. }
  185. // 数据库名称
  186. private final String DB_Name = "chenzheng_java.db";
  187. // 数据表名
  188. private final String Table_Name = "chenzheng_java";
  189. // 版本号
  190. private final int Version_1 = 1;
  191. private class MyOpenHelper extends SQLiteOpenHelper {
  192. public MyOpenHelper(Context context, String name,
  193. CursorFactory factory, int version) {
  194. super(context, name, factory, version);
  195. }
  196. /**
  197. * @description 当数据表无连接时创建新的表
  198. */
  199. @Override
  200. public void onCreate(SQLiteDatabase db) {
  201. String sql = " create table if not exists " + Table_Name
  202. + "(id INTEGER,name varchar(20),age integer,isMan boolean)";
  203. db.execSQL(sql);
  204. }
  205. /**
  206. * @description 当版本更新时触发的方法
  207. */
  208. @Override
  209. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  210. String sql = " drop table if exists " + Table_Name;
  211. db.execSQL(sql);
  212. onCreate(db);
  213. }
  214. }
  215. }

androidManifest.xml代码

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="cn.com.contentProvider" android:versionCode="1"
  4. android:versionName="1.0">
  5. <uses-sdk android:minSdkVersion="8" />
  6. <application android:icon="@drawable/icon" android:label="@string/app_name">
  7. <activity android:name=".ContentProviderAcitivity"
  8. android:label="@string/app_name">
  9. <intent-filter>
  10. <action android:name="android.intent.action.MAIN" />
  11. <category android:name="android.intent.category.LAUNCHER" />
  12. </intent-filter>
  13. </activity>
  14. <provider android:name=".MyContentProvider"
  15. android:authorities="cn.com.chenzheng_java.hello"
  16. android:multiprocess="true" android:permission="cn.com.chenzheng_java.permission"></provider>
  17. </application>
  18. <!--
  19. permission中的android:name的值与provider中的android:permission的值是一样的
  20. android:protectionLevel 则代表了权限等级
  21. -->
  22. <permission android:name="cn.com.chenzheng_java.permission"
  23. android:protectionLevel="normal"></permission>
  24. </manifest>

main.xml为默认。

----------------------------------------------------------------------------------------------------------------

第二个应用(用于访问内容提供者的应用)

activity代码

  1. package cn.com.chenzheng_java;
  2. import android.app.Activity;
  3. import android.content.ContentResolver;
  4. import android.content.ContentValues;
  5. import android.database.Cursor;
  6. import android.net.Uri;
  7. import android.os.Bundle;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.widget.Button;
  11. import android.widget.TextView;
  12. /**
  13. *
  14. * @author chenzheng_java
  15. * @description 通过访问内容提供者进行增删查改.注意本程序中为了方便阅读,
  16. * 在需要数据库列名的地方直接写上了数据库中字段的名称,实际上这是不合理的,
  17. * 作为内容提供者的使用者,我们不可能在使用这个内容提供者之前先去了解sqlite
  18. * 中表的结构。比较适宜的做法是,在内容提供者中将愿意提供给外部访问的字段名称(列名)
  19. * 定义为string final 的常量!
  20. */
  21. public class ContentAccessActivity extends Activity {
  22. private final static String tag = "通知";
  23. private TextView textView;
  24. String result = "结果:/n";
  25. ContentResolver reslover;
  26. Uri uri;
  27. @Override
  28. public void onCreate(Bundle savedInstanceState) {
  29. super.onCreate(savedInstanceState);
  30. setContentView(R.layout.main);
  31. /**
  32. * 这里我们一定要搞清楚,uri的内容到底和内容提供者中哪个地方一一对应
  33. * 在MyContentProvider中我们有如下片段
  34. * uriMatcher.addURI(authority, "path_chenzheng", 1);// 代表当前表中的所有的记录
  35. uriMatcher.addURI(authority, "path_chenzheng/#", 2);// 代表当前表中的某条特定的记录,记录id便是#处得数字
  36. 其中authority为cn.com.chenzheng_java.hello。
  37. */
  38. uri = Uri.parse("content://cn.com.chenzheng_java.hello/path_chenzheng");
  39. /**
  40. * 内容提供者是什么?内容提供者相当于一个封装好了增删改查操作的接口,这个接口有一把锁,只有携带钥匙的访问者才能访问。
  41. * ContentResolver是什么?ContentResolver是一个开锁匠,他携带者钥匙(钥匙上有标签显示他是那个门得钥匙,如path_chenzheng)
  42. * 去寻找内容提供者,然后访问内容提供者的增删查改方法
  43. * 我们这里调用contentResolver的增删查改就相当于将任务交给了锁匠,
  44. * 然后让锁匠去找能打开的内容提供者,并且执行里面相应的方法,并将结果返回.
  45. * ContentResolver的好处在于,我们可以无视CotentProvider的具体实现,无论contentProvider里面是如何实现的,我想执行
  46. * 某一个操作时,所要书写的代码都是一样的。
  47. */
  48. reslover = this.getContentResolver();
  49. textView = (TextView) findViewById(R.id.textView);
  50. Button insertButton = (Button) findViewById(R.id.insertButton);
  51. insertButton.setOnClickListener(new View.OnClickListener() {
  52. @Override
  53. public void onClick(View v) {
  54. insert(reslover, uri);
  55. }
  56. });
  57. Button deleteButton = (Button) findViewById(R.id.deleteButton);
  58. deleteButton.setOnClickListener(new View.OnClickListener() {
  59. @Override
  60. public void onClick(View v) {
  61. delete(reslover, uri);
  62. }
  63. });
  64. Button updateButton = (Button) findViewById(R.id.updateButton);
  65. updateButton.setOnClickListener(new View.OnClickListener() {
  66. @Override
  67. public void onClick(View v) {
  68. update(reslover, uri);
  69. }
  70. });
  71. Button queryButton = (Button) findViewById(R.id.queryButton);
  72. queryButton.setOnClickListener(new View.OnClickListener() {
  73. @Override
  74. public void onClick(View v) {
  75. query(reslover, uri);
  76. }
  77. });
  78. }
  79. private void insert(ContentResolver resolver, Uri uri) {
  80. ContentValues contentValues = new ContentValues();
  81. contentValues.put("name", "张小凡");
  82. contentValues.put("age", 22);
  83. contentValues.put("isMan", true);
  84. Uri uri2 = resolver.insert(uri, contentValues);
  85. Log.i(tag, "插入成功!");
  86. result += "成功插入了一条记录,uri为" + uri2;
  87. textView.setText(result);
  88. result = "";
  89. }
  90. private void update(ContentResolver resolver, Uri uri) {
  91. ContentValues contentValues = new ContentValues();
  92. contentValues.put("age", 122);
  93. int number = resolver.update(uri, contentValues, null, null);
  94. Log.i(tag, "更新成功!");
  95. result += "成功更新了" + number+"条记录";
  96. textView.setText(result);
  97. result = "";
  98. }
  99. private void delete(ContentResolver resolver, Uri uri) {
  100. String where = " 1=1 and isMan=?";
  101. //这里要注意哦,sqlite数据库中是没有boolean的,true会被转成1存储
  102. String[] selectionArgs = new String[] { "1" };
  103. int number = resolver.delete(uri, where, selectionArgs);
  104. Log.i(tag, "删除成功!");
  105. textView.setText(result + "成功删除了" + number + "条记录");
  106. result = "";
  107. }
  108. private void query(ContentResolver resolver, Uri uri) {
  109. String[] projection = new String[] { "id", "name", "age", "isMan" };
  110. Cursor cursor = resolver.query(uri, projection, null, null, null);
  111. int count = cursor.getCount();
  112. Log.i(tag, "总记录数" + count);
  113. int idIndex = cursor.getColumnIndex("id");
  114. int nameIndex = cursor.getColumnIndex("name");
  115. int ageIndex = cursor.getColumnIndex("age");
  116. int isManIndex = cursor.getColumnIndex("isMan");
  117. cursor.moveToFirst();
  118. while (!cursor.isAfterLast()) {
  119. int id = cursor.getInt(idIndex);
  120. String name = cursor.getString(nameIndex);
  121. int age = cursor.getInt(ageIndex);
  122. int isMan = cursor.getInt(isManIndex);
  123. Log.i(tag, "id=" + id + " name=" + name + " age=" + age + " isMan="
  124. + isMan);
  125. result += "id=" + id + " name=" + name + " age=" + age + " isMan="
  126. + isMan;
  127. cursor.moveToNext();
  128. }
  129. textView.setText(result);
  130. result = "";
  131. }
  132. }

manifest.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="cn.com.chenzheng_java"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6. <uses-sdk android:minSdkVersion="8" />
  7. <application android:icon="@drawable/icon" android:label="@string/app_name">
  8. <activity android:name=".ContentAccessActivity"
  9. android:label="@string/app_name">
  10. <intent-filter>
  11. <action android:name="android.intent.action.MAIN" />
  12. <category android:name="android.intent.category.LAUNCHER" />
  13. </intent-filter>
  14. </activity>
  15. </application>
  16. <!-- 添加对内容提供者访问的权限,该权限是有我们自己定义的哦 -->
  17. <uses-permission android:name="cn.com.chenzheng_java.permission"></uses-permission>
  18. </manifest>

main.xml

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. >
  7. <TextView
  8. android:id="@+id/textView"
  9. android:layout_width="fill_parent"
  10. android:layout_height="wrap_content"
  11. android:text="@string/hello"
  12. />
  13. <Button
  14. android:id="@+id/insertButton"
  15. android:layout_width="wrap_content"
  16. android:layout_height="wrap_content"
  17. android:text="insert"
  18. ></Button>
  19. <Button
  20. android:id="@+id/deleteButton"
  21. android:layout_width="wrap_content"
  22. android:layout_height="wrap_content"
  23. android:text="delete"
  24. ></Button>
  25. <Button
  26. android:id="@+id/updateButton"
  27. android:layout_width="wrap_content"
  28. android:layout_height="wrap_content"
  29. android:text="update"
  30. ></Button>
  31. <Button
  32. android:id="@+id/queryButton"
  33. android:layout_width="wrap_content"
  34. android:layout_height="wrap_content"
  35. android:text="query"
  36. ></Button>
  37. </LinearLayout>

--------------------------------------------------------------------------------

想说的话,在代码的注释中已经说的很清晰了。这里再次重复下我们定义和使用内容提供者的步骤吧。

定义内容提供者:

我们定义内容提供者的目的是什么,共享数据,对,定义内容提供者的目的就是让别的应用能够访问当前应用的一些数据,至于到底暴露给外界什么数据,我们可以 在定义内容提供者的时候详细控制!不管如何,我们明确了第一个问题,定义内容提供者的目的----数据共享!

我们平时对数据的操作都有哪些?增删改查!就四个字!这也是为什么我们再定义内容提供者的时候必须要实现相应的方法了。当然如果你要是不想提供相应的操作,你可以在内部进行方法空实现。

是不是所有的应用都可以访问我啊?不可能!我们可不是随便的人,对吧!所以我们要进行验证,验证不通过的直接让它去死就可以了。验证怎么验证啊?通过UriMatcher进行匹配!

现在我们已经提供了访问接口了,我们怎么让系统知道,别的应用可以用我的东西啊?去配置文件中注册!!

使用内容提供者:

如何找到该内容提供者啊?需要Uri和相应的访问权限。相当于地址

如何进行增删查改啊?通过ContentResolver对象的相应方法。

android之内容提供者解析的更多相关文章

  1. Android 中内容提供者的使用

    在Android中内容提供者主要是用于不同程序之间的数据共享.内容提供器的用法一般有两种,一种是使用现有的内容提供器来读取和操作相应程序的数据,另一种是创建自己的内容提供器,供其他的程序访问. 使用现 ...

  2. Android 之内容提供者 内容解析者 内容观察者

    contentProvider:ContentProvider在Android中的作用是对外提供数据,除了可以为所在应用提供数据外,还可以共享数据给其他应用,这是Android中解决应用之间数据共享的 ...

  3. [Android Pro] 内容提供者ContentProvider的基本使用

    一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.ContentProvider为存储和获取数据提 ...

  4. Android中内容提供者ContentProvider的详解

    1.什么是ContentProvider 首先,ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用的比较少. ContentProvider为不 ...

  5. Android使用内容提供者实现增删改查操作

    Android使用内容提供者实现增删改查操作 这里需要建立两个项目:SiYouShuJuKu(使用内容提供者暴露相关的操作),DQDYGApplication(使用内容解析者对第一个应用进行相关的解析 ...

  6. Android -- ContentProvider 内容提供者,创建和调用

    1. 概述 ContentProvider 在android中的作用是对外共享数据,也就是说你可以通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentPr ...

  7. Android基础内容提供者ContentProvider的使用详解(转)

    1.什么是ContentProvider 首先,ContentProvider(内容提供者)是android中的四大组件之一,但是在一般的开发中,可能使用的比较少. ContentProvider为不 ...

  8. android contentprovider内容提供者

    contentprovider内容提供者:让其他app可以访问私有数据库(文件) 1.AndroidManifest.xml 配置provider <?xml version="1.0 ...

  9. Android之内容提供者ContentProvider的总结

    本文包含以下知识点: ContentProvider Uri 的介绍 ContentResolver: 监听ContentProvider的数据改变 一:ContentProvider部分 Conte ...

随机推荐

  1. [CF843D]Dynamic Shortest Path

    [CF843D]Dynamic Shortest Path 题目大意: 给定一个带权有向图,包含\(n(n\le10^5)\)个点和\(m(m\le10^5)\)条边.共\(q(q\le2000)\) ...

  2. ERROR 1044: Access denied for user: 'songyan' to database 'yikexiao' 的错误。

    问题描述:新买的服务器,刚安装了mysql,创建了一个用户,也忘记了给他分配了什么权限,今天在建库的时候出现了这个问题. 出错原因:度娘告诉我是因为songyan用户没有建库的权限报的错. 解决: ( ...

  3. Ubuntu 16.04搭建OpenVPN服务器以及客户端的使用

    说明:启动时注意用户权限,比如root用户启动. Ubuntu: 服务器环境:Ubuntu 16.04 64位系统 内网IP:10.143.80.116 外网IP:203.195.1.2 OpenVP ...

  4. 掌握Linux编程的10个步骤

    Linux 编程经典书籍推荐 Denis 2008年10月17日 浏览:84168 成为一名精通 Linux 程序设计的高级程序员一直是不少朋友孜孜以求的目标.根据中华英才网统计数据,北京地区 Lin ...

  5. ORACLE查询表最近更改数据的方法

    修改项目时,涉及到了Oracle中许多表的修改(包括:增加.删除字段,修改注释等).由于开始没有进行记录,造成在上测试机时,忘记了具体修改过哪些表了.后来在网上查找了一些资料,例如: 1.select ...

  6. (判断url文件大小)关于inputStream.available()方法获取下载文件的总大小

    转自:http://hold-on.iteye.com/blog/1017449 如果用inputStream对象的available()方法获取流中可读取的数据大小,通常我们调用这个函数是在下载文件 ...

  7. timeline bugs

    timeline有个问题巨坑 修了很久 就是一个track控制character的position 另一个track控制同一个character的animaion 拖一段现成的动画进去 这种情况 会有 ...

  8. 【License】一张图该诉你各种License的含义?

    一张图该诉你各种License的含义:

  9. Java中PriorityQueue详解

    Java中PriorityQueue通过二叉小顶堆实现,可以用一棵完全二叉树表示.本文从Queue接口函数出发,结合生动的图解,深入浅出地分析PriorityQueue每个操作的具体过程和时间复杂度, ...

  10. iOS项目开发实战——使用CoreLocation获取当前位置信息

    随着基于位置服务LBS和移动互联网的兴起,你的位置是越来越重要的一个信息.位置服务已经是当前的热门应用如微信.陌陌等社交应用的杀手锏.而在iOS开发中,苹果已经给我们提供了一个位置接口.CoreLoc ...