Android Training Note

google为了让广大Android开发者可以高效地建立优质的app,专门针对开发者提供了Training板块,这个板块的学习资料是最一手的,来自google android工程师之手的。这个资料是每个Android开发者都应该学习的手册,而且它是不断更新的。连接: 
https://developer.android.com/training/index.html 
中文版:
http://hukai.me/android-training-course-in-chinese/index.html 

html

 

版本适配android

Tip:为了能在几个Android版本中都能提供最好的特性和功能,你应该在你的app中使用Android Support Library,它能使你的app能在旧平台上使用最近的几个平台的APIs。程序员

适配不一样的系统版本俩种方法:
  1. 指定最小和目标API级别,具体来讲,<uses-sdk> 元素中的 minSdkVersion和 targetSdkVersion属性,标明在设计和测试app时,最低兼容API的级别和最高适用的API级别(这个最高的级别是须要经过你的测试的)。随着新版本Android的发布,一些风格和行为可能会改变,为了能使你的app能利用这些变化,并且能适配不一样风格的用户的设备,你应该设置 targetSdkVersion 的值去匹配最新的可用Android版本。
  2. 在运行时检查系统版本。
        @SuppressLint("NewApi")
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.activity_main, menu);
		// 若是运行的环境 (部署到什么版本的手机 )大于3.0
		if (android.os.Build.VERSION.SDK_INT > 11) {
			SearchView searchView = (SearchView) menu.findItem(
					R.id.action_search).getActionView();
			searchView.setOnQueryTextListener(this);// 搜索的监听
		} 
		return true;
	}
Note:当解析XML资源时,Android会忽略当前设备不支持的XML属性。因此你能够安全地使用较新版本的XML属性
而不须要担忧旧版本Android遇到这些代码时会崩溃。
 
国际化
为了支持多国语言,在 res/ 中建立一个额外的 values 目录以连字符和ISO国家代码结尾命名,好比 values-es/是为语言代码为"es"的区域设置的简单的资源文件的目录。Android会在运行时根据设备的区域设置,加载相应的资源。详见Providing
 
app/build.gradle
Android Studio使用Gradle 编译运行Android工程. 工程的每一个模块以及整个工程都有一个build.gradle文件。一般你只须要关
注模块的build.gradle文件,该文件存放编译依赖设置,包括defaultConfig设置:
  • compiledSdkVersion你的应用将要编译的目标Android版本,此处默认为你的SDK已安装的最新Android版本(目前应该是4.1或更高版本,若是你没有安装一个可用Android版本,就要先用SDKManager来完成安装),你仍然可使用较老的版本编译项目,但把该值设为最新版本,使你可使用Android的最新特性,同时能够在最新的设备上优化应用来提升用户体验。
  • targetSdkVersion 表示你测试过你的应用支持的最高Android版本(一样用API level)表示).当Android发布最新版本后,你应该在最新版本的Android测试你的应用同时更新target sdk到Android最新版本,以便充分利用Android新版本的特性。
布局
  • 可选的布局文件:在XML中定义界面布局而不是在运行时去动态生成布局是有多个缘由的,其中最重要的一个缘由是这样可使得你为不一样大小的屏幕建立不一样的布局文件。例如,你能够建立建立2个版本的布局文件,告诉系统在小的屏幕上使用其中一个布局文件,在大的屏幕上使用另一个布局文件。
  • 于字符串 @string/edit_message,该字符串资源与id使用了相同的名称(edit_message)。然而,对于资源的引用是区分类型的(好比id和字符串),所以,使用相同的名称不会引发冲突
  • 当你在用户界面定义一个文本的时候,你应该把每个文本字符串列入资源文件。对于全部字符串值,字符串资源可以单独的修改,在资源文件里你能够很容易的找到而且作出相应的修改。经过选择定义每一个字符串,还容许您对不一样语言本地化应用程序。
  • android:layout_weight,“两份伏特加酒,一份咖啡利口酒”,意思就是这个酒中伏特加酒占三分之二。(请注意,使用权重的前提通常是给View的宽或者高的大小设置为0dp,而后系统根据上面的权重规则来计算View应该占据的空间。可是不少状况下,若是给View设置了match_parent的属性,那么上面计算权重时则不是一般的正比,而是反比,也就是权重值大的反而占据空间小)
使用字符资源:
  1. 代码中:String hello = getResources().getString(R.string.hello_world);
  2. 布局中:android:text="@string/hello_world"
 
