KM is big dream!
脑海中有很多想法,但都未付诸实现,苦思良久,觉得还是不是很清晰,需要花时间来深入的规划一下,望有想法的路人给些指点。
经典的Android应用程序应该有两部分组成,一是运行于前台的Activity和View对象,二是在后台运行的Intent和Service对象。 当屏幕上的某个组件完成了其相应的工作后,会把工作交给另一个组件来执行下一个任务。“交接给另一个组件”是通过Intent实现的。
Intent类相当于Android平台应用程序之间的通信网络。从某些方面来看,Android平台与规模更大的SOA(Service–Oriented Architecture,面向服务的架构)类似,每个Activity调用一种Intent以完成某些任务,而无需确切知道该Intent的接收器是哪个组件 。
Intent属于后期绑定,即将Intent映射和传递到能够处理其特定任务的组件(这个过程)是在运行中进行的,而不是在构建或者编译时进行的。优点:可以将单个组件分离出来,对其进行修改,增强和维护。与调用此组件的程序无关。
Intent主要包含三个主要元素:动作(Action),类别(Category),和数据(Data),以及一个额外的可选的元素集合。
动作和类别都是一个String,数据是以Uri对象的形式定义的。
Uri是通用的URI,包括方案(scheme),授权(authority),和可选的路径
Intent对象包含的所有组件如下:
1. Extras 传递给Intent的额外数据,以Bundle的形式定义
2. Component 指定Intent使用的显示包名,可选,通常是从动作,类型,类别导出的
3. Type 指定显示MIME(与URI解析对比)
4. Category 相关Intent的其他元数据(例如android.intent.category.LAUNCHER)
5. Data 以URI形式表示的数据(例如:content://content/1)
6. Action 指示动作的完成限定String
Intent定义通常表示动作,数据和属性(例如类别)的组合。系统将这些标识为一种语言使用,以正确解析应该用哪个类来处理。
一个组件(如:Activity)可以进行隐式Intent调用或者显示Intent调用。
隐式Intent调用是指调用中有平台决定哪个组件最合适处理该Intent
显示Intent调用可以通过使用Intent(Context ctx,Class cls)的形式。利用这个方法,可以直接传递对Activity或者Service类的引用来处理Intent。这是一种紧耦合的方式。
Intent intent = New Intent(Intent.ACTION_VIEW,Uri.parse(this.link)); startActivity(intent);
通过一个动作和一些数据来创建一个Intent类实例变量。对于改动做,我们使用了String常量Intent.ACTION_VIEW。该常量的值是“android.app.action.View”。这是一个包含有包名的完整限定String,以保证其唯一性。Intent有很多类似这样的常量,表示常用的动作。例如:Intent.ACTION_EDIT, Intent.ACTION_INSERT 和Intent.ACTION_DELETE。完整的常量列表http://developer.android.com/intl/ja/reference/android/content/Intent.html
该声明中的数据时使用Uri.paese(link)指定一个Uri(这里link包是一个http的url)。Parse(String s)方法只是解析URI部分并创建一个Uri对象。
基本上,类型可以通过Uri或方案,授权和路径的组合得到。这使得适合的组件能够响应startActivit(intent)请求。并提供由该Uri所标识的内容。
也就是说,当你指定了动作(ACTION_VIEW)以及数据(http://somehost/somepath),后台就会自动用调用浏览器来处理这个Intent。这就是动作中后期绑定。Android使用同一个概念却包含了很多其他的内置的数据类型。(如果有必要,可以自己定义自己的数据类型)
例如
Intent intent = New Intent(Intent.ACTION_VIEW,Uri.parse(content://contacts/people/1)); startActivity(intent);
用“联系人”的界面 显示联系人编号是1的人的信息
Intent intent = New Intent(Intent.ACTION_VIEW,Uri.parse( tel:123 )); startActivity(intent);
显示电话号码为123的信息
Intent intent = New Intent(Intent.ACTION_VIEW,Uri.parse(content://contacts/people)); startActivity(intent);
用“联系人”的界面 显示一个联系人List
Intent intent = New Intent(Intent.Action.ACTION_VIEW,Uri.parse(“geo:0,0?q=”+this.location.getText().toString())); startActivity(intent);
用googleMap显示输入画面中location TextBox里输入的位置的地图信息。
以上用的都是一个动作,Intent.ACTION_VIEW,而数据却不同
Intent intent = New Intent(Intent.ACTION_DIAL,Uri.parse( tel:123 )); startActivity(intent);
将123输入的电话的拨号面板上
Intent intent = New Intent(Intent.ACTION_CALL,Uri.parse( tel:123 )); startActivity(intent);
拨打电话号码123
以上用的都是一个数据tel:123,但是动作不同
以上全是隐式调用。
解析Intent
只有三个类型的android组件可以注册成为Intent句柄:Activity,BroadcaseReceiver和Service。
这些组件通常在AndroidManifest.xml文件中使用<intent-filter>元素向平台注册,成为特定Intent类型的目标。每一个<intent-filter>元素被解析成一个IntentFilter对象。
一个包安装到平台上时,其中的组件将注册到平台中,一旦平台建立了一个Intent过滤器的注册表项,他基本上就知道了如何把收到的Intent请求映射到已注册的正确Activity,Broadcast-Receiver或Service
请求Intent时,Android平台使用Intent的动作,数据和类型,通过已注册的过滤器,按照动作和类别必须匹配,如果指定数据类型的话,数据类型必须匹配,或者数据方案,授权,路径的组合比如匹配两条基本规则进行解析。
动作与类别我们可以归结为String,动作需要一个字符串表示,类别可能需要多个字符串表示。
如果不在<intent-filter>中指定动作,则表示IntentFilter与Intent中的任何动作都匹配。
至于类别,IntentFilter的类别是Intent的一个超集。除了Intent指定的类别外,一个IntentFilter可以有多个其他的类别,但至少包含Intent指定的类别。其次,不包含类别的IntentFilter只与不包含类别的Intent匹配。
因此,动作和类别必须首先匹配。
<activity android:name=".ReviewList" android:label="@string/app_name"> <intent-filter> <action android:name="com.msi.manning.restautaut.VIEW_LIST" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
Intent intent = new Intent(Constants.INTENT_ACTION_LIST); startActivity(intent);
Activity 的类别标志DEFAULT表示,当某个特定类型的数据的默认动作(如,用户按下按钮)被执行时,该Activity应该启动。通常状况下,该类别标志是在IntentFilter中指定的,而不需要在Intent中出现(此时,过滤器仍将进行匹配,且过滤器的类别是Intent类别的超集)
数据
数据或者是一个显示的MIME类型,或者是方案,授权和路径的组合。
方案,授权和路径组合Uri:weather://com.msi.manning/loc?zip=12345
方案 授权 路径
MIME类型的Uri:content://com.google.provider.NotePad/notes
方案,授权和路径与MIME类型的区别在于,MIME中 “content://”这种表示形式表示该数据类型优先于平台的数据类型。这种数据类型本身就是在提供内容的程序包的manifest文件中定义的。
定义Intentfiler类时,会因方案,授权和路径的不同,已决定Intentfiler和Intent的匹配。具体匹配规则如下:
1, 如果有方案而没有类型,则包含任何类型的Intent都匹配
2, 如果有类型而没有方案,则包含任何方案的Intent都匹配
3, 如果没有类型也没有方案,则与既不包含类型也不包含方案的Intent匹配
4, 如果执行了授权,则必须指定方案
5, 如果制定了路径,则必须指定方案和授权
(这段话,我怎么觉得这么难理解啊,这里的方案和类型,我都已经搞不明白了)
给出一个用Uri匹配的例子
IntentFilter
<intent-filter> <action android:name=”android.intent.action.VIEW” /> <category android:name=”android.intent.category.DEFAULT” /> <data android:scheme=”weather” android:host=”com.msi.manning” /> </intent-filter>
Intent = New Intent(Intent.ACTION_VIEW,Uri.parse(“weather://com.msi.manning/loc?zip=1234”))
再给一个例子,截取自 androidmanifest.xml
<activity android:name=”ReportViewDetail” android:lable=”@string/app_name_view_detail”> <intent-filter> <action android:name=”android.intent.action.VIEW” /> <category android:name=”android.intent.category.DEFAULT” /> <data android:scheme=”weather” android:host=”com.msi.manning” /> </intent-filter> <intent-filter> <action android:name=”android.intent.action.VIEW” /> <data android:scheme=”weather” android:host=”com.msi.manning” /> </intent-filter> <intent-filter> <action android:name=”android.intent.action.MAIN” /> <category android:name=”android.intent.category.LAUNCHER” /> </intent-filter> </activity>
介绍几种Android提供的活动
Intent.ACTION_VIEW
geo:latitude,longitude
打开地图应用程序并显示指定的纬度和精度
Intent.ACTION_VIEW
geo:0,0?q=street+address
打开地图应用程序并显示指定的地址
Intent.ACTION_CALL
tel:phone_number
打开电话程序并拨打指定的电话号码
Intent.ACTION_DIAL
tel:phone_number
打开电话程序并拨下指定的电话号码(但不拨出)
Intent.ACTION_DIAL
Voicemail:
打开电话应用程序并拨下该语音信箱的电话号码(但不拨出)
Intent.ACTION_VIEW
http://web_address
打开浏览器应用程序并显示指定URL
Intent.ACTION_VIEW
https://web_address
打开浏览器应用程序并显示指定URL
Intent.ACTION_WEB_SEARCH
plain_text
打开浏览器应用程序并用google搜索引擎
Intent对象用于在android应用程序中之间的切换,虽然这是Intent在android的主要用途,但并不是唯一用途。使用Context类提供的众多方法之一,Intent还可以用于向所有已经配置的广播接收器广播事件。
sendBroadcase(Intent intent) --------------------- 广播Intent的简单形式
广播Intent时,实际上是重用了Intent的概念,以在后台发送事件。虽然这里使用的仍然是Intent,但是与调用前台Activity路径的Intent有所不同。广播Intent不调用Activity。
MyLocation myLocation = new MyLocation();
private void locationClick() {
myLocation.getLocation(this, locationResult));
}
public LocationResult locationResult = new LocationResult(){
@Override
public void gotLocation(final Location location){
//Got the location!
});
}
};
import java.util.Timer;
import java.util.TimerTask;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
public class MyLocation {
Timer timer1;
LocationManager lm;
LocationResult locationResult;
boolean gps_enabled=false;
boolean network_enabled=false;
public boolean getLocation(Context context, LocationResult result)
{
//I use LocationResult callback class to pass location value from MyLocation to user code.
locationResult=result;
if(lm==null)
lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
//exceptions will be thrown if provider is not permitted.
try{gps_enabled=lm.isProviderEnabled(LocationManager.GPS_PROVIDER);}catch(Exception ex){}
try{network_enabled=lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);}catch(Exception ex){}
//don't start listeners if no provider is enabled
if(!gps_enabled && !network_enabled)
return false;
if(gps_enabled)
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListenerGps);
if(network_enabled)
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListenerNetwork);
timer1=new Timer();
timer1.schedule(new GetLastLocation(), 20000);
return true;
}
LocationListener locationListenerGps = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerNetwork);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
LocationListener locationListenerNetwork = new LocationListener() {
public void onLocationChanged(Location location) {
timer1.cancel();
locationResult.gotLocation(location);
lm.removeUpdates(this);
lm.removeUpdates(locationListenerGps);
}
public void onProviderDisabled(String provider) {}
public void onProviderEnabled(String provider) {}
public void onStatusChanged(String provider, int status, Bundle extras) {}
};
class GetLastLocation extends TimerTask {
@Override
public void run() {
lm.removeUpdates(locationListenerGps);
lm.removeUpdates(locationListenerNetwork);
Location net_loc=null, gps_loc=null;
if(gps_enabled)
gps_loc=lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(network_enabled)
net_loc=lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
//if there are both values use the latest one
if(gps_loc!=null && net_loc!=null){
if(gps_loc.getTime()>net_loc.getTime())
locationResult.gotLocation(gps_loc);
else
locationResult.gotLocation(net_loc);
return;
}
if(gps_loc!=null){
locationResult.gotLocation(gps_loc);
return;
}
if(net_loc!=null){
locationResult.gotLocation(net_loc);
return;
}
locationResult.gotLocation(null);
}
}
public static abstract class LocationResult{
public abstract void gotLocation(Location location);
}
}