ContentProvider

在创建ContentProvider时,需要首先使用数据库、文件系统或网络实现底层存储功能,

然后在继承ContentProvider的类中实现基本数据操作的接口函数,包括添加、删除、查找和更新等功能。

调用者不能够直接调用ContentProvider的接口函数,而需要使用ContentResolver对象,

通过URI间接调用ContentProvider。下图是ContentProvider调用关系。

URI

URI是通用资源标志符(Uniform Resource Identifier),用来定位任何远程或本地的可用资源

ContentProvider使用的URI语法结构如下

content://<authority>/<data_path>/<id>

content://是通用前缀,表示该URI用于ContentProvider定位资源,无需修改。

<authority>是授权者名称,用来确定具体由哪一个ContentProvider提供资源。因此,一般<authority>都由类的小写全称组成,以保证唯一性。

<data_path>是数据路径,用来确定请求的是哪个数据集。

例如:

content://edu.hrbeu.peopleprovider/people/3

可以省略id(/3)部分那么意味着整个数据。

UriMatcher:

在新构造的ContentProvider类中,通过构造一个UriMatcher,判断URI是单条数据还是多条数据。

public void  addURI  (String authority, String path, int code)

authority表示匹配的授权者名称

path表示数据路径

#可以代表任何数字 (content://<authority>/<data_path>/#

code表示返回代码uriMatcher.match(uri))的返回值

注册ContentProvider

<provider android:name = ".PeopleProvider" android:authorities = "edu.hrbeu.peopleprovider"/> 

实例:

ContentProvider一般用于两个不同的进程之间的数据共享。

  1. 假设我们有一个新的工程(app),在此工程中创建一个people.db数据库,然后通过自定义了一个ContentProvider来共享数据库中的data。
  2. 我们可以通过注册provider时用到的authority在配合db path之类的来连接(content://<authority>/<data_path>/#)读取内容提供者(不同进程)的数据。

首先是步骤1:

*只要在AndroidManifast中注册provider就会执行对应的provider类。无需再MainActivity中调用或无需直接调用自定义的new provider。

即系统自动会加载调用PeopleProvider类。

<provider android:name = ".PeopleProvider" android:authorities = "edu.hrbeu.peopleprovider"/> 

继承ContentProvider时代码如下:

import android.content.*;
import android.database.Cursor;
import android.net.Uri; public class PeopleProvider extends ContentProvider{
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
} @Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
} @Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
return null;
} @Override
public boolean onCreate() {
// TODO Auto-generated method stub
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
return null;
} @Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
}

类似数据库操作。可以这么理解,相对来说好理解。

开始贴代码。。。