屏幕适配
有4种广泛尺寸:小(small),普通(normal),大(large),超大(xlarge)
4种广泛分辨率:低精度(ldpi), 中精度(mdpi), 高精度(hdpi), 超高精度(xhdpi)
res/layout-large/
xhdpi: 2.0
hdpi: 1.5
mdpi: 1.0 (基准)
ldpi: 0.75
Note:低密度(ldpi)资源是非必要的,当你提供了hdpi的图像,系统会把hdpi的图像按比例缩小一半,去适配ldpi的屏幕。

Activity
为让新启动的activity能查询, 定义key为一个public型的常量,一般使用应用程序包名做为前缀来定义意图键是很好的作法。在应用程序与其余应用程序进行交互时仍能够确保意图键惟一。
    public final static String EXTRA_MESSAGE ="com.mycompany.myfirstapp.MESSAGE";
    Intent intent = new Intent(this, DisplayMessageActivity.class);
    EditText editText = (EditText) findViewById(R.id.edit_message);
    String message = editText.getText().toString();
    intent.putExtra(EXTRA_MESSAGE,message);

    TextView textView = new TextView(this);
    textView.setTextSize(40);
    textView.setText(message);
    //	Set	the	text	view	as	the	activity	layout
    setContentView(textView);

 

使你的activity有一个透明背景:<activity android:theme="@android:style/Theme.Translucent">
不像其余编程范式同样:程序从 main() 方法开始启动。Android系统根据生命周期的不一样阶段唤起对应的回调函数来执行代码。系统存在启动与销毁一个activity的一套有序的回调函数。
 
如何实现一个符合用户期待的app,你须要注意下面几点:
  • 当使用你的app的时候,不会由于有来电通话或者切换到其余app而致使程序crash。
  • 当用户没有激活某个组件的时候不要消耗宝贵的系统资源。
  • 当离开你的app而且一段时间后返回,不要丢失用户的使用进度。
  • 当设备发送屏幕旋转的时候,不会crash或者丢失用户的使用进度。
activity生命周期
  • onCreate里面尽可能少作事情,避免程序启动过久都看不到界面
  • 一般,你不该该使用onPause()来保存用户改变的数据 (例如填入表格中的我的信息) 到永久存储(File或者DB你确认用户期待那些改变可以被自动保存的时候(例如正在撰写邮件草稿),你能够把那些数据存到永久存储 。应该避免在onPause()时执行CPU-intensive 的工做,例如写数据到DB,由于它会致使切换到下一个activity变应该把那些heavy-load的工做放到onStop()去作)。若是你的activity其实是要被Stop,那么你应该为了切换的顺畅而减小在OnPause()方法里面的工做量。
  • 请注意,系统每次调用这个方法(onResume)时,activity都处于最前台,包括第一次建立的时候。因此,你应该实现onResume()来初始化那些你在onPause方法里面释放掉的组件,并执行那些activity每次进入Resumed state都须要的初始化动做 (例如开始动画与初始化那些只有在获取用户焦点时才须要的组件)
  • 由于系统在activity中止时会在内存中保存了Activity实例。有些时候你不须要实现onStop(),onRestart()甚至是onStart()方法.由于大多数的activity相对比较简单,activity会本身中止与重启,你只须要使用onPause()来中止正在运行的动做并断开系统资源连接。
从新建立Activity
  • 一般来讲,跳转到其余的activity或者是点击Home都会致使当前的activity执行onSaveInstanceState,由于这种状况下的activity都是有可能会被destory而且是须要保存状态以便后续恢复使用的,而从跳转的activity点击back回到前一个activity,那么跳转前的activity是执行退栈的操做,因此这种状况下是不会执行onSaveInstanceState的,由于这个activity不可能存在须要重建的操做。
  • 当系统开始中止你的Activity时,只有在Activity实例会须要从新建立的状况下才会调用到onSaveInstanceState()(1) ,在这个方法里面能够指定额外的状态数据到Bunde中。若是这个Activity被destroyed而后这个实例又须要被从新建立时,系统会传递在 (1) 中的状态数据到 onCreate()与 onRestoreInstanceState().
  • onRestoreInstanceState()方法会在 onStart() 方法以后执行. 系统仅仅会在存在须要恢复的状态信息时才会调用 onRestoreInstanceState() ,所以你不须要检查 Bundle 是否为null。
