其它人的学习文章

http://blog.joycode.com/ghj/archives/author/ghj1976/page/2

Android启动画面Splash

方法一,两个Activity

核心代码:

package ghj1976.HelloWorld;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;

public class SplashActivity extends Activity {

	private final int SPLASH_DISPLAY_LENGHT = 8000; // 延迟八秒

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.splash);

		new Handler().postDelayed(new Runnable() {
			public void run() {
				Intent mainIntent = new Intent(SplashActivity.this,
						HelloWorldActivity.class);
				SplashActivity.this.startActivity(mainIntent);
				SplashActivity.this.finish();
			}

		}, SPLASH_DISPLAY_LENGHT);

	}
}

说明:

Handler().postDelayed  是延迟指定的时间再执行

Handler类主要可以使用如下3个方法来设置执行Runnable对象的时间:

//  立即执行Runnable对象  
public final boolean post(Runnable r);
//  在指定的时间(uptimeMillis)执行Runnable对象  
public final boolean postAtTime(Runnable r, long uptimeMillis);
//  在指定的时间间隔(delayMillis)执行Runnable对象  
public final boolean postDelayed(Runnable r, long delayMillis); 

有关 Handler 类的更详细可以看这篇文章:http://book.51cto.com/art/201006/207064.htm

下面两行代码启动一个新的Activity,同时关闭当前Activity。

SplashActivity.this.startActivity(mainIntent);

SplashActivity.this.finish();

对 finish 方法的解释如下: http://android.toolib.net/reference/android/app/Activity.html

Call this when your activity is done and should be closed. The ActivityResult is propagated back to whoever launched you via onActivityResult().

image

图来自: http://www.ibm.com/developerworks/cn/opensource/os-cn-android-actvt/

如上所示,Android 程序员可以决定一个 Activity 的“生”,但不能决定它的“死”,也就时说程序员可以启动一个 Activity,但是却不能手动的“结束”一个 Activity。

当你调用 Activity.finish()方法时,结果和用户按下 BACK 键一样:告诉 Activity Manager 该 Activity 实例完成了相应的工作,可以被“回收”。

随后 Activity Manager 激活处于栈第二层的 Activity 并重新入栈,同时原 Activity 被压入到栈的第二层,从 Active 状态转到 Paused 状态。

例如上面例子中:从 SplashActivity 中启动了 HelloWorldActivity,则当前处于栈顶端的是 HelloWorldActivity,第二层是 SplashActivity 。

当我们调用 SplashActivity.finish()方法时(我们是在SplashActivity中通过SplashActivity.this.finish()调用的),SplashActivity 从 Active 状态转换 Stoped 状态,并被系统从栈中移除,标志可以被“回收”。

Activity 的状态与它在栈中的位置关系如下图:

image

 

上图的例子是

从 Activity1 中启动了 Activity2,则当前处于栈顶端的是 Activity2,第二层是 Activity1,当我们在 Activity2中调用 Activity2.finish()方法时,Activity Manager 重新激活 Activity1 并入栈,Activity2 从 Active 状态转换 Stoped 状态,同时标注Activity2可以被“回收” 。Activity1. onActivityResult(int requestCode, int resultCode, Intent data)方法被执行,Activity2 返回的数据通过 data参数返回给 Activity1。

 

方法二:一个 Activity

 

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<LinearLayout android:id="@+id/splashscreen"
		android:orientation="vertical" android:layout_width="fill_parent"
		android:layout_height="fill_parent">

		<TextView android:id="@+id/info" android:layout_width="fill_parent"
			android:layout_height="wrap_content" android:gravity="center"
			android:paddingTop="10px" android:text="This is a splash !" />
	</LinearLayout>

	<TextView android:layout_width="fill_parent"
		android:paddingTop="10px" android:layout_height="wrap_content"
		android:text="This is a Context" />
</LinearLayout>

说明:

这里有一个id为splashscreen的LinearLayout,是程序启动时显现的部分。当启动完成后,它会被隐藏。

核心代码:

package ghj1976.AndroidTest;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.view.View;
import android.view.Window;
import android.widget.LinearLayout;
import android.widget.TextView;

public class MainActivity extends Activity {

	private LinearLayout splash;
	private TextView tv;

	private static final int STOPSPLASH = 0;
	// time in milliseconds
	private static final long SPLASHTIME = 1000;

	private Handler splashHandler = new Handler() {
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case STOPSPLASH:
				SystemClock.sleep(4000);
				splash.setVisibility(View.GONE);
					break;
			}
			super.handleMessage(msg);
		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getWindow().requestFeature(Window.FEATURE_PROGRESS);
		setContentView(R.layout.main);

		splash = (LinearLayout) findViewById(R.id.splashscreen);
		tv = (TextView) findViewById(R.id.info);
		tv.setText("正在建立数据连接");

		Message msg = new Message();
		msg.what = STOPSPLASH;
		splashHandler.sendMessageDelayed(msg, SPLASHTIME);
	}
}

说明

我们在应用启动后发送一个消息,把 指定区域设置为隐藏, splash.setVisibility(View.GONE);  就实现了 启动界面。

Android到底那个Acitivity启动

启动那个Acitivity有两种方式:implicit(隐藏) intent 和 explicit(明确) intent

Explicit Intent

明确的指定了要启动的Acitivity
比如以下Java代码,明确指定了要启动B:
Intent intent= new Intent(this, B.class) 

 

Implicit Intent


没有明确的指定要启动哪个Activity ,而是通过设置一些Intent Filter来让系统去筛选合适的Acitivity去启动。

当使用startActivity时,隐式Intent解析到一个单一的Activity。如果存在多个Activity都有能力在特定的数据上执行给定的动作的话,Android会从这些中选择最好的进行启动。

 

Implicit Intent 到底发给哪个activity?

这需要进行三个匹配,一个是action,一个是category,一个是data。根据三个的匹配结果,找到应该启动的Activity。

 

Action Implicit Intent

动作匹配指Android Intent Filter包含特定的动作或没有指定的动作。

一个Intent Filter有一个或多个定义的动作,如果没有任何一个能与Intent指定的动作匹配的话,这个Intent Filter在算作是动作匹配检查失败。

<intent-filter>元素中可以包括子元素<action>,比如:
<intent-filter>
<action android:name=”com.example.project.SHOW_CURRENT” />
<action android:name=”com.example.project.SHOW_RECENT” />
<action android:name=”com.example.project.SHOW_PENDING” />
</intent-filter>
一条<intent-filter>元素至少应该包含一个<action>,否则任何Intent请求都不能和该<intent-filter>匹配。如果Intent请求的Action和<intent-filter>中个某一条<action>匹配,那么该Intent就通过了这条<intent-filter>的动作测试。

Category Implicit Intent

种类匹配更为严格。Intent Filter必须包含所有在解析的Intent中定义的种类。一个没有特定种类的Intent Filter只能与没有种类的Intent匹配。

<intent-filter>元素可以包含<category>子元素,比如:
<intent-filter . . . >
<category android:name=”android.Intent.Category.DEFAULT” />
<category android:name=”android.Intent.Category.BROWSABLE” />
</intent-filter>
只有当Intent请求中所有的Category与组件中某一个IntentFilter的<category>完全匹配时,才会让该Intent请求通过测试,IntentFilter中多余的<category>声明并不会导致匹配失败。

Data Implicit Intent

