概念:统一资源标识符(Uniform Resource Identifier)
组成部分:
1.访问资源的命名机制(scheme)
2.存放资源的主机名(authority)
3.资源自身的名称,由路径表示(path)
格式:scheme:// authority//path,其中authority中又包括了host和port两部分。
content://com.example.project:200/folder/subfolder/etc
\---------/ \-------------------/ \----------------------------/
Scheme host port path
\-------------------/
Authority
用处:uri主要用来表示一个资源。这个资源有很多种类,包括图片,视频,文件等。
针对资源的种类,uri用以下几种scheme标识:
1.Content:主要操作的是ContentProvider,所以它代表的是数据库中的某个资源
2.http:一个网站资源
3.file:本地机器上的某个资源
4.git:git仓库中某个资源
5.ftp:服务器上的某个资源
6.ed2k:(电驴协议)
7.等等……….
http://write.blog.csdn.net/postedit/7313543,
file:///c:/WINDOWS/clock.avi
git://github.com/user/project-name.git
ftp://user1:1234@地址
ed2k://|file|%5BMAC%E7%89%88%E6%9E%81%E5%93%81%E9%A3%9E%E8%BD%A69%EF%BC%9A%E6%9C%80%E9%AB%98%E9%80%9A%E7%BC%89%5D.%5BMACGAME%5DNeed.For.Speed.Most.Wanted.dmg|4096933888|2c55f0ad2cb7f6b296db94090b63e88e|h=ltcxuvnp24ufx25h2x7ugfaxfchjkwxa|/
位置:frameworks\base\core\java\android\net\Uri.java
android中对Uri分为两大类:Hierarchical和opaque
Hierarchical:以”//”分级的Uri,比如:http://google.com,content://com.example.project:200/
opaque:没有使用”//”分级的Uri,比如:mailto:nobody@google.com
//Uri是一个抽象类,支持序列化。提供了各种函数用于获取组成Uri的各个部分(scheme,authority,path)
public abstract class Uri implements Parcelable, Comparable<Uri> {
//私有构造函数
private Uri() {}
/**
* Returns true if this URI is hierarchical like "http://google.com".
* Absolute URIs are hierarchical if the scheme-specific part starts with
* a '/'. Relative URIs are always hierarchical.
*/
//当前uri是否是Hierarchical类型
public abstract boolean isHierarchical();
/**
* Returns true if this URI is opaque like "mailto:nobody@google.com". The
* scheme-specific part of an opaque URI cannot start with a '/'.
*/
//当前uri是否是Opaque类型
public boolean isOpaque() {
return !isHierarchical();
}
/**
* Gets the scheme of this URI. Example: "http"
*
* @return the scheme or null if this is a relative URI
*/
@Nullable
public abstract String getScheme();
/**
* Gets the decoded authority part of this URI. For
* server addresses, the authority is structured as follows:
* {@code [ userinfo '@' ] host [ ':' port ]}
*
* Examples: "google.com", "bob@google.com:80"
*
* @return the authority for this URI or null if not present
*/
@Nullable
public abstract String getAuthority();
/**
* Gets the decoded user information from the authority.
* For example, if the authority is "nobody@google.com", this method will
* return "nobody".
*
* @return the user info for this URI or null if not present
*/
@Nullable
public abstract String getUserInfo();
/**
* Gets the encoded host from the authority for this URI. For example,
* if the authority is "bob@google.com", this method will return
* "google.com".
*
* @return the host for this URI or null if not present
*/
@Nullable
public abstract String getHost();
/**
* Gets the port from the authority for this URI. For example,
* if the authority is "google.com:80", this method will return 80.
*
* @return the port for this URI or -1 if invalid or not present
*/
public abstract int getPort();
/**
* Gets the decoded path.
*
* @return the decoded path, or null if this is not a hierarchical URI
* (like "mailto:nobody@google.com") or the URI is invalid
*/
@Nullable
public abstract String getPath();
/**
* Creates a Uri from a file. The URI has the form
* "file://". Encodes path characters with the exception of
* '/'.
*
*
Example: "file:///tmp/android.txt"
*
* @throws NullPointerException if file is null
* @return a Uri for the given file
*/
//提供获取Uri对象的静态函数。返回代表File的uri
public static Uri fromFile(File file) {
if (file == null) {
throw new NullPointerException("file");
}
PathPart path = PathPart.fromDecoded(file.getAbsolutePath());
return new HierarchicalUri(
"file", Part.EMPTY, path, Part.NULL, Part.NULL);
}
/**
* Creates a Uri which parses the given encoded URI string.
*
* @param uriString an RFC 2396-compliant, encoded URI
* @throws NullPointerException if uriString is null
* @return Uri for this given uri string
*/
//提供获取Uri对象的静态函数。返回传入的string包装成uri
public static Uri parse(String uriString) {
return new StringUri(uriString);
}
/**
* Creates an opaque Uri from the given components. Encodes the ssp
* which means this method cannot be used to create hierarchical URIs.
*
* @param scheme of the URI
* @param ssp scheme-specific-part, everything between the
* scheme separator (':') and the fragment separator ('#'), which will
* get encoded
* @param fragment fragment, everything after the '#', null if undefined,
* will get encoded
*
* @throws NullPointerException if scheme or ssp is null
* @return Uri composed of the given scheme, ssp, and fragment
*
* @see Builder if you don't want the ssp and fragment to be encoded
*/
//提供获取Uri对象的静态函数。根据提供的参数返回Opaque类型的uri
public static Uri fromParts(String scheme, String ssp,
String fragment) {
if (scheme == null) {
throw new NullPointerException("scheme");
}
if (ssp == null) {
throw new NullPointerException("ssp");
}
return new OpaqueUri(scheme, Part.fromDecoded(ssp),
Part.fromDecoded(fragment));
}
以上是Uri.java中比较常用的几个函数。至于其子类OpaqueUri,HierarchicalUri ,StringUri,实现上并不是太难,不做展开。这里需要特殊说明的是Uri的内部类Builder,这个Builder是一个辅助类,用于Uri对象的构建,并发不安全。
public static final class Builder {
private String scheme;
private Part opaquePart;
private Part authority;
private PathPart path;
private Part query;
private Part fragment;
/**
* Constructs a new Builder.
*/
//Builder的构造函数
public Builder() {}
//提供了众多的函数去获取Builder对象。
public Builder scheme(String scheme) {
this.scheme = scheme;
return this;
}
......
/**
* Constructs a Uri with the current attributes.
*
* @throws UnsupportedOperationException if the URI is opaque and the
* scheme is null
*/
//获取Uri对象
public Uri build() {
if (opaquePart != null) {
if (this.scheme == null) {
throw new UnsupportedOperationException(
"An opaque URI must have a scheme.");
}
return new OpaqueUri(scheme, opaquePart, fragment);
} else {
// Hierarchical URIs should not return null for getPath().
PathPart path = this.path;
if (path == null || path == PathPart.NULL) {
path = PathPart.EMPTY;
} else {
// If we have a scheme and/or authority, the path must
// be absolute. Prepend it with a '/' if necessary.
if (hasSchemeOrAuthority()) {
path = PathPart.makeAbsolute(path);
}
}
return new HierarchicalUri(
scheme, authority, path, query, fragment);
}
}
......
}
Builder 对象的构建有多种方式,可以根据组成Uri的各个部分花式获取Builder 对象,具体可以详细看源码。之后使用Builder 对象调用build()函数即可获得Uri对象。至于获取的Uri对象的类型是Hierarchical,还是opaque,或者是content,file,git等等,就看你是传入什么参数去获取Builder 对象了。
位置:frameworks\base\core\java\android\content\UriMatcher.java
Uri与UriMatcher的关系:
Uri保存了资源文件的路径,代表了资源文件。一个应用内有很多资源,如果我们想把其中一部分资源共享给其他应用,并集中起来管理,方便查询和匹配,这就需要另外一个类来协调,这个类就是UriMatcher。
public class UriMatcher
{
public static final int NO_MATCH = -1; //没有匹配到uri,返回-1
private static final int EXACT = 0;
private static final int NUMBER = 1; //此处用来代表通配符"#"
private static final int TEXT = 2; //此处用来代表任意字符"*"
//mCode是UriMatcher用来标识Uri的,如果匹配到Uri,则返回此mCode,否则返回NO_MATCH
private int mCode;
private int mWhich;//用来标识特殊字符的,比如通配符"#",任意字符"*"
private String mText;//用来保存Uri的某个组成部分的。比如Scheme等
private ArrayList mChildren;//使用ArrayList来保存完整的Uri
/**
* Creates the root node of the URI tree.
*
* @param code the code to match for the root URI
*/
public UriMatcher(int code)
{
mCode = code;
mWhich = -1;
mChildren = new ArrayList();
mText = null;
}
private UriMatcher()
{
mCode = NO_MATCH;
mWhich = -1;
mChildren = new ArrayList();
mText = null;
}
/**
* Add a URI to match, and the code to return when this URI is
* matched. URI nodes may be exact match string, the token "*"
* that matches any text, or the token "#" that matches only
* numbers.
*
* Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
* this method will accept a leading slash in the path.
*
* @param authority the authority to match
* @param path the path to match. * may be used as a wild card for
* any text, and # may be used as a wild card for numbers.
* @param code the code that is returned when a URI is matched
* against the given components. Must be positive.
*/
/*保存Uri。把Uri分段成authority和path两部分,当成参数传入addURI()函数中。
*必须传入code,且不小于0,否则抛出异常。
*/
public void addURI(String authority, String path, int code)
{
if (code < 0) {
throw new IllegalArgumentException("code " + code + " is invalid: it must be positive");
}
//把path按照"/"分割并保存到string数组tokens 中。
String[] tokens = null;
if (path != null) {
String newPath = path;
// Strip leading slash if present.
if (path.length() > 1 && path.charAt(0) == '/') {
newPath = path.substring(1);
}
tokens = newPath.split("/");
}
int numTokens = tokens != null ? tokens.length : 0;
UriMatcher node = this;
//循环tokens数组
for (int i = -1; i < numTokens; i++) {
//先把authority保存到当前UriMatcher对象的ArrayList集合mChildren中
String token = i < 0 ? authority : tokens[i];
ArrayList children = node.mChildren;
int numChildren = children.size();
UriMatcher child;
int j;
for (j = 0; j < numChildren; j++) {
child = children.get(j);
if (token.equals(child.mText)) {
node = child;
break;
}
}
//新构建UriMatcher对象,把分割的path分别保存进去,形成单链表结构
if (j == numChildren) {
// Child not found, create it
child = new UriMatcher();
//如果path分割后得到通配符"#",mWhich赋值为NUMBER
if (token.equals("#")) {
child.mWhich = NUMBER;
//如果path分割后得到通配符"*",mWhich赋值为TEXT
} else if (token.equals("*")) {
child.mWhich = TEXT;
} else {
child.mWhich = EXACT;
}
child.mText = token;//保存分割的path或者authority
//新构建的UriMatcher对象保存到当前操作节点的ArrayList集合中
node.mChildren.add(child);
node = child;//操作节点后移
}
}
node.mCode = code;
}
/**
* Try to match against the path in a url.
*
* @param uri The url whose path we will match against.
*
* @return The code for the matched node (added using addURI),
* or -1 if there is no matched node.
*/
//循环链表结构的UriMatcher去匹配Uri,匹配到返回mCode,否则返回NO_MATCH
public int match(Uri uri)
{
final List pathSegments = uri.getPathSegments();
final int li = pathSegments.size();
UriMatcher node = this;
if (li == 0 && uri.getAuthority() == null) {
return this.mCode;
}
//循环Uri的构成(authority+path)
for (int i=-1; i//获取authority或者path的分割部分
String u = i < 0 ? uri.getAuthority() : pathSegments.get(i);
ArrayList list = node.mChildren;
if (list == null) {
break;
}
node = null;
int lj = list.size();
//循环当前UriMatcher的ArrayList集合
for (int j=0; j//匹配authority或者path的分割部分
switch (n.mWhich) {
case EXACT:
if (n.mText.equals(u)) {
node = n;
}
break;
case NUMBER:
int lk = u.length();
for (int k=0; kchar c = u.charAt(k);
if (c < '0' || c > '9') {
break which_switch;
}
}
node = n;
break;
case TEXT:
node = n;
break;
}
if (node != null) {
break;
}
}
if (node == null) {
return NO_MATCH;
}
}
return node.mCode;
}
}
位置:packages\providers\TelephonyProvider\src\com\android\providers\telephony\TelephonyProvider.java
TelephonyProvider借助SQLiteOpenHelper创建了telephony.db数据库,并新建了一些table。希望通过TelephonyProvider共享一些数据给其他应用。这些共享数据的Uri被保存到了UriMatcher中,当其他应用使用Uri通过TelephonyProvider访问这些共享数据时,需要先匹配一下Uri是否在UriMatcher中,如果存在,则允许访问,如果不存在,则不允许访问。避免了该应用内所有数据都被公开的可能。
public class TelephonyProvider extends ContentProvider
{
//数据库与表
private static final String DATABASE_NAME = "telephony.db";
private static final String CARRIERS_TABLE = "carriers";
private static final String CARRIERS_TABLE_TMP = "carriers_tmp";
private static final String SIMINFO_TABLE = "siminfo";
private static final String SIMINFO_TABLE_TMP = "siminfo_tmp";
private static final UriMatcher s_urlMatcher = new UriMatcher(UriMatcher.NO_MATCH);
......
//Uri加入到UriMatcher中
static {
s_urlMatcher.addURI("telephony", "carriers", URL_TELEPHONY);
s_urlMatcher.addURI("telephony", "carriers/current", URL_CURRENT);
s_urlMatcher.addURI("telephony", "carriers/#", URL_ID);
s_urlMatcher.addURI("telephony", "carriers/restore", URL_RESTOREAPN);
s_urlMatcher.addURI("telephony", "carriers/preferapn", URL_PREFERAPN);
s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update", URL_PREFERAPN_NO_UPDATE);
s_urlMatcher.addURI("telephony", "carriers/preferapnset", URL_PREFERAPNSET);
s_urlMatcher.addURI("telephony", "siminfo", URL_SIMINFO);
s_urlMatcher.addURI("telephony", "carriers/subId/*", URL_TELEPHONY_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/current/subId/*", URL_CURRENT_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/restore/subId/*", URL_RESTOREAPN_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/preferapn/subId/*", URL_PREFERAPN_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/preferapn_no_update/subId/*",
URL_PREFERAPN_NO_UPDATE_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/preferapnset/subId/*",
URL_PREFERAPNSET_USING_SUBID);
s_urlMatcher.addURI("telephony", "carriers/update_db", URL_UPDATE_DB);
s_urlMatcher.addURI("telephony", "carriers/delete", URL_DELETE);
// Only called by DevicePolicyManager to manipulate DPC records.
s_urlMatcher.addURI("telephony", "carriers/dpc", URL_DPC);
// Only called by DevicePolicyManager to manipulate a DPC record with certain _ID.
s_urlMatcher.addURI("telephony", "carriers/dpc/#", URL_DPC_ID);
// Only called by Settings app, DcTracker and other telephony components to get APN list
// according to whether DPC records are enforced.
s_urlMatcher.addURI("telephony", "carriers/filtered", URL_FILTERED);
// Only called by Settings app, DcTracker and other telephony components to get a
// single APN according to whether DPC records are enforced.
s_urlMatcher.addURI("telephony", "carriers/filtered/#", URL_FILTERED_ID);
// Only Called by DevicePolicyManager to enforce DPC records.
s_urlMatcher.addURI("telephony", "carriers/enforce_managed", URL_ENFORCE_MANAGED);
s_currentNullMap = new ContentValues(1);
s_currentNullMap.put(CURRENT, "0");
s_currentSetMap = new ContentValues(1);
s_currentSetMap.put(CURRENT, "1");
}
//更该telephony.db数据库
@Override
public synchronized int update(Uri url, ContentValues values, String where, String[] whereArgs)
{
int count = 0;
int uriType = URL_UNKNOWN;
int subId = SubscriptionManager.getDefaultSubscriptionId();
checkPermission();
syncBearerBitmaskAndNetworkTypeBitmask(values);
SQLiteDatabase db = getWritableDatabase();
//uri匹配才可以更新数据库
int match = s_urlMatcher.match(url);
switch (match)
{
case URL_TELEPHONY:
{
if (!values.containsKey(EDITED)) {
values.put(EDITED, CARRIER_EDITED);
}
// Replace on conflict so that if same APN is present in db with edited
// as UNEDITED or USER/CARRIER_DELETED, it is replaced with
// edited USER/CARRIER_EDITED
count = db.updateWithOnConflict(CARRIERS_TABLE, values, where +
" and " + IS_NOT_OWNED_BY_DPC, whereArgs,
SQLiteDatabase.CONFLICT_REPLACE);
break;
}
......
case URL_SIMINFO: {
count = db.update(SIMINFO_TABLE, values, where, whereArgs);
uriType = URL_SIMINFO;
break;
}
default: {
throw new UnsupportedOperationException("Cannot update that URL: " + url);
}
}
......
return count;
}
显示网页:
1. Uri uri = Uri.parse(“http://www.google.com“);
2. Intent it = new Intent(Intent.ACTION_VIEW,uri);
3. startActivity(it);
显示地图:
1. Uri uri = Uri.parse(“geo:38.899533,-77.036476”);
2. Intent it = new Intent(Intent.Action_VIEW,uri);
3. startActivity(it);
路径规划:
1. Uri uri = Uri.parse(“http://maps.google.com/maps?f=d&saddr=startLat%20startLng&daddr=endLat%20endLng&hl=en“);
2. Intent it = new Intent(Intent.ACTION_VIEW,URI);
3. startActivity(it);
拨打电话:
调用拨号程序
1. Uri uri = Uri.parse(“tel:xxxxxx”);
2. Intent it = new Intent(Intent.ACTION_DIAL, uri);
3. startActivity(it);
1. Uri uri = Uri.parse(“tel.xxxxxx”);
2. Intent it =new Intent(Intent.ACTION_CALL,uri);
3. 要使用这个必须在配置文件中加入
发送SMS/MMS
调用发送短信的程序
1. Intent it = new Intent(Intent.ACTION_VIEW);
2. it.putExtra(“sms_body”, “The SMS text”);
3. it.setType(“vnd.android-dir/mms-sms”);
4. startActivity(it);
发送短信
1. Uri uri = Uri.parse(“smsto:0800000123”);
2. Intent it = new Intent(Intent.ACTION_SENDTO, uri);
3. it.putExtra(“sms_body”, “The SMS text”);
4. startActivity(it);
发送彩信
1. Uri uri = Uri.parse(“content://media/external/images/media/23”);
2. Intent it = new Intent(Intent.ACTION_SEND);
3. it.putExtra(“sms_body”, “some text”);
4. it.putExtra(Intent.EXTRA_STREAM, uri);
5. it.setType(“image/png”);
6. startActivity(it);
发送Email
1.
2. Uri uri = Uri.parse(“mailto:xxx@abc.com”);
3. Intent it = new Intent(Intent.ACTION_SENDTO, uri);
4. startActivity(it);
1. Intent it = new Intent(Intent.ACTION_SEND);
2. it.putExtra(Intent.EXTRA_EMAIL, “me@abc.com”);
3. it.putExtra(Intent.EXTRA_TEXT, “The email body text”);
4. it.setType(“text/plain”);
5. startActivity(Intent.createChooser(it, “Choose Email Client”));
1. Intent it=new Intent(Intent.ACTION_SEND);
2. String[] tos={“me@abc.com”};
3. String[] ccs={“you@abc.com”};
4. it.putExtra(Intent.EXTRA_EMAIL, tos);
5. it.putExtra(Intent.EXTRA_CC, ccs);
6. it.putExtra(Intent.EXTRA_TEXT, “The email body text”);
7. it.putExtra(Intent.EXTRA_SUBJECT, “The email subject text”);
8. it.setType(“message/rfc822”);
9. startActivity(Intent.createChooser(it, “Choose Email Client”));
添加附件
1. Intent it = new Intent(Intent.ACTION_SEND);
2. it.putExtra(Intent.EXTRA_SUBJECT, “The email subject text”);
3. it.putExtra(Intent.EXTRA_STREAM, “[url=]file:///sdcard/mysong.mp3[/url]”);
4. sendIntent.setType(“audio/mp3”);
5. startActivity(Intent.createChooser(it, “Choose Email Client”));
播放多媒体
1.
2. Intent it = new Intent(Intent.ACTION_VIEW);
3. Uri uri = Uri.parse(“[url=]file:///sdcard/song.mp3[/url]”);
4. it.setDataAndType(uri, “audio/mp3”);
5. startActivity(it);
1. Uri uri = Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, “1”);
2. Intent it = new Intent(Intent.ACTION_VIEW, uri);
3. startActivity(it);
Uninstall 程序
1. Uri uri = Uri.fromParts(“package”, strPackageName, null);
2. Intent it = new Intent(Intent.ACTION_DELETE, uri);
3. startActivity(it);
//调用相册
public static final String MIME_TYPE_IMAGE_JPEG = “image/*”;
public static final int ACTIVITY_GET_IMAGE = 0;
Intent getImage = new Intent(Intent.ACTION_GET_CONTENT);
getImage.addCategory(Intent.CATEGORY_OPENABLE);
getImage.setType(MIME_TYPE_IMAGE_JPEG);
startActivityForResult(getImage, ACTIVITY_GET_IMAGE);
//调用系统相机应用程序,并存储拍下来的照片
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
time = Calendar.getInstance().getTimeInMillis();
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(Environment
.getExternalStorageDirectory().getAbsolutePath()+”/tucue”, time + “.jpg”)));
startActivityForResult(intent, ACTIVITY_GET_CAMERA_IMAGE);
uninstall apk
/**未测试
Uri uninstallUri = Uri.fromParts(“package”, “xxx”, null);
returnIt = new Intent(Intent.ACTION_DELETE, uninstallUri);
*/
Uri packageURI = Uri.parse(“package:”+wistatmap);
Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
startActivity(uninstallIntent);
install apk
Uri installUri = Uri.fromParts(“package”, “xxx”, null);
returnIt = new Intent(Intent.ACTION_PACKAGE_ADDED, installUri);
play audio
Uri playUri = Uri.parse(“[url=]file:///sdcard/download/everything.mp3[/url]”);
returnIt = new Intent(Intent.ACTION_VIEW, playUri);
//发送附件
Intent it = new Intent(Intent.ACTION_SEND);
it.putExtra(Intent.EXTRA_SUBJECT, “The email subject text”);
it.putExtra(Intent.EXTRA_STREAM, “[url=]file:///sdcard/eoe.mp3[/url]”);
sendIntent.setType(“audio/mp3”);
startActivity(Intent.createChooser(it, “Choose Email Client”));
//搜索应用
Uri uri = Uri.parse(“market://search?q=pname:pkg_name”);
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);
//where pkg_name is the full package path for an application
//进入联系人页面
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(People.CONTENT_URI);
startActivity(intent);
//查看指定联系人
Uri personUri = ContentUris.withAppendedId(People.CONTENT_URI, info.id);//info.id联系人ID
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(personUri);
startActivity(intent);