public	void	onSaveInstanceState(Bundle	savedInstanceState)	{
//	Save	the	user's	current	game	state
	savedInstanceState.putInt(STATE_SCORE,	mCurrentScore);
        savedInstanceState.putInt(STATE_LEVEL,	mCurrentLevel);
	super.onSaveInstanceState(savedInstanceState);
}

 

    public void onRestoreInstanceState(Bundle savedInstanceState) {
        //	Always	call	the	superclass	so	it	can	restore	the	view	hierarchy
        super.onRestoreInstanceState(savedInstanceState);
        //	Restore	state	members	from	saved	instance
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    }

  

 

创建隐式的Intent
尽管Android系统会确保每个肯定的intent会被系统内置的app(such as the Phone, Email, or Calendar app)之一接收,可是你仍是应该在触发一个intent以前作验证是否有App接受这个intent的步骤。
Caution: 若是你触发了一个intent,并且没有任何一个app会去接收这个intent,那么你的app会crash。
为了验证是否有合适的activity会响应这个intent,须要执行queryIntentActivities() 来获取到可以接收这个intent的全部activity的list。若是返回的List非空,那么你才能够安全的使用这个intent。例如:
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
  
若是 isIntentSafe 是 true , 那么至少有一个app能够响应这个intent。若是是 false 则说明没有app能够handle这个intent。Note: 你必须在第一次使用以前作这个检查,如果不可行,则应该关闭这个功能。若是你知道某个确切的app可以handle这个intent,你也应该提供给用户去下载这个app的连接。?
一个完整的例子,演示了如何建立一个intent来查看地图,验证有app能够handle这个intent,而后启动它。
 
// Build the intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;
// Start an activity if it's safe
if (isIntentSafe) {
    startActivity(mapIntent);
}

  

若是用户但愿每次都弹出选择界面,并且每次都不肯定会选择哪一个app启动,例如分享功能,用户选择分享到哪一个app都是不肯定的,这个时候,须要强制弹出选择的对话框。(这种状况下用户不能选择默认启动的app)。为了显示chooser, 须要使用createChooser()来建立Intent
 
Intent intent = new Intent(Intent.ACTION_SEND);
...
// Always use string resources for UI text. This says something like "Share this photo with"
String title = getResources().getText(R.string.chooser_title);
// Create and start the chooser
Intent chooser = Intent.createChooser(intent, title);
startActivity(chooser);

 

这样就列出了能够响应 createChooser() 中Intent的app,而且指定了标题。数据库

 
被别的应用启动
若是你的app的功能对别的app也有用,那么你的app应该作好响应的准备。例如,若是你建立了一个social app,它能够分享messages 或者 photos 给好友,那么最好你的app可以接收 ACTION_SEND  的intent,这样当用户在其余app触发分享功能的时候,你的app可以出如今待选对话框。
为了使得其余的app可以启动你的activity,你须要在你的manifest文件的 <activity> 标签下添加 <intent-filter> 的属性。
例如,这个有intent filter的activity,当数据类型为文本或图像时会处理 ACTION_SEND 的intent。
<activity android:name="ShareActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
        <data android:mimeType="image/*"/>
    </intent-filter>
</activity>
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    // Get the intent that started this activity
    Intent intent = getIntent();
    Uri data = intent.getData();
    // Figure out what to do based on the intent type
    if (intent.getType().indexOf("image/") != -1) {
        // Handle intents with image data ...
    } else if (intent.getType().equals("text/plain")) {
        // Handle intents with text ...
    }
}
详情看:Intent过滤(92P)
 
 
Fragment
  • 你能够把fragment想象成activity中一个模块化的部分,它拥有本身的生命周期,接收本身的输入事件,能够在acvitity运行过程当中添加或者移除(有点像"子activity",你能够在不一样的activities里面重复使用)可使用包含action bar的 v7 appcompat library。v7 appcompat library 兼容Android2.1(API level 7),也包含了Fragment APIs。
  • 和activity其中一个区别是当你建立Fragment的时候,你必须重写onCreateView()回调方法来定义你的布局。事实上,这是使Fragment运行起来,惟一一个须要你重写的回调方法
			<fragment	android:name="com.example.android.fragments.ArticleFragment"
					android:id="@+id/article_fragment"
					android:layout_weight="2"
					android:layout_width="0dp"
					android:layout_height="match_parent"	/>
  • 当你用XML布局文件的方式将Fragment添加进activity时,你的Fragment是不能被动态移除的
  • 运用fragment(特别是那些你在运行时添加的)的一个很重要的规则就是在布局中你必须有一个容器view,fragment的layout将会放在这个view里面。
  • 用addToBackStack(),当你移除或者替换一个fragment并把它放入返回栈中时,被移除的fragment的生命周期是stopped(不是destoryed).当用户返回从新恢复这个fragment,它的生命周期是restarts。若是你没把fragment放入返回栈中,那么当他被移除或者替换时,它的生命周期是destoryed。
  • addToBackStack()方法提供了一个可选的String参数为事务指定了一个惟一的名字。这个名字不是必须的,除非你打算用FragmentManager.BackStackEntry APIs来进行一些高级的fragments操做。
  • 常常地,你想fragment之间能相互交互,好比基于用户事件改变fragment的内容。全部fragment之间的交互须要经过他们关联的activity,两个fragment之间不该该直接交互。
