Interacting with Other Apps

App内部通过创建Intent并显示的指明需要start的组件类名来导航到另一个Activity,而导航到另一个独立的app则不需要这种显示指明。只需要对Intent设置正确的MIME类型和相应的data即可,但在invoke Intent之前需要验证设备上是否已安装能处理这类Intent的app,否则你的app将会发生crash。

验证是否有Activity能处理某类Intent可以通过queryIntentActivities()来做:
PackageManager packageManager =getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,0);
boolean isIntentSafe = activities.size()>0;
一旦创建好Intent并调用startActivity(),如果有超过1个Activity能处理这类Intent,系统将会弹出对话框供用户选择哪个app来打开。系统弹出的该对话框底部会有一个checkbox,用户勾选后以后将默认使用该app处理这类Intent。但有时候你需要用户每次都做选择(例如提供不同的分享平台),这时创建的Intent就需要调用createChooser()来创建一个选择对话框。
当你需要从调用的Activity取回结果时(无论是自己的还是其它app的),应该调用startActivityForResult()。该API多了一个参数request code,当被调用的Activity有结果返回时会调用onActivityResult()回调,同时将request code带回供你判断如何处理返回结果。
@Override
protectedvoid onActivityResult(int requestCode,int resultCode,Intent data){
    // Check which request we're responding to
    if(requestCode == PICK_CONTACT_REQUEST){
        // Make sure the request was successful
        if(resultCode == RESULT_OK){
            // The user picked a contact.
            // The Intent's data Uri identifies which contact was selected.

            // Do something with the contact here (bigger example below)
        }
    }
}
当你的app允许接受其它app的调用时,只需在manifest.xml相应的<activiy>标签中通过<intent-filter>来声明。
<activityandroid:name="ShareActivity">
    <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
    <intent-filter>
        <actionandroid:name="android.intent.action.SENDTO"/>
        <categoryandroid:name="android.intent.category.DEFAULT"/>
        <dataandroid:scheme="sms"/>
        <dataandroid:scheme="smsto"/>
    </intent-filter>
    <!-- filter for sending text or images; accepts SEND action and text or image data -->
    <intent-filter>
        <actionandroid:name="android.intent.action.SEND"/>
        <categoryandroid:name="android.intent.category.DEFAULT"/>
        <dataandroid:mimeType="image/*"/>
        <dataandroid:mimeType="text/plain"/>
    </intent-filter>
</activity>

Saving Data

如果只是存储相对较小的key-values应该使用SharedPreferences APIs。一个SharedPreferences对象指向一个包含key-value pairs的文件并提供了相应的读写方法。而每个SharedPreferences文件则由框架管理并且能指定为独占的或共享的。
获取一个SharedPreferences句柄:
getSharedPreferences():如果想通过名字获取多个SharedPreferences文件则使用该API。
getPreferences():如果只想获取一个SharedPreferences则使用该API。
标识Context.MODE_PRIVATE、MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE分别表示SharedPreferences文件允许以独占、共享读、共享写的权限访问。

如果是大量顺序读写的数据应该使用File。所有的Android设备均有内部和外部两个存储区域,这是历史原因,即使有些设备没有可移动的存储,其固定存储也会被分为内部和外部两个存储区域。
Internal Storage:
1、总是可用。
2、你的app存储的文件默认情况下只能你自己访问。
3、当用户卸载你的app时全部文件都会被删除。
当你不想用户或其它app访问你的文件时内部存储是最好的选择。
External Storage:
1、并不总是可用,因为用户可以挂载一个USB存储来作为外部存储也能移除。
2、全局可访问。
3、当用户卸载你的app时存在getExternalFilesDir()目录下的文件也会被删除。
当文件不需要访问权限或需要共享时外部存储是最好的选择。
写外部存储需要在manifest中指明WRITE_EXTERNAL_STORAGE权限。
Saving a File On Internal Storage:
getFilesDir():返回一个代表你app内部存储目录的File。
getCacheDir():返回一个代表你app内部存储临时目录的File。注意要及时删除这里的文件。
Saving a File On External Storage:
getExternalStorageState():判断外部存储是否可用。
getExternalStoragePublicDirectory():返回一个代表你app外部存储公用目录的File,你app卸载时其中的文件不会被删除。
getExternalFilesDir():返回一个代表你app外部存储私有目录的File,你app卸载时其中的文件也会被删除。

