在前一篇总结了下Android 入门——项目结构目录、文件详解,从这一篇正式开始了总结和学习Android开发之旅,Web应用最基本的UI是网页、Winform应用最基本的UI就是Form,那么Activity就是Android最基本的UI,只要有UI都会涉及到Activity。换言之,一个Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务,例如拨号、拍照、发送email、看地图。每一个activity被给予一个窗口,在上面可以绘制用户接口。窗口通常充满屏幕,但也可以小于屏幕而浮于其它窗口之上。(设置样式即可,或者代码设置大小、位置等等)
Context类是所有Android应用程序的核心,绝大部分的应用程序和底层交互的功能都通过Context访问,其设的目的就是为了更加便捷于开发者与底层交互。其实质只是一个抽象类,定义了很多方法,比如获取系统服务、得到SharedPreferences、启动Activity等等。
public abstract class Context {
...
public abstract Object getSystemService(String name); //获得系统级服务
public abstract void startActivity(Intent intent); //通过一个Intent启动Activity
public abstract ComponentName startService(Intent service); //启动Service
//根据文件名得到SharedPreferences对象
public abstract SharedPreferences getSharedPreferences(String name,int mode);
...
}
Activity继承Context,Activity是用来与用户交互的主要UI,一个普通的app由若干个业务(task)构成,可以把每一个提供用户交互的业务理解为一个Activity,比如登录页面是一个Activity。一般来说一个Activity的目的在于处理单个屏幕的UI效果展示。Android本身也预定义了很多的Activity,开发的很多工作我们都可以直接调用就行了,比如说:拨号界面,设置界面,拍照界面我们能看到的一切界面都是Activity。
Intent 本身是定义为一个类,一个Intent实例按照一定的格式把自己要表达一个意图或目的封装起来。Android则根据此Intent对实例,找出相配的组件,然后将 Intent对象传递给所找到的组件,此时他的传话工作就完成了。说得直白点,Intent 其实就是各组件交互的的跑腿角色,比如你想打电话的时候,你是不是希望那个拨打电话的Activity启动?那么你要做的就是把你要做点事(打电话)封装成对应的Intent 实例,然后Android系统接收到了Intent实例之后自动找到对应的接收者。
当Activity从一种状态转变到另一种状态时,系统会自动调用对应的方法来通知这种变化
如果按照Activity的活动状态区分,还可以把Activity的生命周期归纳为:启动Activity、Activity退居后台、Activity返回前台、锁屏、解锁时的生命周期方法变化。
启动Activity: onCreate()—>onStart()—>onResume(),Activity进入运行状态。
Activity退居后台: 当前Activity转到新的Activity界面或按Home键回到主屏:onPause()—>onStop(),进入停滞状态。
Activity返回前台:onRestart()—>onStart()—>onResume(),再次回到运行状态。
Activity退居后台,且系统内存不足,系统会杀死这个后台状态的Activity(此时这个Activity引用仍然处在任务栈中,只是这个时候引用指向的对象已经为null),若再次回到这个Activity,则会走onCreate()–>onStart()—>onResume()(将重新走一次Activity的初始化生命周期)
锁屏:onPause()->onStop()
解锁:onStart()->onResume()
Activity的跳转又分为显式跳转和隐式跳转,调用的方法大同小异,区别只是在于传递的intent对象不同,其实实质是Intent对象的两种匹配方式。
//显式跳转,适合用于启动同一APP中的Activity
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);//无返回值的跳转
//隐式跳转
Intent intent = new Intent();
intent.setClass(LoginActivity.this, MainActivity.class);
startActivity(intent);
/*startActivityForResult(intent,requestCode); 其中requestCode为请求码,请求码的值随便设置,但必须>=0 ,作用在于加入你有多个操作都可以跳转到MainActivity,你可以在LoginActivity的回调方法中onActivityResult(int requestCode, int resultCode, Intent data)得到你的返回值,区别处理,(从MainActivity回来的时候会执行这个方法)
*/
通过Acitivty的xml标签来改变任务栈的默认行为,使用android:launchMode=”standard|singleInstance|singleTask|singleTop”来控制Acivity任务栈。(任务栈是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会一个一个的出栈,并且调用其onDestory()方法。如果栈内没有Activity,那么系统就会回收这个栈,每个APP默认只有一个栈,以APP的包名来命名.)
Activity的堆栈管理以ActivityRecord为单位,所有的ActivityRecord都放在一个List里面.可以认为一个ActivityRecord就是一个Activity栈
Intent对象传值是Activity之间的传统方式(同样适用于Service、Broadcast Receiver),主要是通过Intent.putExtra()设置要传递的值,再通过getXXXExtra()方法获取传递过来的数据。但是只能传递Java简单类型的数据(包括简单类型的数组)和实现了java.io.Serializable的类的对象实例(类成员也必须是序列化的即实现了java.io.Serializable接口)
//传值:直接保存到Intent中
Intent toMain=new Intent(LoginActivity.this, ManageUserActivity.class);
toMain.putExtra("LoginUser", "userEdt");
toMain.putExtra("Phone", "phone");
startActivity(toMain);
// 接收
Intent intent = getIntent();
String loginUser=intent.getStringExtra("LoginUser").toString();
String phone=intent.getStringExtra("Phone").toString();
/***********************传值:先保存到Bundle***************************/
intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("USERNAME", et_username.getText().toString());
intent.putExtras(bundle);
//接收
Bundle bundle = this.getIntent().getExtras();
String str=bundle.getString("USERNAME");
将类成员定义成public static ,就直接可以通过类成员传递数据,好处是在于可以传递任意类型的数据。
只能直接传递文本数据和Intent对象支持的数据,不过还可以传递可序列化的数据、图像等复杂数据的,思路是用字符串传递二进制数据,将二进制数据转成字符串只需要对其进行编码(一般采用Base64),再传递,接收之后再解码还原。
ClipboardManager clipboard=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);//得到剪切板对象
clipboard.setText(et_username.getText().toString());
//从剪切板中获取数据
ClipboardManager clipboard=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);//得到剪切板对象
clipboard.getText();
/*传递复杂数据,可序列化的数据、图像等,Data为一个可序列化的类,有数据成员id,name……,clipData为已经初始化了Data的可序列化实例**/
ClipboardManager clipboard=(ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);//得到剪切板对象
Data clipData=new Data();
...
//开始对可序列化实例进行Base64编码
ByteArrayOutputStream bouts=new ByteArrayOutputStream();
String strBase64="";
try{
ObjectOutputStream oos=new ObjectOutputStream (bouts);
oos.writeObject(clipData);
//使用base64的将byte[]数据转成base64 字符串
strBase64=Base64.encodeToString(bouts.toByteArray(),Base64.DEFAULT);
oos.close();
}catch(){}
//向剪切板中写入Base64格式的字符串
clipboard.setText(strBase64);
……
/********从剪切板中获取Base64格式的字符串,并解码还原成Data对象********/
//获得剪切板Base64字符串
String reBase64Str=clipboard.getText().toString();
//将Base64格式的字符串还原成byte格式的数据
byte[] buff=Base64.decode(reBase64Str,Base64.DEFAULT);
ByteArrayInputStream bins=new ByteArrayInputStream (buff);
try{
ObjectInputStream ois=new ObjectInputStream (bins);
//将byte[]还原成data
Data data=(data)ois.readObject();
//输出原始的Base64格式的字符串
System.out.println("Data.id"+data.getId()+"Data.name"+data.getName());
...
}catch(){}
可以为每一个应用程序定义一个全局对象,该对象由系统创建,但是使用全局对象的类必须继承android.app.Application,并在该类中定义成员变量和方法
//1定义继承Application全局对象的类,并不会自动创建全局对象的,因为Android系统并不知道哪个类是全局类
public class DataTemp extends Application{
public String userName();
public Data d=new Data();//可不序列化
}
//2在manifest中通过application里的android:name属性定义该全局类
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.weima.amis"
android:versionCode="1"
android:versionName="1.0" >
<application
android:name=".DataTemp"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.Black.NoTitleBar" >
...
//3指定全局类后,在程序运行时,全局对象会被自动创建,而且一直驻留在内存中知道app彻底退出内存可以在应用程序中任何地方获取DataTemp 的对象
DataTemp dt=(dt)getApplicationContext();
根据官方建议,传递简单类型或可序列化的对象,宜采用Intent对象进行数据传递,如果是传递不序列化的对象则可以采用静态变量或全局的对象方式,最后采用全局对象。
对于单一Activity的应用来说,退出很简单,直接finish()即可。当然,也可以用killProcess()和System.exit()这样的方法,不过这些方法都无法完美的关闭应用程序,现提供几个方法,供参考:
1. 抛异常强制退出:该方法通过抛异常,使程序Force Close。验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。
2. 记录打开的Activity:每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。
3. 发送特定广播:在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。
4. 递归退出在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。除了第一个,都是想办法把每一个Activity都结束掉,间接达到目的。但是这样做同样不完美。你会发现,如果自己的应用程序对每一个Activity都设置了nosensor,在两个Activity结束的间隙,sensor可能有效了。但至少,我们的目的达到了,而且没有影响用户使用。为了编程方便,最好定义一个Activity基类,把Activity放到栈中去处理这些共通问题。