Fragments之间的交互
定义一个接口
为了让fragment与activity交互,你能够在Fragment 类中定义一个接口,而且在activity中实现这个接口。Fragment在他们生命周期的onAttach()方法中捕获接口的实现,而后调用接口的方法来与Activity交互。
    public class HeadlinesFragment extends ListFragment {
        OnHeadlineSelectedListener mCallback;

        //	Container	Activity	must	implement	this	interface
        public interface OnHeadlineSelectedListener {
            public void onArticleSelected(int position);
        }

        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            try {
                mCallback = (OnHeadlineSelectedListener) activity;
            } catch (ClassCastException e) {
                throw new ClassCastException(activity.toString()
                        + "	must	implement	OnHeadlineSelectedListener");
            }
        }
                ...
    }

 

如今Fragment就能够经过调用 OnHeadlineSelectedListener 接口实例的   mCallback 中的 onArticleSelected() (也能够是其它方法)方法与activity传递消息。举个例子,在fragment中的下面的方法在用户点击列表条目时被调用,fragment 用回调接口来传递事件给父Activity.
 
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        mCallback.onArticleSelected(position);
    }

  

 
实现接口
为了接收回调事件,宿主activity必须实如今Fragment中定义的接口。举个例子,下面的activity实现了上面例子中的接口。
    public static class MainActivity extends Activity
            implements HeadlinesFragment.OnHeadlineSelectedListener {
                ...

        public void onArticleSelected(int position) {
            //	The	user	selected	the	headline	of	an	article	from	the	HeadlinesFragment
            //	Do	something	here	to	display	that	article
        }
    }

  

 
 
数据存储
写Shared Preference:经过相似putInt()与putString()方法来传递keys与values。而后执行commit() 来提交改变. ( 后来有建议除非是出于线程同 步的须要,不然请使用apply()方法来替代commit(),由于后者有可能会卡到UI Thread.)
 
存储在内部仍是外部
全部的Android设备都有两个文件存储区域:"internal" 与 "external" 存储。 那两个名称来自于早先的Android系统中,当时的大多设备都内置了不可变的内存(internal storage),而后再加上一个相似SD card(external storage)这样能够卸载的存储部件。后来有一些设备把"internal" 与"external" 的部分都作成不可卸载的内置存储了,虽然如此,可是这一整块仍是从逻辑上有被划分为"internal"与"external"的。只是如今再也不以是否能够卸载来区分了。 下面列出了二者的区别:
Internal storage:
  • 老是可用的
  • 这里的文件默认是只能被你的app所访问的。
  • 当用户卸载你的app的时候,系统会把internal里面的相关文件都清除干净。
  • Internal是在你想确保不被用户与其余app所访问的最佳存储区域。
External storage:
  • 并不老是可用的,由于用户有时会经过USB存储模式挂载外部存储器,当取下挂载的这部分后,就没法对其进行访问了。
  • 是你们均可以访问的,所以你可能会失去保存在这里的文件的访问控制权。
  • 当用户卸载你的app时,系统仅仅会删除external根目录(getExternalFilesDir())下的相关文件。
  • External是在你不须要严格的访问权限而且你但愿这些文件可以被其余app所共享或者是容许用户经过电脑访问时的最佳存储区域。
