1.LayoutInflater
(1)主要是用于加载布局;
(2)setContentView()的内部原理就是LayoutInflater;
(3)LayoutInflater的实例:
<pre>
//实质是后者的封装
LayoutInflater layoutInflater = LayoutInflater.from(context);
// And
LayoutInflater layoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
</pre>
(3)实例化之后使用inflate()方法来加载布局
<pre>
// resourceId:要加载的布局的ID
// root:给改布局的外层再嵌套一个父布局
layoutInflater.inflate(resourceId, root);
</pre>
(4)原理:LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的。
2.Serivice
(1)Serivce启动:startService(),且一旦以此方式启动的Serivce在stopService()调用之前只能启动一次,即onCreate()只执行一次,知道onDestroy();
(2)Serivice与Activity的通信:onBind()方法起关键作用。
<pre>
private MyService.MyBinder myBinder;
</br>
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
</br>
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
};
</pre>
(3)Service与Thread的关系--没有任何关系
不少Android初学者都可能会有这样的疑惑,Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案可能会有点让你吃惊,因为Service和Thread之间没有任何关系!
之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,你还会觉得它和Thread有什么关系吗?
你可能会惊呼,这不是坑爹么!?那我要Service又有何用呢?其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,前面不是刚刚验证过Service是运行在主线程里的么?在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。
额,既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
一个比较标准的Service就可以写成:
<pre>
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 开始执行后台任务
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
public void startDownload() {
new Thread(new Runnable() {
@Override
public void run() {
// 执行具体的下载任务
}
}).start();
}
}
</pre>
(4)创建前台Service
Service几乎都是在后台运行的,一直以来它都是默默地做着辛苦的工作。但是Service的系统优先级还是比较低的,当系统出现内存不足情况时,就有可能会回收掉正在后台运行的Service。如果你希望Service可以一直保持运行状态,而不会由于系统内存不足的原因导致被回收,就可以考虑使用前台Service。前台Service和普通Service最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。当然有时候你也可能不仅仅是为了防止Service被回收才使用前台Service,有些项目由于特殊的需求会要求必须使用前台Service,比如说墨迹天气,它的Service在后台更新天气数据的同时,还会在系统状态栏一直显示当前天气的信息。
<pre>
public class MyService extends Service {
public static final String TAG = "MyService";
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Notification notification = new Notification(R.drawable.ic_launcher,"有通知到来", System.currentTimeMillis());
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent, 0);
notification.setLatestEventInfo(this, "这是通知的标题", "这是通知的内容",pendingIntent);
//通过此方法设置为前台Serivice
startForeground(1, notification);
Log.d(TAG, "onCreate() executed");
}
.........
}
</pre>
(5)远程Serivce--可以实现Android跨进程通信
将一个普通的Service转换成远程Service其实非常简单,只需要在注册Service的时候将它的android:process属性指定成:remote就可以了,代码如下所示:
<pre>
<service
android:name="com.example.servicetest.MyService"
android:process=":remote" >
</service>
</pre>
为什么将MyService转换成远程Service后就不会导致程序ANR了呢?这是由于,使用了远程Service后,MyService已经在另外一个进程当中运行了,所以只会阻塞其所在进程中的主线程,并不会影响到当前的应用程序。(测试验证可以用Process.myPid()进程ID)
可以看出进程ID和包名都以改变。
远程Serivice的弊端:
远程Serivice与Activity通信不再能使用bindSerivice()的方式了,此时只能使用AIDL来进行跨进程通信了。
(6)AIDL(Android Interface Definition Language)是Android接口定义语言的意思,它可以用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
http://blog.csdn.net/guolin_blog/article/details/9797169
3.Gson
http://stormzhang.com/android/2014/05/22/android-gson/
</br>
4.Android 布局优化
(1)在Android开发中性能优化可能包括:Java代码优化, 算法优化, SQLite优化, 布局优化等。
(2)在Android UI布局过程中,通过遵守一些惯用、有效的布局原则,我们可以制作出高效且复用性高的UI,概括来说包括如下几点:
--尽量多使用RelativeLayout和LinearLayout, 不要使用绝对布局AbsoluteLayout,布局层次一样的情况下LinearLayout比RelativeLayout要好, 但往往RelativeLayout可以简单实现LinearLayout嵌套才能实现的布局;</br>
--将可复用的组件抽取出来并通过include标签使用;
(既能提高UI的制作和复用效率,也能保证制作的UI布局更加规整和易维护。)
header.xml布局:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_above="@id/text"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_alignParentBottom="true"
android:text="@string/app_name" />
</RelativeLayout>
</pre>
main_activity.xml中使用:
<pre>
<include layout="@layout/header" />
</pre>
--使用ViewStub标签来加载一些不常用的布局;
viewstub标签同include标签一样可以用来引入一个外部布局,不同的是,viewstub引入的布局默认不会扩张,即既不会占用显示也不会占用位置,从而在解析layout时节省cpu和内存。 viewstub常用来引入那些默认不会显示,只在特殊情况下显示的布局,如进度布局、网络失败显示的刷新布局、信息出错出现的提示布局等。
例如显示错误的布局:
<pre>
//error.xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@android:color/white"
android:padding="10dip"
android:text="Message"
android:textColor="@android:color/black" />
</pre>
<pre>
//main.xml
<ViewStub
android:id="@+id/error_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout="@layout/error" />
</pre>
<pre>
//MainActivity种使用方法
private View errorView;
private void showError() {
// not repeated infalte
if (errorView != null) {
errorView.setVisibility(View.VISIBLE);
return;
}
ViewStub stub = (ViewStub)findViewById(R.id.error_layout);
errorView = stub.inflate();
}
private void showContent() {
if (errorView != null) {
errorView.setVisibility(View.GONE);
}
}
</pre>
--使用merge标签减少布局的嵌套层次;
(用来合并当前布局和父布局,有两种情况:)
布局根结点是FrameLayout且不需要设置background或padding等属性,可以用merge代替,因为Activity内容布局的parent view就是个FrameLayout,所以可以用merge消除只剩一个:
<pre>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="hello world" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center" >
<include layout="@layout/header" />
</RelativeLayout>
</merge>
</pre>
某布局作为子布局被其他布局include时,使用merge当作该布局的顶节点,这样在被引入时顶结点会自动被忽略,而将其子节点全部合并到主布局中:
<pre>
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_above="@id/text"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_alignParentBottom="true"
android:text="@string/app_name" />
</merge>
</pre>
5.Serializable和Parcelable
<pre>
package com.tutor.objecttran;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = -7060210544600464481L;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
</pre>
<pre>
package com.tutor.objecttran;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private String bookName;
private String author;
private int publishTime;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public int getPublishTime() {
return publishTime;
}
public void setPublishTime(int publishTime) {
this.publishTime = publishTime;
}
public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>() {
public Book createFromParcel(Parcel source) {
Book mBook = new Book();
mBook.bookName = source.readString();
mBook.author = source.readString();
mBook.publishTime = source.readInt();
return mBook;
}
public Book[] newArray(int size) {
return new Book[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(bookName);
parcel.writeString(author);
parcel.writeInt(publishTime);
}
}
</pre>
6.Handler和Message
过程:
创建Handler-->发送消息到MessageQueue-->Lopper.loop()取出消息并回调dispatchMessage()方法-->handleMessage处理消息。
(1)异步消息处理线程;
(2)在Handler创建的源码种得知:在任何一个线程中如果要new Handler(),必先调用Looper.prepare()为当前线程创建一个Looper对象,之后才能创建Handler对象,且每一个线程只能创建一个Looper对象;
(3)对于主线程:程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法,可以查看ActivityThread中的main()方法;
(4)其他可以在子线程中进行UI操作的方法:
--Handler的post()方法;
<pre>
public class MainActivity extends Activity {
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
//我们在Runnable对象的run()方法里更新UI,效果完全等同于在handleMessage()方法中更新UI
handler.post(new Runnable() {
@Override
public void run() {
// 在这里进行UI操作
}
});
}
}).start();
}
}
</pre>
--View的post()方法;
源码中实质是调用了Handler.post()方法;
<pre>
public boolean post(Runnable action) {
Handler handler;
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
ViewRoot.getRunQueue().post(action);
return true;
}
return handler.post(action);
}
</pre>
--Activity的runOnUiThred()方法;
直接看源码就一目了然了:
<pre>
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
</pre>
即:不管是使用哪种方法在子线程中更新UI,其实背后的原理都是相同的,必须都要借助异步消息处理的机制来实现。
</br>
7.AsyncTask
(1)AsyncTask的基本用法:
--AsyncTask是一个抽象类;</br>
--AsyncTask类指定三个泛型参数,这三个参数的用途如下:
1)Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用;
2)Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位;
3)Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型;</br>
--需要去重写的方法有以下四个:
1)onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等;
2)doInBackground(Params...)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成;
3)onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新;
4)onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
一个比较完整的自定义AsyncTask就可以写成如下方式:
<pre>
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressDialog.setMessage("当前下载进度:" + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
}
}
}
</pre>
(2)分析AsyncTask的源码
http://blog.csdn.net/guolin_blog/article/details/11711405