Android简介

Android的系统架构

  分为四层,分别是是:应用层、应用框架层、系统运行库层和Linux内核层。

  • 应用层:运行在Dalvik虚拟机(基于寄存器的Java虚拟机,运行Java程序的速度比JVM更快)上的应用程序组成。
  • 应用框架层:由View、通知管理器、活动管理器等可直接调用的API组成。
  • 系统运行库层:Java本身无法直接访问硬件。需要通过NDK才可以。NDK是一些由C/C++语言编写的库(主要是so文件)。该层主要包括C语言标准库、多媒体库、OpenGL ES、SQLite、Webkit、Dalvik虚拟机等。
  • Linux内核层:该层主要包括驱动、内存管理、进程管理、网络协议栈等组件。



Android应用程序的编译过程

  Android应用程序(APK文件)并不是基于传统的JVM的,而是基于Dalvik虚拟机的。JVM是基于堆栈的虚拟机,Dalvik是基于寄存器的Java虚拟机。两者语法相同,但二进制并不兼容。JVM的.class文件并不能在Dalvik虚拟机上直接运行,Dalvik的apk文件也不能在JVM上运行。

  编译应用程序需要如下4步:

  • Android Resource Manager:根据res目录中的资源在R类中生成响应的子类和变量。
  • Android Pref Compiler:编译res目录中的资源。
  • Java Builder:将Java源代码编译成.class文件。
  • Android Package Builder:将.class文件编译成Dalvik虚拟机格式的文件(classes.dex),然后将classes.dex和其他文件一起打包成apk文件。



Activity

Action 和 Category

  android.intent.action.MAIN 和 android.intent.category.LAUNCHER,可以在多个Activity中使用,如果在一个应用程序中声明了多个使用该Action和Category的窗口,系统会在程序列表中生成多个程序图标,每个图标对应某一个使用了android.intent.action.MAIN 和 android.intent.category.LAUNCHER的Activity。


设置窗口标题事件(onTitleChanged)

  窗口的标题可以在AndroidManifest中通过<activity>标签的android:label属性指定,也可以通过setTitle方法动态设置。

  当窗口标题被设置后,会调用onTitleChanged方法。其中title参数表示改变后的窗口标题,可通过setTitle方法设置;color参数表示改变后的窗口标题颜色,通过setTitleColor设置。


protected void onTitleChanged(CharSequence title, int color);

  onPostCreate方法也会调用onTitleChanged方法,onPostCreate在onCreate和onRestoreInstanceState之后调用。


protected void onPostCreate(@Nullable Bundle savedInstanceState) {
    if (!isChild()) {
        mTitleReady = true;
        <strong>onTitleChanged(getTitle(), getTitleColor());</strong>
    }
    mCalled = true;
}



键盘按下和抬起事件(onKeyDown & onKeyUp)

  onKeyDown & onKeyUp:捕获按键按下和抬起事件。所有的物理按键的按下和抬起都由onKeyDown&onKeyUp方法捕捉。

  onKeyDown和onKeyUp方法不能捕获“Home”按键。


  按键重复次数:指按下按键还没有抬起的期间系统会以一定事件间隔(很短)不断发送键盘按下终端,也就是说会不断调用onKeyDown方法,使用KeyEvent.getRepeatCount方法可以获取调用onKeyDown方法的次数,即按键重复次数。RepeatCount从0开始。若按键抬起,并再次按下,RepeatCount会清零。


  捕捉Home键的动作:由于窗口不管以何种方式关闭(按Back键、执行finish方法等),都会调用Activity.finish方法,而且该方法是在调用窗口生命周期方法之前就调用了,也就是说调用onPause方法之前会先调用finish方法。而按Home键系统并不会调用finish方法,所以覆盖finish方法,并在finish方法中设置标志即可解决该问题。


按键长按事件(onKeyLongPress)

  onKeyLongPress方法捕捉按键长按事件,但若简单实现onKeyLongPress方法并不会被调用,必须在onKeyDown方法中调用KeyEvent.startTracking方法,并且onKeyDown方法返回true才可能捕捉到按键长按事件。startTracking方法用于跟踪按键动作,例如按下的时间等。在onKeyUp方法中还可以使用KeyEvent.isTracking方法判断在onKeyDown方法中是否调用了startTracking方法进行跟踪。

  使用startTracking方法应注意:该方法同时只能跟踪一个按键,若在按键过程中按下了别的按键,当前跟踪过程将停止,转而跟踪新按下的按键。