Tip: 尽管app是默认被安装到internal storage的,你仍是能够经过在程序的manifest文件中声明android:installLocation 属性来指定程序也能够被安装到external storage。当某个程序的安装文件很大且用户的internal storage空间大于external storage时,他们会倾向与将该程序安装到external storage。
你能够执行openFileOutput() 来获取一个 FileOutputStream 用于写文件到internal目录
    String filename = "myfile";
    String string = "Hello world!";
    FileOutputStream outputStream;
    try{
        outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
        outputStream.write(string.getBytes());
        outputStream.close();
    } catch(Exception e){
        e.printStackTrace();
    }

  

若是,你须要缓存一些文件,你可使用createTempFile()。例如:下面的方法从URL中抽取了一个文件名,而后再在程序的internal缓存目录下建立了一个以这个文件名命名的文件。编程

 public File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    catch (IOException e) {
        // Error while creating file
    }
    return file;
}

  

你使用MODE_PRIVATE ,那么这些文件就不可能被其余app所访问
 
尽管external storage对于用户与其余app是可修改的,那么你可能会保存下面两种类型的文件。
  • Public files :这些文件对与用户与其余app来讲是public的,当用户卸载你的app时,这些文件应该保留。例如,那些被你的app拍摄的图片或者下载的文件。
  • Private files: 这些文件应该是被你的app所拥有的,它们应该在你的app被卸载时删除掉。尽管因为存储在external  storage,那些文件从技术上而言能够被用户与其余app所访问,但实际上那些文件对于其余app是没有意义的。所以,当用户卸载你的app时,系统会删除你的app的private目录。例如,那些被你的app下载的缓存文件。