package edu.hrbeu.contentproviderdemo;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri; public class PeopleProvider extends ContentProvider { private static final String DB_NAME = "people.db";
private static final String DB_TABLE = "peopleinfo";
private static final int DB_VERSION = 1; private SQLiteDatabase db;
private DBOpenHelper dbOpenHelper; private static final int MULTIPLE_PEOPLE = 1;
private static final int SINGLE_PEOPLE = 2;
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(People.AUTHORITY, People.PATH_MULTIPLE, MULTIPLE_PEOPLE);
uriMatcher.addURI(People.AUTHORITY, People.PATH_SINGLE, SINGLE_PEOPLE);
} @Override
public boolean onCreate() {
// TODO Auto-generated method stub
Context context = getContext();
dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
db = dbOpenHelper.getWritableDatabase(); if (db == null)
return false;
else
return true; } @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// TODO Auto-generated method stub
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(DB_TABLE);
switch(uriMatcher.match(uri)){
case SINGLE_PEOPLE:
qb.appendWhere(People.KEY_ID + "=" + uri.getPathSegments().get(1));
break;
default:
break;
}
Cursor cursor = qb.query(db,
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor; } @Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
switch(uriMatcher.match(uri)){
case MULTIPLE_PEOPLE:
return People.MINE_TYPE_MULTIPLE;
case SINGLE_PEOPLE:
return People.MINE_TYPE_SINGLE;
default:
throw new IllegalArgumentException("Unkown uri:"+uri);
} } @Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
long id = db.insert(DB_TABLE, null, values);
if ( id > 0 ){
Uri newUri = ContentUris.withAppendedId(People.CONTENT_URI, id);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
throw new SQLException("Failed to insert row into " + uri); } @Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count = 0;
switch(uriMatcher.match(uri)){
case MULTIPLE_PEOPLE:
count = db.delete(DB_TABLE, selection, selectionArgs);
break;
case SINGLE_PEOPLE:
String segment = uri.getPathSegments().get(1);
count = db.delete(DB_TABLE, People.KEY_ID + "=" + segment, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unsupported URI:" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count; } @Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
int count;
switch(uriMatcher.match(uri)){
case MULTIPLE_PEOPLE:
count = db.update(DB_TABLE, values, selection, selectionArgs);
break;
case SINGLE_PEOPLE:
String segment = uri.getPathSegments().get(1);
count = db.update(DB_TABLE, values, People.KEY_ID+"="+segment, selectionArgs);
break;
default:
throw new IllegalArgumentException("Unknow URI:" + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count; } private static class DBOpenHelper extends SQLiteOpenHelper { public DBOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
}
private static final String DB_CREATE = "create table " +
DB_TABLE + " (" + People.KEY_ID + " integer primary key autoincrement, " +
People.KEY_NAME+ " text not null, " + People.KEY_AGE+ " integer," + People.KEY_HEIGHT + " float);";
@Override
public void onCreate(SQLiteDatabase _db) {
// TODO Auto-generated method stub
_db.execSQL(DB_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase _db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
_db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
onCreate(_db); } } }
package edu.hrbeu.contentproviderdemo;
import android.net.Uri; public class People{ public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";
public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";
public static final String MINE_ITEM = "vnd.hrbeu.people"; public static final String MINE_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MINE_ITEM;
public static final String MINE_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MINE_ITEM;
public static final String AUTHORITY = "edu.hrbeu.peopleprovider";
public static final String PATH_SINGLE = "people/#";
public static final String PATH_MULTIPLE = "people";
public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING); public static final String KEY_ID = "_id";
public static final String KEY_NAME = "name";
public static final String KEY_AGE = "age";
public static final String KEY_HEIGHT = "height";
}

MainActivity是空的。。。不贴出来了。

运行结果没有任何内容,因为仅是添加了一个provider。

看一下步骤2:

在另一个App中调用此ContentProvider。

无需再AndroidManifast中注册provider。

只需对应步骤1中的People类里的静态变量是完全匹配的就是可以了。

也就是说URI需要与我们自定义的ContentProvider保持一致。

package edu.hrbeu.contentresolverdemo;
import android.net.Uri; public class People{ public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";
public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";
public static final String MINE_ITEM = "vnd.hrbeu.people"; public static final String MINE_TYPE_SINGLE = MIME_ITEM_PREFIX + "/" + MINE_ITEM;
public static final String MINE_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MINE_ITEM;
public static final String AUTHORITY = "edu.hrbeu.peopleprovider";
public static final String PATH_SINGLE = "people/#";
public static final String PATH_MULTIPLE = "people";
public static final String CONTENT_URI_STRING = "content://" + AUTHORITY + "/" + PATH_MULTIPLE;
public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_STRING); public static final String KEY_ID = "_id";
public static final String KEY_NAME = "name";
public static final String KEY_AGE = "age";
public static final String KEY_HEIGHT = "height";
}

操作Contentpriver提供的数据我们需要用到ContentResolver:

package edu.hrbeu.contentresolverdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView; public class ContentResolverDemo extends Activity { private EditText nameText;
private EditText ageText;
private EditText heightText;
private EditText idEntry; private TextView labelView;
private TextView displayView; private ContentResolver resolver; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_content_resolver_demo);
nameText = (EditText) findViewById(R.id.name);
ageText = (EditText) findViewById(R.id.age);
heightText = (EditText) findViewById(R.id.hight);
idEntry = (EditText) findViewById(R.id.id); labelView = (TextView) findViewById(R.id.label);
displayView = (TextView) findViewById(R.id.display);
Button addButton = (Button) findViewById(R.id.button1);
Button queryAllButton = (Button) findViewById(R.id.button2);
Button clearButton = (Button) findViewById(R.id.button3);
Button queryButton = (Button) findViewById(R.id.button6);
Button deleteButton = (Button) findViewById(R.id.button5);
Button updateButton = (Button) findViewById(R.id.button7); resolver = this.getContentResolver();
addButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
ContentValues values = new ContentValues(); values.put(People.KEY_NAME, nameText.getText().toString());
values.put(People.KEY_AGE,
Integer.parseInt(ageText.getText().toString()));
values.put(People.KEY_HEIGHT,
Float.parseFloat(heightText.getText().toString())); Uri newUri = resolver.insert(People.CONTENT_URI, values);
labelView.setText("添加成功,URI:" + newUri); }
});
queryAllButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
Cursor cursor = resolver.query(People.CONTENT_URI,
new String[] { People.KEY_ID, People.KEY_NAME,
People.KEY_AGE, People.KEY_HEIGHT }, null,
null, null);
if (cursor == null) {
labelView.setText("数据库中没有数据");
return;
}
labelView.setText("数据库:" + String.valueOf(cursor.getCount())
+ "条记录"); String msg = "";
if (cursor.moveToFirst()) {
do {
msg += "ID:"
+ cursor.getInt(cursor
.getColumnIndex(People.KEY_ID)) + ",";
msg += "姓名:"
+ cursor.getString(cursor
.getColumnIndex(People.KEY_NAME)) + ",";
msg += "年龄:"
+ cursor.getInt(cursor
.getColumnIndex(People.KEY_AGE)) + ", ";
msg += "身高:"
+ cursor.getFloat(cursor
.getColumnIndex(People.KEY_HEIGHT))
+ "\n";
} while (cursor.moveToNext());
} displayView.setText(msg); }
}); clearButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
displayView.setText("");
}
}); queryButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/"
+ idEntry.getText().toString());
Cursor cursor = resolver.query(uri, new String[] {
People.KEY_ID, People.KEY_NAME, People.KEY_AGE,
People.KEY_HEIGHT }, null, null, null);
if (cursor == null) {
labelView.setText("数据库中没有数据");
return;
} String msg = "";
if (cursor.moveToFirst()) {
msg += "ID:"
+ cursor.getInt(cursor
.getColumnIndex(People.KEY_ID)) + ",";
msg += "姓名:"
+ cursor.getString(cursor
.getColumnIndex(People.KEY_NAME)) + ",";
msg += "年龄:"
+ cursor.getInt(cursor
.getColumnIndex(People.KEY_AGE)) + ", ";
msg += "身高:"
+ cursor.getFloat(cursor
.getColumnIndex(People.KEY_HEIGHT)) + "\n";
} labelView.setText("数据库:");
displayView.setText(msg); }
});
deleteButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/"
+ idEntry.getText().toString());
int result = resolver.delete(uri, null, null);
String msg = "删除ID为" + idEntry.getText().toString() + "的数据"
+ (result > 0 ? "成功" : "失败");
labelView.setText(msg);
}
}); updateButton.setOnClickListener(new OnClickListener() { @Override
public void onClick(View v) {
// TODO Auto-generated method stub
ContentValues values = new ContentValues();
values.put(People.KEY_NAME, nameText.getText().toString());
values.put(People.KEY_AGE,
Integer.parseInt(ageText.getText().toString()));
values.put(People.KEY_HEIGHT,
Float.parseFloat(heightText.getText().toString())); Uri uri = Uri.parse(People.CONTENT_URI_STRING + "/"
+ idEntry.getText().toString());
int result = resolver.update(uri, values, null, null); String msg = "更新ID为" + idEntry.getText().toString() + "的数据"
+ (result > 0 ? "成功" : "失败");
labelView.setText(msg); }
});
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.content_resolver_demo, menu);
return true;
} @Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

事成相识的布局文件也附上:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="edu.hrbeu.contentresolverdemo.ContentResolverDemo" > <TextView android:id="@+id/Username"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:text="用户名:">
</TextView> <EditText
android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@id/Username" > </EditText> <TextView android:id="@+id/Userage"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/name"
android:text="年龄:">
</TextView>
<EditText android:id="@+id/age"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/Userage">
</EditText> <TextView android:id="@+id/Userhight"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/age"
android:text="身高:">
</TextView>
<EditText android:id="@+id/hight"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/Userhight">
</EditText> <Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/button1"
android:layout_alignBottom="@+id/button1"
android:layout_centerHorizontal="true"
android:text="全部显示" /> <Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/button2"
android:layout_alignBottom="@+id/button2"
android:layout_alignRight="@+id/hight"
android:text="清除显示" /> <TextView android:id="@+id/Userid"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/button1"
android:text="ID:">
</TextView>
<EditText android:id="@+id/id"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_below="@id/Userid">
</EditText> <Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/button5"
android:layout_alignBottom="@+id/button5"
android:layout_alignLeft="@+id/button2"
android:text="ID查询" /> <Button
android:id="@+id/button7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/button6"
android:layout_alignBottom="@+id/button6"
android:layout_alignLeft="@+id/button3"
android:text="ID更新" /> <Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/hight"
android:text="添加数据" /> <Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/id"
android:layout_below="@+id/id"
android:text="ID删除" /> <TextView
android:id="@+id/display"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/label"
android:layout_below="@+id/label"
android:layout_marginTop="23dp" /> <TextView
android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/button5"
android:layout_below="@+id/button6"
android:layout_marginTop="14dp" /> </RelativeLayout>

运行结果:

SQLite数据库与Contentprovider(2)的更多相关文章

  1. Android开发8:数据存储(二)——SQLite数据库和ContentProvider的使用

    前言 啦啦啦各位小伙伴们许久不见了~学期末和过年期间自己忙着做其他事没能及时更新Android开发系列课程的博客,实在是罪过罪过~ 好啦~废话不多说,进入我们今天的主题.今天我们将和大家学习其他的数据 ...

  2. Android开发8:数据存储(二)——SQLite数据库和ContentProvider的使用

    前言 啦啦啦各位小伙伴们许久不见了~学期末和过年期间自己忙着做其他事没能及时更新Android开发系列课程的博客,实在是罪过罪过~ 好啦~废话不多说,进入我们今天的主题.今天我们将和大家学习其他的数据 ...

  3. SQLite数据库与Contentprovider(1)

    SQlite:类似mysql的数据库.把数据保存到.db文件夹中. Contentprovider:一般用于不同进程之间的数据共享(两个APP). 手动建库:http://www.runoob.com ...

  4. 利用SQLiteOpenHelper来管理SQLite数据库 (转)

    转载自 利用SQLiteOpenHelper来管理SQLite数据库 http://blog.csdn.net/conowen/article/details/7306545 Android学习笔记( ...

  5. Android基础总结+SQlite数据库【申明:来源于网络】

    Android基础总结+SQlite数据库[申明:来源于网络] 基础总结篇之一:Activity生命周期:http://blog.csdn.net/liuhe688/article/details/6 ...

  6. SQLite数据库学习小结——Frameworks层实现

    3. SQLite的Frameworks层实现 3.1 Frameworks层架构 Android系统方便应用使用,在Frameworks层中封装了一套Content框架,之所以叫Content框架而 ...

  7. Android数据存储之SQLite 数据库学习

    Android提供了五种存取数据的方式 (1)SharedPreference,存放较少的五种类型的数据,只能在同一个包内使用,生成XML的格式存放在设备中 (2) SQLite数据库,存放各种数据, ...

  8. Android 创建SQLite数据库(一)

    Android内置了轻量级的数据库SQLite,这里将自己理解作个记录,方便自己复习. 一.首先,创建SQLite数据库比较常见的方式是通过Android提供的SQLiteOpenHelper来实现, ...

  9. Android中数据存储(三)——SQLite数据库存储数据

    当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方式: 1. Share ...

随机推荐

  1. LA3902 Network

    给出一棵树,对于每一个叶子节点要使得在它的k距离内至少一个节点被打了标记,(叶节点不能打标记,非叶结点也不必满足这个条件),现在已经有一个节点s被打了标记,问至少还要打几个标记(这表达能力也是捉急.. ...

  2. 洛谷P1519 穿越栅栏 Overfencing

    P1519 穿越栅栏 Overfencing 69通过 275提交 题目提供者该用户不存在 标签USACO 难度普及/提高- 提交  讨论  题解 最新讨论 USACO是100分,洛谷是20分 为什么 ...

  3. TCP/IP详解学习笔记(12)-- TCP:传输控制协议

    1.概述      TCP提供了一种可靠的面向连接的字节流运输层服务.      TCP将用户数据打包成报文段,它发送后启动一个定时器,另一端收到的数据进行确认,对失序的数据重新排序,丢弃重复数据,T ...

  4. 关于oracle中传过来的一个多id需要插入到数据库用,分格的存储过程

    create or replace procedure test ( jf_Id in nvarchar2, yf_id in nvarchar2 ) as v_length NUMBER := LE ...

  5. [drp 2]String、StringBuffer和StringBuilder的区别

    导读:在实际运用了,我们会频繁的用到string.stringBuffer和stringBuilder这三个东西,那么,他们之间的区别是什么呢.本文主要是从拼写SQL语句的角度,来阐释他们之间的区别! ...

  6. [SSH 3]以网上商城项目浅谈spring配置

    导读:在做ITOO项目的时候,就用到了容器+反射,从而运用了依赖注入和依赖查找.如果看过WCF端的配置文件,那么对于这个spring的配置就很容易理解.本篇博客,是对于自己做的一个小项目中所运用到的s ...

  7. HTML5--》点击显示隐藏内容

    <details>浏览器支持比较差,可以用JavaScript实现这种功能. <!doctype html> <html> <head> <met ...

  8. NSBundle 的使用

    NSBundle 读取图片 plist text NSBundle *mainbundle=[NSBundle mainBundle]; //使mainBundle 对象获取图片的路径 NSStrin ...

  9. Linux之通配符与转义字符

    通配符: *:代表任意字符,可以为空字符 ?:代表一个字符,不可以为空字符 转义字符: \

  10. 实例分析ELF文件动态链接

    参考文献: <ELF V1.2> <程序员的自我修养---链接.装载与库>第6章 可执行文件的装载与进程 第7章 动态链接 <Linux GOT与PLT> 开发平台 ...