屏幕触摸事件(onTouchEvent)

  Android系统使用的坐标系的0点坐标在屏幕的左上角,坐标值为(0, 0)。屏幕从左向右为x轴的正方向,屏幕从上到下为y轴的正方向。MotionEvent.getY方法从返回的Y坐标值包括标题栏的高度。


窗口获得焦点事件(onWindowFocusChanged)

  除了可以使用Activity生命周期的onResume和onPause方法处理窗口获得和失去焦点的动作,还可以使用onWindowFocusChanged方法来处理窗口获得和失去焦点的动作。


public void onWindowFocusChanged(boolean hasFocur);

  onResume 和 onPause方法在执行结束时都会调用onWindowFocusChanged方法。


在不同Activity之间传递数据

  可通过以下4中数据传递方法:

  • 通过Intent传递数据。


public class Data implements Serializable{}

Intent intent = new Intent(this, NextActivity.class);
intent.putExtra("data_String", "字符串");
intent.putExtra("data_object", new Data());
startActivity(intent);

String str = getIntent().getStringExtra("data_String");
Data data = (Data) getIntent().getExtras().get("data_object");
  • 通过静态变量传递数据。
  • 通过剪贴板传递数据。

  由于剪贴板只能存储简单类型数据或可序列化的对象,对于某些不可序列化的对象如果可以将其转换成字节流(字节数组),也可以将这些对象保存到剪贴板中。


ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
// 存储数据
Data data = new Data();
ByteArrayOutputStream baos = nwe ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(data);
String base64Str = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
oos.close();
ClipData clipData = ClipData.newPlainText("data", base64Str);
clipboard.setPrimaryClip(clipData);

// 取数据
String base64Str = clipboard.getPrimaryClip().getItemAt(0).getText().toString();
byte[] buffer = Base64.decode(base64Str, Base64.DEFAULT);
ByteArrayInputStream bis = new ByteArrayInputStream(buffer);
ObjectInputStream ois = new ObjectInputStream(bis);
Data data = (Data) ois.readObject();
  • 通过全局对象传递数据。


与Activity相关的技巧与特效

  • 全屏显示


requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

 注意:requestWindowFeature方法必须在setContentView方法之前调用,而setFlags只是控制系统的UI,所以该方法可以在代码的任何位置调用。

  setContentView方法的代码主要分为两部分:installDecor和inflate方法,分别用来设置标题栏和装载窗口跟视图。

if((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {
	View titleContainer = findViewById(com.android.internal.R.id.title_container);
	if (titleContainer != null) {
		titleContainer.setVisibility(View.GONE);
	} else {
		mTitleView.setVisibility(View.GONE);
	}
}
  • 修改窗口主题

  注意,setTheme方法必须在setContentView之前调用;动态设置主题的优先级高于静态设置主题。

  • 窗口截屏

  截屏主要分为以下四步:

  1>获取存储屏幕图像的Bitmap对象

  2>获取状态栏高度,由于无法获取状态栏图像,所以必须将状态栏从屏幕图像文件中去除,否则图像上方的状态栏位置会显示一条白色的区域。

  3>获取屏幕图像的高度和宽度。

  4>建立一个新的Bitmap对象(已去除状态栏的高度),将屏幕图像中除了状态栏部分的其他区域复制到该图像中去。

  5>将新建立的Bitmap对象保存到SD卡的根目录。

// 第1步
View v = getWindow().getDecorView();
v.setDrawingCacheEnaabled(true);
v.buildDrawingCache();
Bitmap scrBitmap = v.getDrawingCache();

// 第2步
Rect frame = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
int statusBarHeight = frame.top;

// 第3步
Point outSize = new Point();
getWindowManager().getDefaultDisplay().getSize(outSize);
int width = outSize.x;
int height = outSize.y;

// 第4步
Bitmap bitmap = Bitmap.createBitmap(srcBitmap, 0, statusBarHeight, width, height-statusBarHeight);
v.destroyDrawingCache();

// 第5步
FileOutputStream fos = null;
try {
	File file = File.createTempFile("capture", ".jpg", new File("/sdcard"));
	fos = new FileOutputStream(file);
	if (null != fos) {
		bitmap.compress(Bitmap.CompressFormat.PNG, 90, fos);
		fos.flush();
	}
	fos.close();
} catch(Exception e) {
}