若是你想要保存文件为public形式的,请使用getExternalStoragePublicDirectory()方法来获取一个 File 对象来表示存储在external storage的目录。这个方法会须要你带有一个特定的参数来指定这些public的文件类型,以便于与其余public文件进行分类。参数类型包括DIRECTORY_MUSIC 或者 DIRECTORY_PICTURES. 以下:
public File getAlbumStorageDir(String albumName) {
    // Get the directory for the user's public pictures directory.
    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

  

 
若是你想要保存文件为私有的方式,你能够经过执行getExternalFilesDir() 来获取相应的目录,而且传递一个指示文件类型的参数。每个以这种方式建立的目录都会被添加到external storage封装你的app目录下的参数文件夹下(以下则是albumName)。这下面的文件会在用户卸载你的app时被系统删除。以下示例:
public File getAlbumStorageDir(Context context, String albumName) {
    // Get the directory for the app's private pictures directory.
    File file = new File(context.getExternalFilesDir(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

  

 
请记住,getExternalFilesDir() 方法会建立的目录会在app被卸载时被系统删除。若是你的文件想在app被删除时仍然保留,请使用getExternalStoragePublicDirectory().
 
无论你是使用 getExternalStoragePublicDirectory() 来存储能够共享的文件,仍是使用 getExternalFilesDir() 来储存那些对与你的app来讲是私有的文件,有一点很重要,那就是你要使用那些相似 DIRECTORY_PICTURES  的API的常量。那些目录类型参数能够确保那些文件被系统正确的对待。例如,那些以 DIRECTORY_RINGTONES  类型保存的文件就会被系统的media scanner认为是ringtone而不是音乐。
 
查询剩余空间
若是你事先知道你想要保存的文件大小,你能够经过执行getFreeSpace() or getTotalSpace() 来判断是否有足够的空间来保存文件,从而避免发生IOException。
那些方法提供了当前可用的空间还有存储系统的总容量。然而,系统并不能保证你能够写入经过 getFreeSpace() 查询到的容量文件, 若是查询的剩余容量比你的文件大小多几MB,或者说文件系统使用率还不足90%,这样则能够继续进行写的操做,不然你最好不要写进去。
Note: 你并无被强制要求在写文件以前必定有要去检查剩余容量。你能够尝试先作写的动做,而后经过捕获 IOException 。这种作法仅适合于你事先并不知道你想要写的文件的确切大小。例如,若是在把PNG图片转换成JPEG以前,你并不知道最终生成的图片大小是多少。
 
删除文件
你应该在不须要使用某些文件的时候,删除它。删除文件最直接的方法是直接执行文件的 delete() 方法。
myFile.delete();
若是文件是保存在internal storage,你能够经过 Context 来访问并经过执行 deleteFile() 进行删除
myContext.deleteFile(fileName);
Note: 当用户卸载你的app时,android系统会删除如下文件:
  • 全部保存到internal storage的文件。
  • 全部使用getExternalFilesDir()方式保存在external storage的文件。
然而,一般来讲,你应该手动删除全部经过 getCacheDir() 方式建立的缓存文件,以及那些不会再用到的文件。
 
数据库
就像保存文件到设备的internal storage 同样,Android会保存db到你的程序的private的空间上。你的数据是受保护的,由于那些区域默认是私有的,不可被其余程序所访问。
Note:由于那些操做多是很耗时的,请确保你在background thread(AsyncTask or IntentService)里面去执行 getWritableDatabase() 或者 getReadableDatabase() 。
 
和查询信息同样,删除数据一样须要提供一些删除标准。DB的API提供了一个防止SQL注入的机制来建立查询与删除标准。
SQL Injection:(随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也愈来愈多。可是因为程序员的水平及经验也良莠不齐,至关大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户能够提交一段数据库查询代码,根据程序返回的结果,得到某些他想得知的数据,这就是所谓的SQL Injection,即 SQL注入)
这个机制把查询语句划分为选项条款与选项参数两部分。条款部分定义了查询的列的特征,参数部分用来测试是否符合前面的条款。由于处理的结果与一般的SQL语句不一样,这样能够避免SQL注入问题。
 
 
 
 
 
Android图像和动画
高效显示Bitmap
  • 移动设备的系统资源有限。Android设备对于单个程序至少须要16MB的内存。程序应该在这个最低内存限制下最优化程序的效率。固然,大多数设备的都有更高的限制需求。
  • 在过去, 一个比较流行的内存缓存的实现方法是使用软引用(SoftReference)或弱引用(WeakReference)bitmap缓存, 然而这是不推荐的。从Android 2.3 (API Level 9) 开始,GC变得更加频繁的去释放soft/weak references,这使得他们就显得效率低下。并且在Android 3.0 (API Level 11)以前,备份的bitmap是存放在native memory 中,它不是以可预知的方式被释放,这样可能致使程序超出它的内存限制而崩溃。
  • 在上面的例子中, 有1/8的程序内存被做为Cache. 在一个常见的设备上(hdpi),最小大概有4MB (32/8). 若是一个填满图片的GridView组件放置在800x480像素的手机屏幕上,大概会花费1.5MB (800x480x4 bytes), 所以缓存的容量大概能够缓存2.5页的图片内容.、

磁盘缓存能够用来保存那些已经处理好的位图,而且在那些图片在内存缓存中不可用时减小加载的次数。固然从磁盘读取图片会比从内存要慢,并且读取操做须要在后台线程中处理,由于磁盘读取操做是不可预期的。
Note:若是图片被更频繁的访问到,也许使用 ContentProvider 会更加的合适,好比在Gallery程序中。
内存缓存的检查是能够在UI线程中进行的,磁盘缓存的检查须要在后台线程中处理。磁盘操做永远都不该该在UI线程中发生。当图片处理完成后,最后的位图须要添加到内存缓存与磁盘缓存中,方便以后的使用。
 
处理配置改变(Handle Configuration Changes):详情请看P162
为了测试上面的效果,尝试在保留Fragment与没有这样作的状况下旋转屏幕。你会发现当你保留缓存时,从内存缓存中从新绘制几乎没有延迟的现象. 内存缓存中没有的图片可能在存在磁盘缓存中.若是两个缓存中都没有,则图像会像平时同样被处理。
 
Android管理bitmap memory的演变进程
  • 在Android 2.2 (API level 8)以及以前, 当GC发生时, 你的应用的线程是会stopped的. 这致使了一个滞后,它会下降效率. 在Android 2.3上,添加了并发GC的机制, 这意味着在一个bitmap再也不被引用到以后,内存会被当即回收.
  • 在Android 2.3.3 (API level 10)以及以前, 一个bitmap的像素级数据是存放在native内存中的. 这些数据与bitmap自己是隔离的, bitmap自己是被存放在Dalvik heap中。在native内存中的pixel数据的释放是不可预测的,这意味着有可能致使一个程序容易超过它的内存限制并Crash。 自Android 3.0 (API Level 11)起, pixel数据则是与bitmap自己一块儿存放在dalvik heap中。
在Android 3.0及以上版本管理内存:详情请看P165
在Android 3.0 (API Level 11) 引进了BitmapFactory.Options.inBitmap. 若是这个值被设置了,decode方法会在加载内容的时候去重用已经存在的bitmap. 这意味着bitmap的内存是被从新利用的,这样能够提高性能, 而且减小了内存的分配与回收。然而,使用inBitmap有一些限制。特别是在Android 4.4 (API level 19)以前,只支持同等大小的位图。详情请查看inBitmap文档.
后面的不是很懂,往后再看
 
添加动画
滥用动画或者在错误时机使用动画也是有害的,例如:他们形成了延迟。这节课程告诉你如何应用经常使用动画类型来提高易用性,在不给用户用户增长烦恼的前提下提高性能。
 
俩个view间渐变
渐变更画(也叫消失)一般指渐渐的淡出某个 UI 组件,同时同步地淡入另外一个。在你 App 想切换内容或 view的状况下,这种动画颇有用。渐变简短不易察觉,它也能提供从一个界面到下一个之间流畅的转换。当你不使用它们,无论怎么样转换常常感到生硬而仓促。
将 config_shortAnimTime 系统属性暂存到一个成员变量里。这个属性为动画定义了一个标准的“短”持续时间。对于微妙或者快速发生的动画,这是个很理想的时间段。config_longAnimTime 和 config_mediumAnimTime 也行,若是你想用的话。
 
    // 获取并缓存系统默认的“短”时长
    mShortAnimationDuration = getResources().getInteger(
            android.R.integer.config_shortAnimTime);
  • 对于淡入的 view,设置 alpha 值为 0 而且设置 visibility 为 VISIBLE(要记得他起初被设置成了 GONE)。这让iew 可见了可是它是透明的。
  • 对于淡入的 view,把 alpha 值从 0 动态改变到 1。同时,对于淡出的 view,把 alpha 值从 1 动态变到 0。
  • 使用 Animator.AnimatorListener 中的 onAnimationEnd(),设置淡出 view 的 visibility 为 GONE。即便 alpha 值为0,也要把 view 的 visibility 设置成 GONE 来防止 view 占据布局空间,还能把它从布局计算中忽略,加速处理过程。
    // 设置内容View为0%的不透明度,可是状态为“可见”,
    // 所以在动画过程当中是一直可见的(可是为全透明)。
    mContentView.setAlpha(0f);
    mContentView.setVisibility(View.VISIBLE);
    // 开始动画内容View到100%的不透明度,而后清除全部设置在View上的动画监听器。
    mContentView.animate()
            .alpha(1f)
            .setDuration(mShortAnimationDuration)
            .setListener(null);
    // 加载View开始动画逐渐变为0%的不透明度,
    // 动画结束后,设置可见性为GONE(消失)做为一个优化步骤
    //(它将再也不参与布局的传递等过程)
    mLoadingView.animate()
            .alpha(0f)
            .setDuration(mShortAnimationDuration)
            .setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mLoadingView.setVisibility(View.GONE);
                }
            });

  

viewpager
   @Override
    public void onBackPressed() {
        if (mPager.getCurrentItem() == 0) {
            // 若是用户当前正在看第一步(也就是第一页),那就要让系统来处理返回按钮。
            //这个是结束(finish())当前活动并弹出回退栈。
            super.onBackPressed();
        } else {
            // 不然,返回前一页
            mPager.setCurrentItem(mPager.getCurrentItem() - 1);
        }
    }
viewpager滑动动画用PageTransformer自定义动画
 
 
 
 
Android网络链接和云服务
管理网络
在执行网络操做以前检查设备当前链接的网络链接信息是个好习惯。这样能够防止你的程序在无心间链接使用了非意向的网络频道。若是网络链接不可用,你的应用应该优雅的作出响应。 为了检测网络链接,咱们须要使用到下面两个类:
  • ConnectivityManager: 它会回答关于网络链接状态的查询,并在网络链接改变时通知应用程序。
  • NetworkInfo: 描述一个给定网络类型(就本节而言是移动网络或Wi-Fi)的网络接口的状态。
public boolean isOnline() {
    ConnectivityManager connMgr = (ConnectivityManager)
            getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
    return (networkInfo != null && networkInfo.isConnected());
}

private static final String DEBUG_TAG = "NetworkStatusExample";
...
    ConnectivityManager connMgr = (ConnectivityManager)
        getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
    boolean isWifiConn = networkInfo.isConnected();
    networkInfo = connMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
    boolean isMobileConn = networkInfo.isConnected();
    Log.d(DEBUG_TAG, "Wifi connected: " + isWifiConn);
    Log.d(DEBUG_TAG, "Mobile connected: " + isMobileConn);
相关文章
相关标签/搜索