Saving Data in SQL Databasees:SQLiteOpenHelper。

Managing the Activity Lifecycle

Image
Figure 1. A simplified illustration of the Activity lifecycle, expressed as a step pyramid. This shows how, for every callback used to take the activity a step toward the Resumed state at the top, there’s a callback method that takes the activity a step down. The activity can also return to the resumed state from the Paused and Stopped state.
用户在Home界面选择你的app图标时系统将调用你指定的”launcher”或是”main” activity的onCreate方法。该activity在AndroidManifest.xml中被指定,它必须有一个包含MAIN action和LAUNCHER category的,缺少其中任何一个你的app图标就不会再Home界面显示。

系统调用onDestroy一般是在按顺序调用onPause和onStop之后,但有一个例外:当在onCreate中调用finish方法时将会触发系统直接调用onDestroy。

* Does not crash if the user receives a phone call or switches to another app while using your app.
* Does not consume valuable system resources when the user is not actively using it.
* Does not lose the user’s progress if they leave your app and return to it at a later time.
* Does not crash or lose the user’s progress when the screen rotates between landscape and portrait orientation.

当activity处于部分可见(如activity上弹出对话框时)时系统将调用onPause方法。onPause中需要处理一些必要的数据保存工作,但不要处理很耗时的工作(如写数据库)。很耗时的工作将影响activity状态的转换(转到onStop),应该放在onStop中处理。

有些正常的app行为会导致activity被destroy,如:用户按了返回键、activity自己调用了finish方法。另一些情况,如activity处于stop状态很长一段时间了,或者当前拥有焦点的activity需要更多系统资源必须终止后台进程时,或者旋转屏幕时,也会导致activity被destroy。但对后一种情况系统通过Bundle保存了当时activity的信息。

系统会在发生后一种情况时调用onSaveInstanceState来保存activity当时的信息,因此我们可以通过重写该方法实现保存自定义数据。当系统重新创建该activity时,我们可以通过onCreate和onRestoreInstanceState来恢复。当有保存的数据时,系统会在onStart之后调用onRestoreInstaceState;而当用onCreate来恢复时需要注意判断参数是否为空,因为新建而非重建的activity也会调用onCreate。

Supporting Different Devices

多语言的支持通过将本地化的字符串保存到对应的strings.xml中来实现,需要建立以“-对应语言”为后缀的文件夹来存放,如:
English(default locale): res/values/strings.xml
French: res/values-fr/strings.xml
系统会根据用户设备当前的系统设置正确加载本地化的字符串。

Android用size和density两个属性来对设备屏幕进行归类,有4种size:small, normal, large, xlarge和4种density:low, medium, high, extra high。
屏幕多尺寸的支持是将资源存放在以“-”为后缀的文件夹中来实现的。对布局来说可能还要考虑屏幕的方向来优化布局的显示,如:
res/layout/main.xml # default
res/layout-land/main.xml # landscape
res/layout-large/main.xml # large
res/layout-large-land/main.xml # large landscape
位图则需要对原始的向量图进行缩放来生成对应尺寸的位图,缩放大小:
xhdpi: 2.0
hdpi: 1.5
mdpi: 1
ldip: 0.75
对应文件夹:
res/drawable-xhdpi/awesomeimage.png
res/drawable-hdpi/awesomeimage.png
res/drawable-mdpi/awesomeimage.png
res/drawable-ldpi/awesomeimage.png

多系统版本的支持一是通过在AndroidManifest.xml中通过minSdkVersion和targetSdkVersion来指定支持的最低和最高系统版本,二是尽量使用Android Support Library来在老系统中支持新的特性,三是编码中动态判断是否启用对用特性,如:
private void setUpActionBar() {
// Make sure we’re running on Honeycomb or higher to use ActionBar APIs
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
四是系统解析XML时会忽略不支持的属性。

Building Your First App

按官方教程创建并运行MyFirstApp时会crash,LogCat查看日志发现是一个类没找到,有点莫名其妙。
回头看项目里多了个appcompat_v7的项目,粗略看了下,应该是因为ActionBarActivity是新特性,需要引入这个项目,而这也是crash log指向的。
查了下资料,通过将项目属性中Java Build Path—>Order and Export—>Android 4.4.2勾选后解决,但奇怪的是我重建一个新项目时不再重现crash。

通过AVD Mgr创建AVD时也遇到个坑:AVD Name不能包含空格,不然OK按钮是禁用的。