Intent的数据URI中的部分会与Intent Filter中的data标签比较。如果Intent Filter定义scheme,host/authority,path或mimetype,这些值都会与Intent的URI比较。任何不匹配都会导致Intent Filter从列表中删除。没有指定data值的Android Intent Filter会和所有的Intent数据匹配。

  • mimetype是正在匹配的数据的数据类型。当匹配数据类型时,你可以使用通配符来匹配子类型(例如,earthquakes/*)。如果Intent Filter指定一个数据类型,它必须与Intent匹配;没有指定数据的话全部匹配。
  • scheme是URI部分的协议——例如,http:,mailto:,tel:。
  • host-name或“data authority”是介于URI中scheme和path之间的部分(例如,www.google.com)。匹配主机名时,Intent Filter的scheme也必须通过匹配。
  • 数据path是紧接在“data authority”的后面(例如,/ig)。path只在scheme和host-name部分都匹配的情况下才匹配。

数据在<intent-filter>中的描述如下:
<intent-filter . . . >
<data android:type=”video/mpeg” android:scheme=”http” . . . />
<data android:type=”audio/mpeg” android:scheme=”http” . . . />
</intent-filter>
<data>元素指定了希望接受的Intent请求的数据URI和数据类型,URI被分成三部分来进行匹配:scheme、authority和path。其中,用setData()设定的Inteat请求的URI数据类型和scheme必须与IntentFilter中所指定的一致。若IntentFilter中还指定了authority或path,它们也需要相匹配才会通过测试。

解析出来后的处理逻辑

如果这个过程中多于一个组件解析出来的话,它们会以优先度来排序,可以在Android Intent Filter的节点里添加一个可选的标签。最高等级的组件会返回。

Android本地的应用程序组件和第三方应用程序一样,都是Intent解析过程中的一部分。它们没有更高的优先度,可以被新的Activity完全的代替,这些新的Activity宣告自己的Intent Filter能响应相同的动作请求。

 

 

应用程序的启动

AndroidManifest.xml 文件中,把那个 activity 配置了 <action android:name="android.intent.action.MAIN" /> ,那就就是最先被启动的 Activity ,如果多个设置了,则第一个设置的是最新被启动的 Activity。

如下图设置,是 .SplashActivity 先启动的

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="ghj1976.HelloWorld" android:versionCode="1"
	android:versionName="1.0">
	<uses-sdk android:minSdkVersion="3" />
	<application android:icon="@drawable/icon" android:label="@string/app_name">
		<activity android:name=".HelloWorldActivity" android:label="@string/app_name">
		</activity>
		<activity android:name=".SplashActivity" android:label="@string/app_name">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
</manifest>

上面代码中的

activity android:name:  activity 的类名,必须有。

android.intent.action.MAIN   决定应用程序最先启动的Activity

android.intent.category.LAUNCHER     决定应用程序是否显示在程序列表里

 

参考资料

Intent Filter匹配

http://www.moandroid.com/?p=1651

什么时候加上android.intent.category.DEFAULT和LAUNCHER

http://www.crazydevelop.com/content.aspx?ID=6416

Android 开发之:Intent.createChooser() 妙用

http://www.oschina.net/bbs/thread/9299

Android 任务共用性Affinity

建议首先阅读下面这篇文章,这样才能对本文有所了解:

Android Application Task Activities的关系
http://www.cnblogs.com/ghj1976/archive/2011/04/29/2032412.html 尤其要明白 Task 是啥。

 

什么是Affinity

在某些情况下,Android需要知道一个Activity属于哪个Task,即使它没有被启动到一个具体的Task里。这是通过任务共用性(Affinities)完成的。任务共用性(Affinities)为这个运行一个或多个Activity的Task提供了一个独特的静态名称,默认的一个活动的任务共用性(Affinity)是实现了该Activity的.apk包的名字。

 

当开始一个没有Intent.FLAG_ACTIVITY_NEW_TASK标志的Activity时,任务共用性affinities不会影响将会运行该新活动的Task:它总是运行在启动它的Task里。但是,如果使用了NEW_TASK标志,那么共用性(affinity)将被用来判断是否已经存在一个有相同共用性(affinity)的Task。如果是这样,这项Task将被切换到前面而新的Activity会启动于这个Task的顶层。

 

这种特性在您必须使用NEW_TASK标志的情况下最有用,尤其是从状态栏通知或桌面快捷方式启动活动时。结果是,当用户用这种方式启动您的应用程序时,它的当前Task将被切换到前台,而且想要查看的Activity被放在最上面。

 

你可以在程序清单(Manifest)文件的应用程序application标签中为.apk包中所有的活动分配你自己的任务共用性Affinites,或者在活动标记中为各个活动进行分配。

一些说明其如何使用的例子如下:

  • 如果您的.apk包含多个用户可以启动的高层应用程序,那么您可能需要对用户看到的每个Activity(活动)指定不同的affinities。一个不错的命名惯例是以附加一个以冒号分隔的字符串来扩展您的.apk包名。例如,“ com.android.contacts ”.apk可以有affinities:“com.android.contacts:Dialer”和“ com.android.contacts:ContactsList”。
  • 如果您正在替换一个通知,快捷方式,或其他可以从外部发起的应用程序的“内部”活动,你可能需要明确设定您替代活动的taskAffinity和您准备替代的应用程序一样。例如,如果您想替换contacts详细信息视图(用户可以创建并调用快捷方式),你得把taskAffinity设置成“com.android.contacts”。

 

在前面的文章“Android四种Activity的加载模式”我们提到:Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的<activity>元素的特性值交互控制。

 

跟 Task 有关的 manifest文件中Activity的特性值介绍

android:allowTaskReparenting
    用来标记Activity能否从启动的Task移动到有着affinity的Task(当这个Task进入到前台时)

   “true”,表示能移动,“false”,表示它必须呆在启动时呆在的那个Task里。

    如果这个特性没有被设定,设定到<application>元素上的allowTaskReparenting特性的值会应用到Activity上。默认值为“false”。

    一般来说,当Activity启动后,它就与启动它的Task关联,并且在那里耗尽它的整个生命周期。当当前的Task不再显示时,你可以使用这个特性来强制Activity移动到有着affinity的Task中。典型用法是:把一个应用程序的Activity移到另一个应用程序的主Task中。
    例如,如果 email中包含一个web页的链接,点击它就会启动一个Activity来显示这个页面。这个Activity是由Browser应用程序定义的,但是,现在它作为email Task的一部分。如果它重新宿主到Browser Task里,当Browser下一次进入到前台时,它就能被看见,并且,当email Task再次进入前台时,就看不到它了。

    Actvity的affinity是由taskAffinity特性定义的。Task的affinity是通过读取根Activity的affinity决定。因此,根Activity总是位于相同affinity的Task里。由于启动模式为“singleTask”和“singleInstance”的Activity只能位于Task的底部,因此,重新宿主只能限于“standard”和“singleTop”模式。

android:alwaysRetainTaskState
    用来标记Activity所在的Task的状态是否总是由系统来保持。

    “true”,表示总是;“false”,表示在某种情形下允许系统恢复Task到它的初始化状态。默认值是“false”。

    这个特性只针对Task的根Activity有意义;对其它Activity来说,忽略之。
    一般来说,特定的情形如当用户从主画面重新选择这个Task时,系统会对这个Task进行清理(从stack中删除位于根Activity之上的所有Activivity)。典型的情况,当用户有一段时间没有访问这个Task时也会这么做,例如30分钟。
    然而,当这个特性设为“true”时,用户总是能回到这个Task的最新状态,无论他们是如何启动的。这非常有用,例如,像Browser应用程序,这里有很多的状态(例如多个打开的Tab),用户不想丢失这些状态。

android:clearTaskOnLaunch
    用来标记是否从Task中清除所有的Activity,除了根Activity外(每当从主画面重新启动时)

   “true”,表示总是清除至它的根Activity,“false”表示不。默认值是“false”。

    这个特性只对启动一个新的Task的Activity(根Activity)有意义;对Task中其它的Activity忽略。
    当这个值为“true”,每次用户重新启动这个Task时,都会进入到它的根Activity中,不管这个Task最后在做些什么,也不管用户是使用BACK还是HOME离开的。当这个值为“false”时,可能会在一些情形下(参考alwaysRetainTaskState特性)清除Task的Activity,但不总是。
    假设,某人从主画面启动了Activity P,并从那里迁移至Activity Q。接下来用户按下HOME,然后返回Activity P。一般,用户可能见到的是Activity Q,因为它是P的Task中最后工作的内容。然而,如果P设定这个特性为“true”,当用户按下HOME并使这个Task再次进入前台时,其上的所有的Activity(在这里是Q)都将被清除。因此,当返回到这个Task时,用户只能看到P。
    如果这个特性和allowTaskReparenting都设定为“true”,那些能重新宿主的Activity会移动到共享affinity的Task中;剩下的Activity都将被抛弃,如上所述。

android:finishOnTaskLaunch
    用来标记当用户再次启动它的Task(在主画面选择这个Task)时已经存在的Activity实例是否要关闭(结束)

   “true”,表示应该关闭,“false”表示不关闭。默认值是“false”。
    如果这个特性和allowTaskReparenting都设定为“true”,这个特性胜出。Activity的affinity忽略。这个Activity不会重新宿主,但是会销毁。

android:launchMode
    用于指示Activity如何启动。这里有四种模式,与Intent对象中的Activity Flags(FLAG_ACTIVITY_*变量)共同作用,来决定Activity如何启动来处理Intent。它们是:

    "standard"
    "singleTop"
    "singleTask"
    "singleInstance"

    默认模式是“standard”。
    前面文章:“Android四种Activity的加载模式”已经详细描述,这里就不做描述了.

android:noHistory
    用于标记当用户从Activity上离开并且它在屏幕上不再可见时Activity是否从Activity stack中清除并结束(调用finish()方法)——“true”,表示它应该关闭,“false”,表示不需要。默认值是“false”。
    “true”值意味着Activity不会留下历史痕迹。因为它不会在Activity stack的Task中保留,因此,用户不能返回它。

    比如启用界面的就可以借用这个。

android:taskAffinity
   这就是本文所描述的任务共用性。

   Activity为Task拥有的一个affinity。拥有相同的affinity的Activity理论上属于相同的Task(在用户的角度是相同的“应用程序”)。Task的affinity是由它的根Activity决定的。
   affinity决定两件事情——Activity重新宿主的Task(参考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK标志启动的Activity宿主的Task。
    默认情况,一个应用程序中的所有Activity都拥有相同的affinity。捏可以设定这个特性来重组它们,甚至可以把不同应用程序中定义的Activity放置到相同的Task中。为了明确Activity不宿主特定的Task,设定该特性为空的字符串。
    如果这个特性没有设置,Activity将从应用程序的设定那里继承下来(参考<application>元素的taskAffinity特性)。应用程序默认的affinity的名字是<manifest>元素中设定的package名。

 

跟 Task 有关的 Intent对象中设置的Flag

FLAG_ACTIVITY_BROUGHT_TO_FRONT
    这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定。

FLAG_ACTIVITY_CLEAR_TOP
    如果设置,并且这个Activity已经在当前的Task中运行,因此,不再是重新启动一个这个Activity的实例,而是在这个Activity上方的所有Activity都将关闭,然后这个Intent会作为一个新的Intent投递到老的Activity(现在位于顶端)中。
    例如,假设一个Task中包含这些Activity:A,B,C,D。如果D调用了startActivity(),并且包含一个指向Activity B的Intent,那么,C和D都将结束,然后B接收到这个Intent,因此,目前stack的状况是:A,B。
    上例中正在运行的Activity B既可以在onNewIntent()中接收到这个新的Intent,也可以把自己关闭然后重新启动来接收这个Intent。如果它的启动模式声明为“multiple”(默认值),并且你没有在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,那么它将关闭然后重新创建;对于其它的启动模式,或者在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,都将把这个Intent投递到当前这个实例的onNewIntent()中。
    这个启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合起来使用:用于启动一个Task中的根Activity,它会把那个Task中任何运行的实例带入前台,然后清除它直到根Activity。这非常有用,例如,当从Notification Manager处启动一个Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
    如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。
    这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    如果设置,新的Activity不会在最近启动的Activity的列表中保存。

FLAG_ACTIVITY_FORWARD_RESULT
    如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
    这个标志一般不由应用程序代码设置,如果这个Activity是从历史记录里启动的(常按HOME键),那么,系统会帮你设定。

FLAG_ACTIVITY_MULTIPLE_TASK
    不要使用这个标志,除非你自己实现了应用程序启动器。与FLAG_ACTIVITY_NEW_TASK结合起来使用,可以禁用把已存的Task送入前台的行为。当设置时,新的Task总是会启动来处理Intent,而不管这是是否已经有一个Task可以处理相同的事情。
    由于默认的系统不包含图形Task管理功能,因此,你不应该使用这个标志,除非你提供给用户一种方式可以返回到已经启动的Task。
    如果FLAG_ACTIVITY_NEW_TASK标志没有设置,这个标志被忽略。

FLAG_ACTIVITY_NEW_TASK
    如果设置,这个Activity会成为历史stack中一个新Task的开始。一个Task(从启动它的Activity到下一个Task中的Activity)定义了用户可以迁移的Activity原子组。Task可以移动到前台和后台;在某个特定Task中的所有Activity总是保持相同的次序。
    这个标志一般用于呈现“启动”类型的行为:它们提供用户一系列可以单独完成的事情,与启动它们的Activity完全无关。
    使用这个标志,如果正在启动的Activity的Task已经在运行的话,那么,新的Activity将不会启动;代替的,当前Task会简单的移入前台。参考FLAG_ACTIVITY_MULTIPLE_TASK标志,可以禁用这一行为。
    这个标志不能用于调用方对已经启动的Activity请求结果。

FLAG_ACTIVITY_NO_ANIMATION
    如果在Intent中设置,并传递给Context.startActivity()的话,这个标志将阻止系统进入下一个Activity时应用Acitivity迁移动画。这并不意味着动画将永不运行——如果另一个Activity在启动显示之前,没有指定这个标志,那么,动画将被应用。这个标志可以很好的用于执行一连串的操作,而动画被看作是更高一级的事件的驱动。

FLAG_ACTIVITY_NO_HISTORY
    如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。

FLAG_ACTIVITY_NO_USER_ACTION
    如果设置,作为新启动的Activity进入前台时,这个标志将在Activity暂停之前阻止从最前方的Activity回调的onUserLeaveHint()。
    典型的,一个Activity可以依赖这个回调指明显式的用户动作引起的Activity移出后台。这个回调在Activity的生命周期中标记一个合适的点,并关闭一些Notification。
    如果一个Activity通过非用户驱动的事件,如来电或闹钟,启动的,这个标志也应该传递给Context.startActivity,保证暂停的Activity不认为用户已经知晓其Notification。

FLAG_ACTIVITY_PREVIOUS_IS_TOP
    If set and this intent is being used to launch a new activity from an existing one, the current activity will not be counted as the top activity for deciding whether the new intent should be delivered to the top instead of starting a new one. The previous activity will be used as the top, with the assumption being that the current activity will finish itself immediately.

FLAG_ACTIVITY_REORDER_TO_FRONT
    如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。
    例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被忽略。

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

If set, and this activity is either being started in a new task or bringing to the top an existing task, then it will be launched as the front door of the task. This will result in the application of any affinities needed to have that task in the proper state (either moving activities to or from it), or simply resetting that task to its initial state if needed.

FLAG_ACTIVITY_SINGLE_TOP
    如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的。

 

参考资料:

Android应用程序模型:应用程序,任务,进程和线程
http://blog.csdn.net/iefreer/archive/2009/08/18/4460196.aspx

Task和Activity相关
http://chengbs.iteye.com/blog/798810

Android四种Activity的加载模式

建议首先阅读下面两篇文章,这样才可以更好的理解Activity的加载模式:

Android的进程,线程模型
http://www.cnblogs.com/ghj1976/archive/2011/04/28/2031586.html 其中对“Android的单线程模型”的描述,明白Activity的一些注意事项。

Android Application Task Activities的关系
http://www.cnblogs.com/ghj1976/archive/2011/04/29/2032412.html  尤其要明白 Task 是啥。

 

一个Activty的生命周期

Activty的生命周期的也就是它所在进程的生命周期。

image 

每一个活动( Activity )都处于某一个状态,对于开发者来说,是无法控制其应用程序处于某一个状态的,这些均由系统来完成。
但是当一个活动的状态发生改变的时候,开发者可以通过调用 onXX() 的方法获取到相关的通知信息。

 

在实现 Activity 类的时候,通过覆盖( override )这些方法即可在你需要处理的时候来调用。

  • onCreate :当活动第一次启动的时候,触发该方法,可以在此时完成活动的初始化工作。
    onCreate 方法有一个参数,该参数可以为空( null ),也可以是之前调用 onSaveInstanceState ()方法保存的状态信息。
  • onStart :该方法的触发表示所属活动将被展现给用户。
  • onResume :当一个活动和用户发生交互的时候,触发该方法。
  • onPause :当一个正在前台运行的活动因为其他的活动需要前台运行而转入后台运行的时候,触发该方法。这时候需要将活动的状态持久化,比如正在编辑的数据库记录等。
  • onStop :当一个活动不再需要展示给用户的时候,触发该方法。如果内存紧张,系统会直接结束这个活动,而不会触发 onStop 方法。 所以保存状态信息是应该在onPause时做,而不是onStop时做。活动如果没有在前台运行,都将被停止或者Linux管理进程为了给新的活动预留足够的存储空间而随时结束这些活动。因此对于开发者来说,在设计应用程序的时候,必须时刻牢记这一原则。在一些情况下,onPause方法或许是活动触发的最后的方法,因此开发者需要在这个时候保存需要保存的信息。
  • onRestart :当处于停止状态的活动需要再次展现给用户的时候,触发该方法。
  • onDestroy :当活动销毁的时候,触发该方法。和 onStop 方法一样,如果内存紧张,系统会直接结束这个活动而不会触发该方法。
  • onSaveInstanceState :系统调用该方法,允许活动保存之前的状态,比如说在一串字符串中的光标所处的位置等。
    通常情况下,开发者不需要重写覆盖该方法,在默认的实现中,已经提供了自动保存活动所涉及到的用户界面组件的所有状态信息。 

Activity栈

上面提到开发者是无法控制Activity的状态的,那Activity的状态又是按照何种逻辑来运作的呢?这就要知道 Activity 栈。

 

每个Activity的状态是由它在Activity栈(是一个后进先出LIFO,包含所有正在运行Activity的队列)中的位置决定的。

当一个新的Activity启动时,当前的活动的Activity将会移到Activity栈的顶部。

如果用户使用后退按钮返回的话,或者前台的Activity结束,在栈上的Activity将会移上来并变为活动状态。如下图所示:

image

一个应用程序的优先级是受最高优先级的Activity影响的。当决定某个应用程序是否要终结去释放资源,Android内存管理使用栈来决定基于Activity的应用程序的优先级。

Activity状态
一般认为Activity有以下四种状态:

活动的:当一个Activity在栈顶,它是可视的、有焦点、可接受用户输入的。Android试图尽最大可能保持它活动状态,杀死其它Activity来确保当前活动Activity有足够的资源可使用。当另外一个Activity被激活,这个将会被暂停。
暂停:在很多情况下,你的Activity可视但是它没有焦点,换句话说它被暂停了。有可能原因是一个透明或者非全屏的Activity被激活。
当被暂停,一个Activity仍会当成活动状态,只不过是不可以接受用户输入。在极特殊的情况下,Android将会杀死一个暂停的Activity来为活动的Activity提供充足的资源。当一个Activity变为完全隐藏,它将会变成停止。
停止:当一个Activity不是可视的,它“停止”了。这个Activity将仍然在内存中保存它所有的状态和会员信息。尽管如此,当其它地方需要内存时,它将是最有可能被释放资源的。当一个Activity停止后,一个很重要的步骤是要保存数据和当前UI状态。一旦一个Activity退出或关闭了,它将变为待用状态。
待用: 在一个Activity被杀死后和被装在前,它是待用状态的。待用Acitivity被移除Activity栈,并且需要在显示和可用之前重新启动它。

 

activity的四种加载模式

在android的多activity开发中,activity之间的跳转可能需要有多种方式,有时是普通的生成一个新实例,有时希望跳转到原来某个activity实例,而不是生成大量的重复的activity。加载模式便是决定以哪种方式启动一个跳转到原来某个Activity实例。

在android里,有4种activity的启动模式,分别为:

  • standard: 标准模式,一调用startActivity()方法就会产生一个新的实例。
  • singleTop: 如果已经有一个实例位于Activity栈的顶部时,就不产生新的实例,而只是调用Activity中的newInstance()方法。如果不位于栈顶,会产生一个新的实例。
  • singleTask: 会在一个新的task中产生这个实例,以后每次调用都会使用这个,不会去产生新的实例了。
  • singleInstance: 这个跟singleTask基本上是一样,只有一个区别:在这个模式下的Activity实例所处的task中,只能有这个activity实例,不能有其他的实例。

这些启动模式可以在功能清单文件AndroidManifest.xml中进行设置,<activity>中的launchMode属性。

相关的代码中也有一些标志可以使用,比如我们想只启用一个实例,则可以使用 Intent.FLAG_ACTIVITY_REORDER_TO_FRONT 标志,这个标志表示:如果这个activity已经启动了,就不产生新的activity,而只是把这个activity实例加到栈顶来就可以了。

Intent intent = new Intent(ReorderFour.this, ReorderTwo.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(intent);  

Activity的加载模式受启动Activity的Intent对象中设置的Flag和manifest文件中Activity的<activity>元素的特性值交互控制。

下面是影响加载模式的一些特性

核心的Intent Flag有:

FLAG_ACTIVITY_NEW_TASK

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_RESET_TASK_IF_NEEDED

FLAG_ACTIVITY_SINGLE_TOP

核心的<activity>特性有:

taskAffinity

launchMode

allowTaskReparenting

clearTaskOnLaunch

alwaysRetainTaskState

finishOnTaskLaunch

四种加载模式的区别

所属task的区别

一般情况下,“standard”和”singleTop”的activity的目标task,和收到的Intent的发送者在同一个task内,就相当于谁调用它,它就跟谁在同一个Task中。

除非Intent包括参数FLAG_ACTIVITY_NEW_TASK。如果提供了FLAG_ACTIVITY_NEW_TASK参数,会启动到别的task里。

“singleTask”和”singleInstance” 总是把要启动的activity作为一个task的根元素,他们不会被启动到一个其他task里。

 

是否允许多个实例

“standard”和”singleTop”可以被实例化多次,并且是可以存在于不同的task中;这种实例化时一个task可以包括一个activity的多个实例;

“singleTask”和”singleInstance”则限制只生成一个实例,并且是task的根元素。

singleTop 要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不创建新的实例。

 

是否允许其它activity存在于本task内

“singleInstance”独占一个task,其它activity不能存在那个task里;

如果它启动了一个新的activity,不管新的activity的launch mode 如何,新的activity都将会到别的task里运行(如同加了FLAG_ACTIVITY_NEW_TASK参数)。

而另外三种模式,则可以和其它activity共存。

 

是否每次都生成新实例

“standard”对于每一个启动Intent都会生成一个activity的新实例;

“singleTop”的activity如果在task的栈顶的话,则不生成新的该activity的实例,直接使用栈顶的实例,否则,生成该activity的实例。

比如:

现在task栈元素为A-B-C-D(D在栈顶),这时候给D发一个启动intent,如果D是 “standard”的,则生成D的一个新实例,栈变为A-B-C-D-D。

如果D是singleTop的话,则不会生产D的新实例,栈状态仍为A-B-C-D

如果这时候给B发Intent的话,不管B的launchmode是”standard” 还是 “singleTop” ,都会生成B的新实例,栈状态变为A-B-C-D-B。

“singleInstance”是其所在栈的唯一activity,它会每次都被重用。

“singleTask”  如果在栈顶,则接受intent,否则,该intent会被丢弃,但是该task仍会回到前台。 当已经存在的activity实例处理新的intent时候,会调用onNewIntent()方法,如果收到intent生成一个activity实例,那么用户可以通过back键回到上一个状态;如果是已经存在的一个activity来处理这个intent的话,用户不能通过按back键返回到这之前的状态。

 

参考资料

Android的七巧板Activity之一 Activity的生命周期

http://winuxxan.blog.51cto.com/2779763/502523

Android的七巧板Activity之二 Activity的加载模式

http://winuxxan.blog.51cto.com/2779763/504047

Android Activity生命周期

http://www.haoni.org/2011/02/25/androidactivityshengmingzhouqi/

android中activity的四种加载模式

http://blog.csdn.net/pcwings/archive/2010/09/19/5895197.aspx

区分Activity的四种加载模式

http://marshal.easymorse.com/archives/2950

Hello Android 第三版 (二)

http://blog.csdn.net/cqwty/archive/2010/09/08/5870219.aspx

只生成一个Activity的实例

http://www.eoeandroid.com/home-space-uid-43043-do-blog-id-6.html

activity的启动方式(launch mode)

http://liubin.nanshapo.com/2010/12/23/activity-launch-mode/

Android应用程序模型:应用程序,任务,进程和线程

http://blog.csdn.net/iefreer/archive/2009/08/18/4460196.aspx

Android Application Task Activities的关系

什么是Android  Application?

简单来说,一个apk文件就是一个Application。

任何一个Android Application基本上是由一些Activities组成,当用户与应用程序交互时其所包含的部分Activities具有紧密的逻辑关系,或者各自独立处理不同的响应。

这些Activities捆绑在一起成为了一个处理特定需求的Application, 并且以“.apk”作为后缀名存在于文件系统中。

Android平台默认下的应用程序 例如:Email、Calendar、Browser、Maps、Text Message、Contacts、Camera和Dialer等都是一个个独立的Apps。

 

安装 Application的过程也可以简单理解为将其所包裹的Activities导入到当前的系统中,如果系统中已经存在了相同的Activities, 那么将会自动将其关联,而不会重复安装相同的Activities,避免资源的浪费。

Application卸载的过程也会检查当前所关联的 Activities是否有被其它Application标签所关联,如果仅仅是提供当前的Application使用,那么将会彻底被移除,相反则不做 任何操作。

 

就像我们已经知道的,Application基本上是由四个模块组成:Activity、Service、Content Provider 和 Broadcast Receiver,其中Activity是实现应用的主体。

 

什么是 Activity Stack?

操作应用程序时,有时需要调用多个Activities来完成需求,例如:发送邮件程序,首先是进入邮件主界面,然后启动一个新的Activity用于填写新邮件内容,同时可以调出联系人列表用于插入收件人信息等等。在这个操作过程中 Android平台有一个专门用于管理Activities堆栈的机制,其可以方便的线性记录Activities实例,当完成某个操作时,可以通过导航功能返回之前的Activity(通过按操作台的“Back”按钮)。

每次启动新的Activity都将被添加到Activity Stack。用户可以方便的返回上一个Activity直到Home Screen,到达Home Screen后,将无法再继续查看堆栈记录(俗话说:到头了)。如果当前Task被中止(Interrupting the task),返回到系统主界面后启动了其它操作,当希望返回到前一个Task继续执行时,只需要再次通过主界面的Application launcher或者快捷方式启动这个Task的Root Activity便可返回其中止时的状态继续执行。

相对于Views、Windows、Menus和Dialogs而言,Activity是唯一可被记录在History stack中的数据,所以当你所设计的应用程序需要用户由A界面进入到次一级界面B,当完成操作后需要再次返回A,那么必须考虑将A看作为 Activity,否则将无法从历史堆栈中返回。

 

什么是Task

当我们需要一个Activity可以启动另一个Activity,可能另外一个Activity是定义在不同应用程序中的Activity。

例如,假设你想在你的应用中让用户显示一些地方的街景。而这里已经有一个Activity可以做到这一点,因此,你的Activity所需要做的只是在Intent对象中添加必要的信息,并传递给startActivity()。地图浏览将会显示你的地图。当用户按下BACK键,你的Activity会再次出现在屏幕上。

对于用户来说,看起来好像是地图浏览与你的Activity一样,属于相同的应用程序,即便是它定义在其它的应用程序里,并运行在那个应用程序的进程里。

Android通过将这两个Activity保存在同一个Task里来体现这一用户体验。简单来说,一个Task就是用户体验上的一个“应用”。
它将相关的Activity组合在一起,以stack的方式管理(就是前面提到的Activity Stack),这就是Task。

 

在Android平台上可以将task简单的理解为幽多个Activity共同协作完成某项应用,而不管Activity具体属于哪个Application,

通过下图可以更清晰的理解Application、task、Activity三者之间的关系:

image

 

Task 有啥用?

我们用过Android的手机就会知道有下面的场景:

假设我们首先在用IReader在看书,从选书到具体书的阅读界面,这是有好几个Activity。我们每一个点击的Activity都被放在阅读这个Task对应的Activity Stack中了,这可以放我们通过回退键返回每一个前面的Activity。

我们在阅读到一半时,想看看Sina微博,按Home键离开了IReader。

在Sina微博界面也是有多个Activity,我们一步到阅读界面。这时候我们每一个点击的Activity都被放在Sina微博这个Task对应的Activity Stack中了,这可以放我们通过回退键返回每一个前面的Activity。

我们这时候再回到IReader读书界面,原先的状态还是保留的。

显然每一个Task有自己的 Activity Stack。

Task就是这样为了方便人们使用手机而设置的,就像前面提到的场景Task可以跨Application。

 

下面这个图从另外一个角度描述了Application Task Activities的关系

image

 

Task通过Application launcher、Home screen的快捷方式或者 由 “Recent Tasks”(长时间按住Home键)最近使用过的Task记录中启动。

当从一个Activity中启动另外一个Activity时,Back键将作用于返回前一个Activity,与此同时 新开启的Activity将被添加到Activity Stack中。

有关更详细的可以参看这篇文章:

[译]关于Activity和Task的设计思路和方法
http://blogold.chinaunix.net/u2/85193/showart_1966109.html

 

参考资料:

http://skyswim42.egloos.com/3127700

关于Activity和Task的设计思路和方法
http://blogold.chinaunix.net/u2/85193/showart_1966109.html

Android Task
http://www.apkbus.com/forum.php?mod=viewthread&tid=146

Android的进程,线程模型

Android 包括一个应用程序框架、几个应用程序库和一个基于 Dalvik 虚拟机的运行时,所有这些都运行在 Linux 内核之上。

通过利用 Linux 内核的优势,Android 得到了大量操作系统服务,包括进程和内存管理、网络堆栈、驱动程序、硬件抽象层、安全性等相关的服务。

 

有关Java虚拟机跟进程,线程的关系请参看下面这篇文章:

进程、线程与JVM、CLR
http://blog.csdn.net/ghj1976/archive/2010/04/13/5481038.aspx

 

下面这篇文章对Android的进程和线程描述的很好,我在这篇文章基础补充了一些图片和信息。

http://blog.csdn.net/L_serein/archive/2011/03/22/6269270.aspx 

android进程模型:

在安装Android应用程序的时候,Android会为每个程序分配一个Linux用户ID,并设置相应的权限,这样其它应用程序就不能访问此应用程序所拥有的数据和资源了。

在 Linux 中,一个用户ID 识别一个给定用户;在 Android 上,一个用户ID 识别一个应用程序。

应用程序在安装时被分配用户 ID,应用程序在设备上的存续期间内,用户ID 保持不变。

 

默认情况下,每个apk运行在它自己的Linux进程中。当需要执行应用程序中的代码时,Android会启动一个jvm,即一个新的进程来执行,因此不同的apk运行在相互隔离的环境中。

下图显示了:两个 Android 应用程序,各自在其自己的基本沙箱或进程上。他们是不同的Linux user ID。

image

开发者也可以给两个应用程序分配相同的linux用户id,这样他们就能访问对方所拥有的资源。

为了保留系统资源,拥有相同用户id的应用程序可以运行在同一个进程中,共享同一个jvm。

如下图,显示了两个 Android 应用程序,运行在同一进程上。

不同的应用程序可以运行在相同的进程中。要实现这个功能,首先必须使用相同的私钥签署这些应用程序,然后必须使用 manifest 文件给它们分配相同的 Linux 用户 ID,这通过用相同的值/名定义 manifest 属性 android:sharedUserId 来做到。

image

Android进程知识的补充:

下图是标准的Android 架构图,

其中我们可以看到在“Android本地库 & Java运行环境层”中,Android 运行时中,

Dalvik是Android中的java虚拟机,可支持同时运行多个虚拟机实例;每个Android应用程序都在自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例;
所有java类经过java编译器编译,然后通过SDK中的dx工具转成.dex格式交由虚拟机执行。

image

Android系统进程

init进程(1号进程),父进程为0号进程,执行根目录底下的init可执行程序,是用户空间进程
——-> /system/bin/sh
——-> /system/bin/mediaserver
——-> zygote
—————–> system_server
—————–>com.android.phone
—————–>android.process.acore(Home)
… …

kthreadd进程(2号进程),父进程为0号进程,是内核进程,其他内核进程都是直接或者间接以它为父进程

  

Android的单线程模型

当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。

在开发Android 应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

如果在非UI线程中直接操作UI线程,会抛出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,这与普通的java程序不同。

由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,UI线程里的操作应该向中断事件那样短小,费时的操作(如网络连接)需要另开线程,否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒用户终止应用程序。

如果在新开的线程中需要对UI进行设定,就可能违反单线程模型,因此android采用一种复杂的Message Queue机制保证线程间通信。

 

Message Queue:

Message Queue是一个消息队列,用来存放通过Handler发布的消息。Android在第一次启动程序时会默认会为UI thread创建一个关联的消息队列,可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers 等等。你可以在自己的子线程中创建Handler与UI thread通讯。 

通过Handler你可以发布或者处理一个消息或者是一个Runnable的实例。每个Handler都会与唯一的一个线程以及该线程的消息队列管理。

Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。

实例如下:

public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.main);
   editText = (EditText) findViewById(R.id.weather_city_edit);
   Button button = (Button) findViewById(R.id.goQuery);
   button.setOnClickListener(this);  

   Looper looper = Looper.myLooper();  //得到当前线程的Looper实例,由于当前线程是UI线程也可以通过Looper.getMainLooper()得到  
    messageHandler = new MessageHandler(looper);  //此处甚至可以不需要设置Looper,因为 Handler默认就使用当前线程的Looper  
} 

public void onClick(View v) {
   new Thread() {
      public void run() {
          Message message = Message.obtain();
          message.obj = "abc";
          messageHandler.sendMessage(message);  //发送消息 
       }
   }.start();
} 

Handler messageHandler = new Handler {
   public MessageHandler(Looper looper) {
      super(looper);
  }
   public void handleMessage(Message msg) {
      setTitle((String) msg.obj);
   }
}

对于这个实例,当这个activity执行玩oncreate,onstart,onresume后,就监听UI的各种事件和消息。

当我们点击一个按钮后,启动一个线程,线程执行结束后,通过handler发送一个消息,由于这个handler属于UI线程,因此这个消息也发送给UI线程,然后UI线程又把这个消息给handler处理,而这个handler是UI线程创造的,他可以访问UI组件,因此,就更新了页面。

由于通过handler需要自己管理线程类,如果业务稍微复杂,代码看起来就比较混乱,因此android提供了AsyncTask类来解决此问题。

 

AsyncTask:

首先继承一下此类,实现以下若干方法,

onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。 

doInBackground(Params…), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。

可以调用publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。 

onProgressUpdate(Progress…),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。 

onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

使用时需要遵循以下规则:

1)Task的实例必须在UI thread中创建 

2)execute方法必须在UI thread中调用 

3)不要手动的调用这些方法,只调用execute即可

4)该task只能被执行一次,否则多次调用时将会出现异常

示例如下:

public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
       editText = (EditText) findViewById(R.id.weather_city_edit);
       Button button = (Button) findViewById(R.id.goQuery);
       button.setOnClickListener(this);
}  

public void onClick(View v) {
       new GetWeatherTask().execute(“aaa”);
} 

class GetWeatherTask extends AsyncTask<String, Integer, String> {
    protected String doInBackground(String... params) {
         return getWetherByCity(params[0]);
    }
    protected void onPostExecute(String result) {
         setTitle(result);
    }
}

 

参考资料:

Android进程和线程模型

http://blog.csdn.net/L_serein/archive/2011/03/22/6269270.aspx

Hello Android 第三版 (二)

http://blog.csdn.net/cqwty/archive/2010/09/08/5870219.aspx

理解 Android 上的安全性

http://www.ibm.com/developerworks/cn/xml/x-androidsecurity/

Android 的Margin和Padding属性以及支持的长度单位

Android的Margin和Padding跟Html的是一样的。如下图所示:黄色部分为Padding,灰色部分为Margin。

image

通俗的理解 Padding 为内边框,Margin 为外边框

对应的属性为

android:layout_marginBottom="25dip"
android:layout_marginLeft="10dip"
android:layout_marginTop="10dip"
android:layout_marginRight="10dip"
android:paddingLeft="1dip"
android:paddingTop="1dip"
android:paddingRight="1dip"
android:paddingBottom="1dip"

如果左右上下都是相同的设置则可以直接设置

android:layout_margin="10dip"
android:padding="5dip"

 

Android支持的长度单位。

  • px(像素):屏幕上的点。
    pixels(像素). 不同设备显示效果相同,一般我们HVGA代表320×480像素,这个用的比较多。
  • in(英寸):长度单位。
  • mm(毫米):长度单位。
  • pt(磅):1/72英寸。
    point,是一个标准的长度单位,1pt=1/72英寸,用于印刷业,非常简单易用;
  • dp(与密度无关的像素):一种基于屏幕密度的抽象单位。在每英寸160点的显示器上,1dp = 1px。
  • dip:与dp相同,多用于android/ophone示例中。
    device independent pixels(设备独立像素). 不同设备有不同的显示效果,这个和设备硬件有关,一般我们为了支持WVGA、HVGA和QVGA 推荐使用这个,不依赖像素。
  • sp(与刻度无关的像素):与dp类似,但是可以根据用户的字体大小首选项进行缩放。
    scaled pixels(放大像素). 主要用于字体显示best for textsize。

为了使用户界面能够在现在和将来的显示器类型上正常显示,建议大家始终使用sp作为文字大小的单位,Android默认的字号也是用的sp。

将dip作为其他元素的单位,比如长度、高度。当然,也可以考虑使用矢量图形,而不是用位图。

 

dp是与密度无关,sp除了与密度无关外,还与scale无关。

如果屏幕密度为160,这时dp和sp和px是一样的。1dp=1sp=1px,但如果使用px作单位,如果屏幕大小不变(假设还是3.2寸),而屏幕密度变成了320。

那么原来TextView的宽度设成160px,在密度为320的3.2寸屏幕里看要比在密度为160的3.2寸屏幕上看短了一半。

但如果设置成160dp或160sp的话。系统会自动将width属性值设置成320px的。

也就是160 * 320 / 160。其中320 / 160可称为密度比例因子。也就是说,如果使用dp和sp,系统会根据屏幕密度的变化自动进行转换.

 

参考资料:

dip, dp, px, sp区别
http://sifutian.iteye.com/blog/680935

android:layout_gravity 和 android:gravity 的区别

gravity 这个英文单词是重心的意思,在这里就表示停靠位置的意思。

android:layout_gravity 和 android:gravity 的区别

从名字上可以看到,android:gravity是对元素本身说的,元素本身的文本显示在什么地方靠着换个属性设置,不过不设置默认是在左侧的。

android:layout_gravity是相对与它的父元素说的,说明元素显示在父元素的什么位置。

比如说button: android:layout_gravity 表示按钮在界面上的位置。 android:gravity表示button上的字在button上的位置。

 

可选值

这两个属性可选的值有:top、bottom、left、right、center_vertical、fill_vertical、center_horizontal、fill_horizontal、center、fill、clip_vertical。

而且这些属性是可以多选的,用“|”分开。

默认这个的值是:Gravity.LEFT

对这些属性的描述:

出自:

http://androidmirror.com/guide/topics/resources/drawable-resource.html

http://android.toolib.net/reference/android/graphics/drawable/ClipDrawable.html

Value Description
top Put the object at the top of its container, not changing its size.
将对象放在其容器的顶部,不改变其大小.
bottom Put the object at the bottom of its container, not changing its size.
将对象放在其容器的底部,不改变其大小.
left Put the object at the left edge of its container, not changing its size.
将对象放在其容器的左侧,不改变其大小.
right Put the object at the right edge of its container, not changing its size.
将对象放在其容器的右侧,不改变其大小.
center_vertical Place object in the vertical center of its container, not changing its size.
将对象纵向居中,不改变其大小.
垂直对齐方式:垂直方向上居中对齐。
fill_vertical Grow the vertical size of the object if needed so it completely fills its container.
必要的时候增加对象的纵向大小,以完全充满其容器.
垂直方向填充
center_horizontal Place object in the horizontal center of its container, not changing its size.
将对象横向居中,不改变其大小.
水平对齐方式:水平方向上居中对齐
fill_horizontal Grow the horizontal size of the object if needed so it completely fills its container.
必要的时候增加对象的横向大小,以完全充满其容器.
水平方向填充
center Place the object in the center of its container in both the vertical and horizontal axis, not changing its size.
将对象横纵居中,不改变其大小.
fill Grow the horizontal and vertical size of the object if needed so it completely fills its container. This is the default.
必要的时候增加对象的横纵向大小,以完全充满其容器.
clip_vertical Additional option that can be set to have the top and/or bottom edges of the child clipped to its container’s bounds. The clip is based on the vertical gravity: a top gravity clips the bottom edge, a bottom gravity clips the top edge, and neither clips both edges.

附加选项,用于按照容器的边来剪切对象的顶部和/或底部的内容. 剪切基于其纵向对齐设置:顶部对齐时,剪切底部;底部对齐时剪切顶部;除此之外剪切顶部和底部.

垂直方向裁剪

clip_horizontal Additional option that can be set to have the left and/or right edges of the child clipped to its container’s bounds. The clip is based on the horizontal gravity: a left gravity clips the right edge, a right gravity clips the left edge, and neither clips both edges.

附加选项,用于按照容器的边来剪切对象的左侧和/或右侧的内容. 剪切基于其横向对齐设置:左侧对齐时,剪切右侧;右侧对齐时剪切左侧;除此之外剪切左侧和右侧.

水平方向裁剪

简单记忆 : horizontal 都是操作的水平方向,即横向, vertical 都是炒作的垂直方向,即纵向。

对于LinearLayout何时生效的问题

参看:也谈layout_gravity和gravity
http://www.lephone.net/viewthread.php?tid=325

对于 LinearLayout

当 android:orientation="vertical"  时, 只有水平方向的设置才起作用,垂直方向的设置不起作用。即:left,right,center_horizontal 是生效的。

当 android:orientation="horizontal" 时, 只有垂直方向的设置才起作用,水平方向的设置不起作用。即:top,bottom,center_vertical 是生效的。

Android设置窗口的背景图

drawable- hdpi、drawable- mdpi、drawable-ldpi的区别:

Android2.1(含)以后的版本中有drawable-mdpi、drawable-ldpi、drawable-hdpi三个目录,这三个目录主要是为了支持多分辨率。

dpi是“dot per inch”的缩写,每英寸像素数。

四种密度分类: ldpi (low), mdpi (medium), hdpi (high), and xhdpi (extra high)
一般情况下的普通屏幕:ldpi是120,mdpi是160,hdpi是240,xhdpi是320。

WVGA,HVGA,QVGA的区别
VGA是”Video Graphics Array”,显示标准为 640*480。
WVGA(Wide VGA)分辨率为 480*800
HVGA(Half VGA)即VGA的一半分辨率为 320*480
QVGA(Quarter VGA)即VGA非四分之一分辨率为240*320

 

drawable-(hdpi,mdpi,ldpi)和WVGA,HVGA,QVGA的联系
hdpi里面主要放高分辨率的图片,如WVGA (480×800),FWVGA (480×854)     长宽比  5:3
mdpi里面主要放中等分辨率的图片,如HVGA (320×480)                            长宽比   3:2
ldpi里面主要放低分辨率的图片,如QVGA (240×320)                                 长宽比  4:3
系统会根据机器的分辨率来分别到这几个文件夹里面去找对应的图片。

 

在开发程序时为了兼容不同平台不同屏幕,我们可以上面制定长宽比裁剪图片,并确保有足够分辨率,并把它放入对应目录即可。

比如我们希望设置我们应用窗口的背景,则可以简单的用上面提到的规范裁剪对应的图片,然后做下面步骤即可:

1、将背景图片COPY到对应的drawable文件夹中,假设这里用的是bg1.png

2、修改main.xml,添加 android:background="@drawable/bg1" 即可:

3、这时候的布局文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@drawable/bg1">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Hello Android"/>
</LinearLayout>

 

参考资料:

Android2.1中的 drawable(hdpi,ldpi,mdpi) 的区别
http://blog.csdn.net/infsafe/archive/2010/03/29/5426562.aspx

Android开发中的drawable-(hdpi,mdpi,ldpi)和WVGA,HVGA,QVGA的区别以及联系
http://disanji.net/2011/04/25/android-development-drawable-hdpimdpildpi-wvgahvgaqvga-diff-connection/

Android调用天气预报的WebService简单例子

下面例子改自网上例子:http://express.ruanko.com/ruanko-express_34/technologyexchange5.html

不过网上这个例子有些没有说明,有些情况不一样了,所以我重新写了。

一、获取并使用KSOAP包

在Android SDK中并没有提供调用WebService的库,因此,需要使用第三方的SDK来调用WebService。PC版本的WebService库非常丰富,但这些对Android来说过于庞大。适合手机的WebService客户端的SDK有一些,比较常用的是KSOAP2。

KSOAP2 地址:http://code.google.com/p/ksoap2-android/

我下载的最新的是: ksoap2-android-assembly-2.5.4-jar-with-dependencies.jar

注意:

我在使用ksoap2-android时犯了一个低级错误:使用时报错误:The import org.ksoap2 cannot be resolved。
当时分析这个问题时一直以为是Eclipse出了问题,找了好多方法都不行,
实际是我下载的ksoap2-android-assembly-2.5.4-jar-with-dependencies.jar文件是错误的导致的,走了弯路。

http://code.google.com/p/ksoap2-android/wiki/HowToUse?tm=2 页面 通过鼠标右键链接另存为存的是同名的一个纯文本的Html文件。而不是我们想要的。

我是在
http://code.google.com/p/ksoap2-android/source/browse/m2-repo/com/google/code/ksoap2-android/ksoap2-android-assembly/2.5.4/ksoap2-android-assembly-2.5.4-jar-with-dependencies.jar  点 View raw file 才正确下载对应文件的。

image

 

选择我们的项目,右键菜单中 Build Path –> Add External Archives… 增加这个下载的包

image

增加好后,我们在 选择我们的项目,右键菜单中 Build Path –> Configure Build Path 的 Libraries 中可以看到下面图:

image

二,分以下几步来调用 WebService

 

1、指定 WebService 的命名空间和调用方法

import org.ksoap2.serialization.SoapObject;
private static final String NAMESPACE = "http://WebXml.com.cn/";
private static final String METHOD_NAME = "getWeatherbyCityName";

SoapObject rpc = new SoapObject(NAMESPACE, METHOD_NAME);

SoapObject类的第一个参数表示WebService的命名空间,可以从WSDL文档中找到WebService的命名空间。

第二个参数表示要调用的WebService方法名。

2、设置调用方法的参数值,如果没有参数,可以省略,设置方法的参数值的代码如下:

rpc.addProperty("theCityName", "北京");

要注意的是,addProperty方法的第1个参数虽然表示调用方法的参数名,但该参数值并不一定与服务端的WebService类中的方法参数名一致,只要设置参数的顺序一致即可。

3、生成调用Webservice方法的SOAP请求信息。

SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut = rpc;
envelope.dotNet = true;
envelope.setOutputSoapObject(rpc);

创建SoapSerializationEnvelope对象时需要通过SoapSerializationEnvelope类的构造方法设置SOAP协议的版本号。

该版本号需要根据服务端WebService的版本号设置。

在创建SoapSerializationEnvelope对象后,不要忘了设置SOAPSoapSerializationEnvelope类的bodyOut属性,

该属性的值就是在第一步创建的SoapObject对象。

4、创建HttpTransportsSE对象。

这里不要使用 AndroidHttpTransport ht = new AndroidHttpTransport(URL); 这是一个要过期的类

private static String URL = "http://www.webxml.com.cn/webservices/weatherwebservice.asmx";
HttpTransportSE ht = new HttpTransportSE(URL); 
ht.debug = true;

5、使用call方法调用WebService方法

private static String SOAP_ACTION = "http://WebXml.com.cn/getWeatherbyCityName";
ht.call(SOAP_ACTION, envelope);

网上有人说这里的call的第一个参数为null,但是经过我的测试,null是不行的。

第2个参数就是在第3步创建的SoapSerializationEnvelope对象。

6、获得WebService方法的返回结果

有两种方法:

1、使用getResponse方法获得返回数据。

private SoapObject detail;
detail =(SoapObject) envelope.getResponse();

2、使用 bodyIn 及 getProperty。

private SoapObject detail;
SoapObject result = (SoapObject)envelope.bodyIn;
detail = (SoapObject) result.getProperty("getWeatherbyCityNameResult");

7、 这时候执行会出错,提示没有权限访问网络

需要修改 AndroidManifest.xml 文件,赋予相应权限

简单来说就是增加下面这行配置:<uses-permission android:name="android.permission.INTERNET"></uses-permission>

完整的 AndroidManifest.xml 文件 如下:

 

注:Android 中在代码中为了调试写了system.out.print()输出项

在菜单:Window–>show view–>other–>找到Android,选择Logcat 是可以看到输出的,

如果你想在一个单独的窗口看到system.out.print()的输出的话,可以在logcat界面点那个绿色的“+”好,

在Filter name 和 By log tag里面均填入System.out,这样的话你就能在单独的界面查看system.out.print()的输出了!!

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="ghj1976.MyWeather" android:versionCode="1"
	android:versionName="1.0">

	<application android:icon="@drawable/icon" android:label="@string/app_name">
		<activity android:name=".MyWeatherActivity" android:label="@string/app_name">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
	<uses-permission android:name="android.permission.INTERNET"></uses-permission>

</manifest>

完整的代码如下:

package ghj1976.MyWeather;

import java.io.UnsupportedEncodingException;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
//import org.ksoap2.transport.AndroidHttpTransport;
import org.ksoap2.transport.HttpTransportSE;

public class MyWeatherActivity extends Activity {

	private Button okButton;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		okButton = (Button) this.findViewById(R.id.btn_Search);
		okButton.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View v) {
				  String city = "北京";
				  getWeather(city);
			}

		});
	}

	private static final String NAMESPACE = "http://WebXml.com.cn/";

	// WebService地址
	private static String URL = "http://www.webxml.com.cn/webservices/weatherwebservice.asmx";

	private static final String METHOD_NAME = "getWeatherbyCityName";

	private static String SOAP_ACTION = "http://WebXml.com.cn/getWeatherbyCityName";

	private String weatherToday;

	private SoapObject detail;

	public void getWeather(String cityName) {
		try {
			System.out.println("rpc------");
			SoapObject rpc = new SoapObject(NAMESPACE, METHOD_NAME);
			System.out.println("rpc" + rpc);
			System.out.println("cityName is " + cityName);
			rpc.addProperty("theCityName", cityName);

			SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
			envelope.bodyOut = rpc;
			envelope.dotNet = true;
			envelope.setOutputSoapObject(rpc);

			HttpTransportSE ht = new HttpTransportSE(URL);

			//AndroidHttpTransport ht = new AndroidHttpTransport(URL);
			ht.debug = true;

			ht.call(SOAP_ACTION, envelope);
			//ht.call(null, envelope);

			//SoapObject result = (SoapObject)envelope.bodyIn;
			//detail = (SoapObject) result.getProperty("getWeatherbyCityNameResult");

			detail =(SoapObject) envelope.getResponse();

			//System.out.println("result" + result);
			System.out.println("detail" + detail);
			Toast.makeText(this, detail.toString(), Toast.LENGTH_LONG).show();
			parseWeather(detail);

			return;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private void parseWeather(SoapObject detail)
			throws UnsupportedEncodingException {
		String date = detail.getProperty(6).toString();
		weatherToday = "今天:" + date.split(" ")[0];
		weatherToday = weatherToday + "\n天气:" + date.split(" ")[1];
		weatherToday = weatherToday + "\n气温:"
				+ detail.getProperty(5).toString();
		weatherToday = weatherToday + "\n风力:"
				+ detail.getProperty(7).toString() + "\n";
		System.out.println("weatherToday is " + weatherToday);
		Toast.makeText(this, weatherToday, Toast.LENGTH_LONG).show();

	}
}

参考资料

在Android中访问WebService接口

http://www.cnblogs.com/yy-7years/archive/2011/01/24/1943286.html

Android调用WebService

http://express.ruanko.com/ruanko-express_34/technologyexchange5.html

中国气象局的WebService地址

http://www.webxml.com.cn/WebServices/WeatherWebService.asmx

Android与服务器端数据交互(基于SOAP协议整合android+webservice)

http://www.cnblogs.com/zhangdongzi/archive/2011/04/19/2020688.html

«page 2 of 76»

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章