[置顶] android开发经验笔记总计

四大组件:

Android四大组件中除了BroadcastReceiver以外,其他三种组件都必须在AndroidManifest中注册。在调用方式上,除了ContentProvider都必须借助Intent。
http://www.cnblogs.com/pepcod/archive/2013/02/11/2937403.html

Activity

1.activity是用户和应用程序交互的窗口,一个activity相当于我们实际中的一个网页,当打开一个屏幕时,之前的那个屏幕会被置为暂停状态,并且压入历史堆栈中,用户可以通过回退操作回到以前打开过的屏幕。

2.在android中activity主要是用来做控制的,它可以选择要显示的view,也可以从view中获取数据然后把数据传给modele层进行处理,最后再来显示出处理结果。activity相当于MVC模式的controller控制层。


3.activity的启动模式
( 多次启动 Activity 会重复创建实例,为了解决这个,提供了启动模式的选择 )
四种启动模式:standardsingleTopsingleTasksingleInstance
1).standard:标准模式,系统默认的模式。
每次启动一个Activity都会创建一个实例,不管这个实例是否存在。一个任务栈中可以有多个实例,每个实例也可以输入不同的任务栈。谁启动了Activity,这个Activity就运行在启动他的任务栈中。当我们用ApplicationContext去启动standard模式的Acitivty的时候会报错,如下:
AndroidRuntime: android.util.AndroidRuntimeException: Calling startActivity from outside of 
an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag .Is this really what you  want?

这是因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的Context(ApplicationContext)没有所谓的任务栈,所以出现了这个问题。
解决方法就是为待启动Activity指定FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候就会创建一个新的任务栈,这个时候待启动的Activity实际是以singleTask模式启动的。
2).singleTop:栈顶复用模式。
如果新的 Activity已经位于栈顶,那么此 Activity就不会重新创建,同时它的 onNewIntent方法会被回调,通过此方法的参数我们可以取出当前请求的信息。需要注意的是,这个 ActivityonCreateonStart不会被系统调用。如果新的 Actvity的实例已经存在但不是位于栈顶,那么新的 Activity仍然会被重新创建。

3).singleTask:栈内复用模式。

这是一种单实例模式,只要Activity在一个栈中存在,那么多次启动Activity都不会重新创建实例,和singleTop一样,系统会回调其onNewIntent。当一个具有singleTask模式的Activity请求启动后,比如Activity A,系统首先会寻找是否存在A想要的任务栈,如果不存在,就重新创建一个任务栈,然后创建A的实例后把A放入栈中。如果存在A所需要的任务栈,这时就要看栈中的A的实例存在,如果实例存在,那么系统就会把A调到栈顶并调用它的onNewIntent方法,如果实例不存在,就创建A的事例并把A压入栈中。举例:

a.比如目前任务栈中S1的情况是ABC,这个时候Activity DsingleTask的,模式请求启动,其所需要的是任务栈S2,由于S2D的实例都不存在,所以系统会先创建任务栈S2,然后再创建D的实例并将其入栈S2.

b.另外一种情况,假设D所需要的任务栈为S1,那么由于S1已经存在了,所以系统会直接创建D的实例,并将其入栈S1.

c.如果D所需要的任务栈为S1,此时S1的情况为ADBC,根据栈内复用的原则,此时D不会创建,系统会把D切换到栈顶并调用其onNewIntent方法,同时由于singleTask默认具有clearTop的效果,会导致栈内所有在D上面的Activity全部出栈,于是最终S1的情况为AD

4).singleInstance:单实例模式。

    加强版的singleTask模式,除了具备singleTask的所有特性外,还加强了一点,具有此模式的Activity只能单独位于一个任务栈中。


4. 当前 Activity 只有执行完 onPause() ,才会执行新 Activity onResume()

5. Activity在屏幕旋转时的生命周期:不设置Acitivityandroid:configChanges时,切屏会重新调用各个生命周期,切横屏会调用一次,切竖屏会调用两次;设置android:configChanges=”orientation”时,切屏还是会重新调用各个生命周期,切横、竖屏时只会调用一次;设置android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。

6.activity之间的传递arraylist : http://blog.csdn.net/u012975370/article/details/50033553#t3

7.Activity之间传递数据的方法:
1).基于消息的通信机制:Intent。action是要去哪,category、data是带上什么。传递的数据类型有限,如遇到Bitmap、InputStream、或者是LinkList链表时就不好用了。
2).static静态数据,public static成员变量。其实gc机制并不可靠,如果经常使用static的bitmap、drawable等,就会很有可能抛出OOM异常。
3).存储机制:File、Sqlite、SharedPreference、ContentProvider等。sp本质是xml,方便使用;file常用语存储大量的数据,但是更新数据相对较难;
4).基于IPC的通信机制:Context与Service之间的传递,如Activity和Service之间的传递
5).Application Context:每个Activity都是context,同样,Application也是一个context,而且android会保证这个context是唯一的实例
public class MyApp extends Application
{
    private Bitmap mBitmap;

    public Bitmap getBitmap()
    {
        return mBitmap;
    }

    public void setBitmap(Bitmap bitmap)
    {
        this.mBitmap = bitmap;
    }
    
}
<pre name="code" class="java"><application android:name=".MyApp" android:icon="@drawable/icon" android:label="@string/app_name">
</application>
 获得Bitmap对象的代码: 
 
    ImageView imageview = (ImageView)findViewById(R.id.ivImageView);
    MyApp myApp = (MyApp)getApplication();
    imageview.setImageBitmap(myApp.getBitmap());
上面的代码可以在任何Service和Activity中使用,全局的。

8.Activity的Task相关: http://blog.csdn.net/liuhe688/article/details/6761337

9.startActivityForResult:
package com.example.result;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {

	public static final int REQUEST_CODE1 = 1;
	public static final int REQUEST_CODE2 = 2;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button button1 = (Button) findViewById(R.id.button1);
		button1.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Intent intent = new Intent(MainActivity.this,
						SecondActivity.class);
				startActivityForResult(intent, REQUEST_CODE1);
			}
		});

		Button button2 = (Button) findViewById(R.id.button2);
		button2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Intent intent = new Intent(MainActivity.this,
						ThridActivity.class);
				startActivityForResult(intent, REQUEST_CODE2);
			}
		});

	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (requestCode == REQUEST_CODE1) {
			Toast.makeText(getApplicationContext(),
					"111->" + data.getStringExtra("njj"), Toast.LENGTH_SHORT)
					.show();
		}
		if (requestCode == REQUEST_CODE2) {
			Toast.makeText(getApplicationContext(),
					"222->" + data.getStringExtra("njj"), Toast.LENGTH_SHORT)
					.show();
		}
	}
}
package com.example.result;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class SecondActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_second);

		Button button2 = (Button) findViewById(R.id.button2);
		button2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Intent intent = new Intent();
				intent.putExtra("njj", "niejianjian - second");
				setResult(RESULT_OK, intent);
				finish();
			}
		});

	}
}
package com.example.result;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class ThridActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_thrid);

		Button button3 = (Button) findViewById(R.id.button3);
		button3.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				Intent intent = new Intent();
				intent.putExtra("njj", "niejianjian - thrid");
				setResult(RESULT_OK, intent);
				finish();
			}
		});

	}
}



参考博客:

http://blog.csdn.net/liuhe688/article/details/6733407




BroadcastReceiver

1.Broadcast是广泛的应用在程序之间传输信息的机制。而BroadcastReceiver是对发送出来的Broadcast进行过滤接受并响应的一类组件。

2.应用程序可以拥有任意数量的广播接收者以对所以它感兴趣的通知信息予以响应。所有的接收器均继承自BroadcastReceiver基类。

3.广播接收器没有界面。然而,它可以启动一个activity来相应他们接受到的消息,或者用NotificationManager拉力通知用户。通知可以有很多方式来吸引用户的注意力--闪动背光、震动、播放声音等等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

4.到底什么时候用BroadcastReceiver:想要接收到某处的通知后,来进行一系列判断操作时。如用于接受系统的广播通知,系统会有很多sd卡挂在,手机重启,广播通知,低电量,来电,短信等。

5.注册方式,静态注册和动态注册:
 静态广播是常驻型的广播,即使应用程序关闭了,如果有消息广播来,程序也会被系统调用自动运行。
 动态广播不是常驻型的广播,也就是说广播跟随着程序的生命周期。
 1).静态注册
   写一个MyReceive类继承BroadcastReceive,并实现onReceive方法,在方法中进行相关的处理。
  此时还需要在AndroidManifest.xml中添加receive标签, 
        <receiver android:name="com.example.boradcasttest.MyReceive2" >
            <intent-filter android:priority="30" >
                <action android:name="njj.test.receive" />
            </intent-filter>
        </receiver>
然后只需要通过 sendBroadcast发送即可
                        Intent intent = new Intent();
			intent.setAction("njj.test.receive");
			sendBroadcast(intent);
 2).动态注册
		IntentFilter filter = new IntentFilter();
		filter.addAction("njj.test.receive"); // 添加actionyongyu匹配需要接受的广播
		registerReceiver(receiver, filter); // receiver是一个广播对象。
<span style="white-space:pre">	</span>BroadcastReceiver receiver = new BroadcastReceiver() {
<span style="white-space:pre">		</span>@Override
<span style="white-space:pre">		</span>public void onReceive(Context context, Intent intent) {
<span style="white-space:pre">		</span>}
<span style="white-space:pre">	</span>};
取消绑定只需要执行
unregisterReceiver(receiver);
一般在onStart中注册,在onStop中取消解绑。

6.动态注册的广播,永远要快于静态注册的广播,不管静态注册的优先级设置有多高,不管动态注册的优先级有多低。因为代码中注册的运行在内存中,xml在系统中。

7.BroadcastReceive会不会被杀死,如何保证不被杀死?
   广播接受者接收到广播时,会调用 onReceive()方法并传递给它包含消息的intent的对象。广播接受者会被认为紧当它执行这个方法时是活跃的。当onReceive()方法返回时,它是不活跃的。有一个活跃的广播接受者是被进程保护的,不会被杀死,但是系统可以在任何时候杀死仅包括不活跃组件的进程,只要占用的内存被别的进程所需要。
这样就有问题,当一个广播消息的响应是费时的,因此应该在独立的线程中做这件事,远离用户界面其他组件运行的主线程,如果onReceive一旦返回,整个进程就会认定为不活跃的,就有可能被杀死。
  解决的方法就是,在onReceive中开启一个服务,及时服务做这个工作,因此系统知道进程中有活跃的工作在做。

8.有序广播和无序广播
 1).无序广播
   通过 context.sendBroadcast() 发送,就是发送出去之后,所有的接受者的接受顺序不一定,这种方式效率更高,但是无法使用 setResult() getResult() 以及 abort 系列的 API 。索然所以的接受者都可以接收到,但是不能接受到广播,也不能把它的处理结果传递到下一个接受者。
 2).有序广播
   当 android 系统接受到短信时,会发送一个广播 BoradcastReceiver ,这个广播有序广播。
 通过 Context.sendOrderedBroadcast() 发送,所有的 receiver 依次执行,就是广播发出后,接受者按照设置好的优先级一个一个的接受,前面的接受者有权决定这条广播是终止还是发送给后面的接受者。 BroadcastReceiver可以使用setResult系列函数来结果传给下一个BroadcastReceiver,通过getResult系列函数来取得上个BroadcastReceiver返回的结果,并可以abort系列函数来让系统丢弃该广播,使用该广播不再传送到别的BroadcastReceiver。可以通过在intent-filter中设置android:priority属性来设置receiver的优先级,优先级相同的receiver其执行顺序不确定。如果BroadcastReceiver是代码中注册的话,且其intent-filter拥有相同android:priority属性的话,先注册的将先收到广播。
 有序广播,从优先级别最高的广播接收器开始接收,接收完了如果没有丢弃,就下传给下一个次高优先级别的广播接收器进行处理,依次类推,直到最后。 如果多个应用程序设置的优先级别相同,则谁先注册的广播,谁就可以优先接收到广播。优先级的数值可以设置为-1000到1000之间的任何一个数。
案例:
        <receiver android:name="com.example.boradcasttest.MyReceive1" >
            <intent-filter android:priority="20" >
                <action android:name="njj.test.receive" />
            </intent-filter>
        </receiver>
        <receiver android:name="com.example.boradcasttest.MyReceive2" >
            <intent-filter android:priority="0" >
                <action android:name="njj.test.receive" />
            </intent-filter>
        </receiver>
在一个按钮的点击事件中发送广播
				Intent intent = new Intent();
				intent.setAction("njj.test.receive");
				intent.putExtra("key", "发送给receive1的广播");
				sendOrderedBroadcast(intent, null);
public class MyReceive1 extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		System.out.println("onreceive");
		if (intent.getAction().equals("njj.test.receive")) {
			Toast.makeText(context, intent.getStringExtra("key"),
					Toast.LENGTH_SHORT).show();
			Bundle bundle = new Bundle();
			bundle.putString("re1", "有序广播");
			setResultExtras(bundle);
			// abortBroadcast(); // 终止传递
		}
	}
}
public class MyReceive2 extends BroadcastReceiver {
	@Override
	public void onReceive(Context context, Intent intent) {
		Bundle bundle = getResultExtras(true);
		String msg = bundle.getString("re1");
		String msg2 = intent.getStringExtra("key");
		System.out.println("msg -> " + msg);
		System.out.println("msg2 -> " + msg2);
	}
}
广播会先传递到优先级高的MyReceive1,然后在传递到MyReceive2,在MyReceive1中可以将广播终止传递,也可以传递给下一个接受者数据。

9.sendBroadcast和sendStickyBroadcast
 在MainActivity里面会有sendBroadcastsendStickyBroacat.ReceverActivity里面通过BroadcastReceiver来接收这两个消息,在ReceiverActivity里是通过代码来注册Recevier而不是在 Manifest里面注册的。所以通过sendBroadcast中发出的intentReceverActivity不处于onResume状态是无 法接受到的,即使后面再次使其处于该状态也无法接受到。而sendStickyBroadcast发出的IntentReceverActivity重 新处于onResume状态之后就能重新接受到其Intent.这就是the Intent will be held to be re-broadcast to future receivers这句话的表现。就是说sendStickyBroadcast发出的最后一个Intent会被保留,下次当Recevier处于活跃的 时候,又会接受到它。
sendStickyBroadcast() 意思只要是如果发送广播之后才执行registerReceiver(BroadcastReceiver,IntentFilter).这个方法依然可以接受到。换句话说,在ReceiverActivity里是通过代码来注册Recevier而不是在Manifest里面注册的。sendStickyBroadcast发出的最后一个Intent会被保留,当下次Recevier处于活跃的时候,又会接受到它。需要加BROADCAST_STICKY权限,否则会抛SecurityException。

10.android为什么要引入广播机制
1).从MVC的角度考虑(应用程序内)。其实这个问题也可以这样问,android为什么要有那四大组件,现在的移动开发模型基本也是照搬web的那一套MVC架构,只不过是改了点家长而已。android的四大组件本质上是为了实现移动或者说嵌入式设备上的MVC架构,他们之间有时候是一种相互依存的关系,有时候又是一种补充关系,引入广播机制可以方便几大组件的信息和数据的交互
2).程序间互通消息(例如在自己的应用程序内监听系统来电)
3).效率上(才考UDP的广播协议在局域网内的方便性)
4).设计模式上(反转控制的一种应用,类似监听者模式)

参考博客:http://blog.csdn.net/liuhe688/article/details/6955668


Service

1.什么时候使用Service
它主要用于后台处理一些耗时的逻辑,或者去执行某些需要长期运行的任务。必要的时候我们甚至可以在程序退出的情况下,让Service在后台继续保持运行状态。
当BroadcastReceive捕获到一个事件之后,可以开启一个service来完成相应的耗时操作。

2.service的start、bind、stop、onbind。
package com.example.boradcasttest;

import android.app.Activity;
import android.app.IntentService;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	MyConn myConn;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		myConn = new MyConn();

		Button button1 = (Button) findViewById(R.id.button1);
		button1.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				System.out.println("click ----> button1");
				Intent intent = new Intent(MainActivity.this, MyService1.class);
				startService(intent);
			}
		});

		Button button2 = (Button) findViewById(R.id.button2);
		button2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				System.out.println("click ----> button2");
				Intent intent = new Intent(MainActivity.this, MyService1.class);
				stopService(intent);
			}
		});

		Button button3 = (Button) findViewById(R.id.button3);
		button3.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				System.out.println("click ----> button3");
				Intent intent = new Intent(MainActivity.this, MyService1.class);
				bindService(intent, myConn, BIND_AUTO_CREATE);
			}
		});

		Button button4 = (Button) findViewById(R.id.button4);
		button4.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				System.out.println("click ----> button4");
				unbindService(myConn);
			}
		});

		Button button5 = (Button) findViewById(R.id.button5);
		button5.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				System.out.println("click ----> button5");
				Intent intent = new Intent();
				intent.setAction("stop.service1");
				sendBroadcast(intent);
			}
		});

	}

	private class MyConn implements ServiceConnection {

		public void onServiceConnected(ComponentName name, IBinder service) {
			// 可以通过IBinder的对象 去使用service里面的方法
		}

		public void onServiceDisconnected(ComponentName name) {
		}
	}

}
package com.example.boradcasttest;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.IBinder;

public class MyService1 extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		System.out.println("MyService1 -> onBind");
		return null;
	}

	@Override
	public void onRebind(Intent intent) {
		System.out.println("MyService1 -> onRebind");
		super.onRebind(intent);
	}

	@Override
	public boolean onUnbind(Intent intent) {
		System.out.println("MyService1 -> onUnbind");
		return super.onUnbind(intent);
	}

	@Override
	public void onCreate() {
		System.out.println("MyService1 -> onCreate");
		super.onCreate();
	}

	@Override
	@Deprecated
	public void onStart(Intent intent, int startId) {
		System.out.println("MyService1 -> onStart");
		IntentFilter filter = new IntentFilter();
		filter.addAction("stop.service1");
		registerReceiver(receiver, filter);
		super.onStart(intent, startId);
	}

	@Override
	public boolean bindService(Intent service, ServiceConnection conn, int flags) {
		System.out.println("MyService1 -> bindService");
		return super.bindService(service, conn, flags);
	}

	@Override
	public void unbindService(ServiceConnection conn) {
		System.out.println("MyService1 -> unbindService");
		super.unbindService(conn);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		System.out.println("MyService1 -> onStartCommand");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public boolean stopService(Intent name) {
		System.out.println("MyService1 -> stopService");
		stopSelf(); // 停止服务
		return super.stopService(name);
	}

	@Override
	public void onDestroy() {
		System.out.println("MyService1 -> onDestroy");
		super.onDestroy();
	}

	BroadcastReceiver receiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			System.out.println("MyService1 -> receiver");
			if (intent.getAction().equals("stop.service1")) {
				context.stopService(intent);
			}
		}
	};

}
        <service android:name="MyService1" ></service>

3.Service的特点:
没有用户界面;比Activity的优先级高,不会轻易被android系统停止;即使Service被停止,在系统资源恢复后,service也能自动恢复运行状态;
可用于IPC,解决两个不同的android应用程序之间的调用和通信问题。

4.默认情况,如果没有显式的指定service所运行的进程,service和activity是运行在当前app所在进程的main thread(UI 主线程)里面。
service里面不能执行耗时的操作(网络请求,拷贝数据库、大文件),在子线程中new Thread(){}.start();
(anddroid的几大组件都是在main thread中执行的,所以不能执行耗时操作,新版的sdk会直接抛出异常)
特殊情况下,可以在清单文件中配置service所在的进程,让service在另外的进程中执行。

5.拥有Service的进程具有较高的优先级
官方文档告诉我们,Android系统会尽量保持拥有Service的进程运行,只要在该service已经被启动(start)或者客户端连接(bindservice)到它,当内存不足时,需要保持,拥有service的进程具有较高的优先级。
1).如果service正在调用oncreate,onstartcommand或者onSestory方法,那么用于当前的service进程相当于前台进程以避免被killed。
2).如果service已经被启动,拥有它的进程比那些用户可见的进程优先级低一些,但是比那些不可见得进程更重要,这意味着service一般不会被killed。
3).如果客户端已经连接到service,那么拥有service的进程则拥有较高的优先级,那么认为service是可见的
4).如果service可以使用startForeground(int,Notification)方法来将service设置为前台状态,那么系统会认为是对用户可见的,并不会在内存不足时killed。如果其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。

6.如何让service不被杀死?
Android开发过程中,每次调用startService,都会调用该service对象的onStartCommand(Intent,int,int)方法,然后在onStartCommand方法中做一些处理。
从Android的开发文档中,我们知道onStartCommand有4中int返回值,简单讲讲int返回值的作用:
a.START_STICKY:如果service进程被kill掉,保留service的状态为开始状态, 但不保留递送的intent对象,随后系统会尝试重新创建service,由于服务状态为开始状态,所 以创建服务后一定要调用onStartCommand(Intent,int,int)方法,如果在此期间没有任何启动命令被传递到service,那么参数intent将为null。
b.START_NOT_STICKY:非粘性的,使用这个返回值,如果在执行完onStartCommand之后,服务被异常kill掉,系统不会自动重启服务。
c.START_REDELIVER_INTENT:重传inent,使用这个返回值时,如果执行玩onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将intent的值传入。
d.START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务kill掉后一定能重启。
其实一般服务不被杀死,分三种情况讨论:
a.系统根据资源分配情况杀死服务
b.用户通过settings->apps->running->stop方式杀死服务
c.用户通过settings->apps->downloaded-Force stop方式杀死服务

1).让service杀不死,在service的onStartCommand中返回START_STICKY,同时在onDestory中调用startService启动自身。
2).让service从后台变为前置,在Android2.0以前有效,接住startForeground(true)方法。
3).提升service的优先级。在AndroidMainfest.xml中,设置service的android:priority的值为最大1000.尽量拥有高优先级,避免service被回收。(实质只是降低了概率)
4).当应用升级之后,即使用户不点开app,也要重启服务。因为升级app会发送ACTION_PACKAGE_RESTARTED,AlarmManager作为系统服务,会接受这个事件,然后startService。
5).创建守护进程。

7.启动前台服务
    前台服务是被认为是用户已知的运行的服务,当系统需要释放内存时不会被优先杀掉的进程。前天进程必须发一个notification在状态栏中显示,知道进程被杀死。因为前台服务会一直消耗一部分资源,但不像一般服务那样会在需要的时候被杀掉,所以为了节约资源,保持电池的寿命,一定要在建前台服务的时候发notification,提示用户。当然,系统提供的方法就是必须有notification参数的,所以不要想着怎么办notification隐藏。
startForeground方法就是将服务设置为前台服务,参数123456就是这个通知唯一的id,只要不为0就可以了。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
	// TODO Auto-generated method stub
	Intent notificationIntent = new Intent(this, MainActivity.class);
	PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
	Notification noti = new Notification.Builder(this)
				.setContentTitle("Title")
				.setContentText("Message")
				.setSmallIcon(R.drawable.ic_launcher)
				.setContentIntent(pendingIntent)
				.build();
	startForeground(12346, noti);
	return Service.START_STICKY;
}

8.service和intentservice的比较
   这里主要说的是IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread来处理队列中的Intent,对于异步的startservice请求,IntentService会处理完成一个之后才会处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程,这里就给我们提供一个思路,如果有耗时的操作与其在Service里面开启新的线程,不如使用IntnetService来处理耗时操作。
public class MyService extends Service {  
  
    @Override  
    public void onCreate() {  
        super.onCreate();  
    }  
      
    @Override  
    public void onStart(Intent intent, int startId) {  
        super.onStart(intent, startId);  
        //经测试,Service里面是不能进行耗时的操作的,必须要手动开启一个工作线程来处理耗时操作   
        System.out.println("onStart");  
        try {  
            Thread.sleep(20000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("睡眠结束");  
    }  
      
    @Override  
    public IBinder onBind(Intent intent) {  
        return null;  
    }  
}  
public class MyIntentService extends IntentService {  
  
    public MyIntentService() {  
        super("yyyyyyyyyyy");  
    }  
  
    @Override  
    protected void onHandleIntent(Intent intent) {  
        // 经测试,IntentService里面是可以进行耗时的操作的   
        //IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent   
        //对于异步的startService请求,IntentService会处理完成一个之后再处理第二个   
        System.out.println("onStart");  
        try {  
            Thread.sleep(20000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("睡眠结束");  
    }  
}  
public class ServiceDemoActivity extends Activity {  
    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        startService(new Intent(this,MyService.class));//主界面阻塞,最终会出现Application not responding   
        //连续两次启动IntentService,会发现应用程序不会阻塞,而且最重的是第二次的请求会再第一个请求结束之后运行(这个证实了IntentService采用单独的线程每次只从队列中拿出一个请求进行处理)   
        startService(new Intent(this,MyIntentService.class));  
        startService(new Intent(this,MyIntentService.class));  
    }  
}  
IntentServicede 的好处:
*Activity的进程,当处理Intent的时候,会产生一个对应的Service。
*Android的进程处理器现在会尽可能的不kill掉你
*非常容易使用

9.intentservice的处理流程
创建默认的一个worker线程处理传递给onStartCommand()的所有intent,不占据应用的主线程;创建一个工作队列一次传递一个intent到你实现的onHandleIntent()方法,避免了多线程;在所有启动请求被处理后自动关闭服务,不需要调用stopSelf();默认提供onBind()的实现,并返回null;默认提供onStartCommand()的实现,实现发送intent到工作队列再到你的onHandleIntent()方法实现。这些都加入到intentservice中了,你需要做的就是实现构造方法和onHandleIntent()。
public class HelloIntentService extends IntentService {

  /**
   * A constructor is required, and must call the super IntentService(String)
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}
如果需要重写其他回调方法,如onCreate,onStartCommand()等,一定要调用super()方法,保证intentservice正确处理worker线程,只有onHandleIntent()和onBind()不需要这样,如:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent,flags,startId);
}

10.Android中程序与Service的交互方式 :  http://blog.csdn.net/gebitan505/article/details/18151203

Service对象不能自己启动,需要通过某个Activity、Service或者其他Context对象来启动,启动的方法有两种,Context.startService()和Context.bindService()。两种方式的生命周期是不同的。

context.startService:

如果service还没有运行,则android先调用onCreate()方法,然后调用onStart();如果service已经运行,则至调用onStart(),所以一个service的onStart()方法可能会重复调用多次。stopService的时候会直接调用onDestory(),如果是调用者自己直接退出而没有调用stopService的话,service会一直在后台运行,该service的调用者再次启动起来后可以通过stopService关闭service

1.在同一个应用任何地方调用startService()方法就能启动Service了,然后系统会回调service类的onCreate()以及onStart()方法,这样启动的service会一直运行在后台,知道Context.stopService()或者selfStop()方法被调用。

2.另外一种bindService()方法的意思是,把这个Service和调用service的客户类绑起来,如果调用这个客户类被销毁,service也会被销毁,用这个方法的一个好处是,bindService()方法执行后service会回调上边提到的onBind()方法,你可以从这里返回一个实现了IBind接口的类,在额客户端操作这个类就能和这个服务通信了, 比如得到service运行的状态或其他操作,如果service还没有运行,使用这个方法启动service就会onCreate()方法而不会调用onStart().

3.startService()的目的是回调onStart()方法,onCreate()方法是在Service不存在的时候调用的,如果Service存在(例如之前调用了bindService,那么Service的onCreate方法已经被调用了)那么startService()将跳过onCreate()方法。

4.bindService()目的是回调onBind()方法,它的作用是在Service和调用者之间建立一个桥梁,并不负责更多的工作(例如一个service需要连接服务器的操作),一般使用bindService来帮顶到一个现有的Service(即通过StartService启动的服务)

由于Service的onStart()方法只有在startService()启动service的情况下才会调用,故使用onStart()的时候要注意这点。

5.与Service通信并且让它持续运行:如果我们想要保持和Service的通信,又不想让Service随着Activity退出而退出。你可以先startService()然后在bindService()。当你不需要帮顶的时候就执行unbindService()方法,执行这个方法只会触发Service的onUnbind()方法而不会把这个Service销毁,这样就可以既保持和service的通信,也不会随着activity销毁而销毁。

6.为什么是foregorund:默认启动的Service是被标记为background,当前运行的Activity一般被标记为foreground,也就是说你给service设置了foreground那么他就和正在运行的Activity类似优先级得到了一定的提高。当让这并不能保证你service永远不被杀掉。只是提高了他的优先级

7.如果先是bind了,那么start的时候就直接运行service的onStart方法,如果是先start,那么bind的时候就直接运行onBind方法,如果你先bind上了,就stop不掉了,只能先执行UnbindService,再stopService,所以是先start还是先bind行为是有区别的。

8.如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()。如果调用startService()方法前服务已经被创建,多次调用startService()方法,并不会导致多次创建服务,但会导致多次调用onStart()方法,采用startService()方法启动的服务只能调用Context.stopService()方法结束服务,服务结束时会被调用onDestory()。

9.如果打算采用Context.bindService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onBind()方法,这个时候调用者和服务绑定在一起,调用者退出,系统就会先调用服务的onUnbind()方法,接着调用onDestory()方法,如果调用bindService()方法前服务已经被绑定,多次调用bindService()并不会导致多次创建服务及绑定(也就是说onCreate和onBind方法并不会多次调用)。如果调用者希望与正在绑定的服务解除绑定,可以调用unbindService()方法。

10.Service和Thread的区别:

Thread是程序执行的最小单位,他是分配CPUde基本但那位,可以用Thread来执行一些异步的操作。

Service是android的一种机制,当它运行的时候,如果是Local Service,那么对应的Service是运行在主进程的main线程上的。如onCreate,onStart这些函数在被系统调用的时候都是在主进程的main线程上运行的。如果Remote Service,那么对应的Service则是运行在独立进程的main线程上,因此不要把Service理解成线程,它和线程半毛钱关系都没有。

那么我们为什么要用Service?   Thread的运行是独立于Activity的,也就是说当一个Activity被finish之后,如果你没有主动停止Thread或者Thread中的run方法没有执行完毕,Thread也会一直执行。因此这里会出现一个问题,当Activity被finish掉之后,你不在持有该Thread的引用,另一方面,你没有办法在不同的Activity中对同一个Thread进行控制。

举个例子:如果你的Thread需要不停的隔一段时间就要连接服务器做某种同步的话,该Thread需要在Activity没有start的时候就运行。这个时候当你start一个Activity就没有办法在该Activity里面控制之前创建的Thread。因此你需要创建并启动一个Service。在Service里面创建、运行并控制该Thread,这样便解决了该问题(因为任何Activity都可以控制同一个Service,而系统也只会创建一个对应的Service实例)

因此你可以把Service想象成一种消息服务,而你可以在任何有Context的地方调用StartService、Context.stopService、Context.bindService、Context.unbindService,来控制它,你也可以在Service中注册BroadcastReceiver,在其他地方通过发送broadcast来控制它,当然这些都是Thread做不到的。


Service中可以正常显示ToastIntentService中不能正常显示Toast,在2.3系统上,不显示toast,在4.3系统上,toast显示,但是不会消失。
2. 原因
Toast要求运行在UI主线程中。
Service运行在主线程中,因此Toast是正常的。
IntentService运行在独立的线程中,因此Toast不正常。
3. IntentService中显示Toast
利用Handler,将显示Toast的工作,放在主线程中来做。具体有两个实现方式。
Handlerpost方式实现,这个方式比较简单。

private void showToastByRunnable(final IntentService context, final CharSequence text, final int duration) {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(context, text, duration).show();
}
});
}
Handler的msg方式实现,这个方式比较复杂。
Handler msgHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
Toast.makeText(ToastIntentService.this, msg.getData().getString("Text"), Toast.LENGTH_SHORT).show();
super.handleMessage(msg);
}
};
private void showToastByMsg(final IntentService context, final CharSequence text, final int duration) {
Bundle data = new Bundle();
data.putString("Text", text.toString());
Message msg = new Message();
msg.setData(data);
msgHandler.sendMessage(msg);
}

4. 关于耗时操作
Service中如果有耗时的操作,要开启一个Thread来做。
IntentService是在独立的线程中,所以可以进行一些耗时操作。
5. 考虑AsyncTaskService的使用区别
如果是全后台的工作,使用Service,结果的提示可以使用Notification
如果是异步工作,工作结束后需要更新UI,那么最好使用Thread或者AsyncTask

Service弹出dialog   : http://blog.csdn.net/djun100/article/details/23756681







参考博客: http://www.myexception.cn/android/1925562.html





ContentProvider

1.Android有一个独到之处,就是数据库只能被它的创建者所使用,其他的应用是不能访问到的,所以如果你想实现不同应用之间的数据共享,就不得不用contentprovider了。在Android中,contentprovider是一个特殊的存储数据的类型,他提供了一套标准的接口用来获取以及操作数据,并且,android自身也提供了几个现成的contentprovider:Contacts,Browser,CallLog,Settings,MediaStore。



Intent

android应用的三大组件——Activitied、Services、BroadcastReceiver,通过消息触发,这个消息就称作意图(Intent)。


2.Intent可以传递的数据类型:从原码查看:

1).基本数据类型

Intent putExtra(String name, int[] value)  
Intent putExtra(String name, float value)  
Intent putExtra(String name, byte[] value)  
Intent putExtra(String name, long[] value)  
Intent putExtra(String name, float[] value)  
Intent putExtra(String name, long value)  
Intent putExtra(String name, String[] value)  
Intent putExtra(String name, boolean value)  
Intent putExtra(String name, boolean[] value)  
Intent putExtra(String name, short value)  
Intent putExtra(String name, double value)  
Intent putExtra(String name, short[] value)  
Intent putExtra(String name, String value)  
Intent putExtra(String name, byte value)  
Intent putExtra(String name, char[] value)  
Intent putExtra(String name, CharSequence[] value) 

    public Intent putExtra(String name, CharSequence value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putCharSequence(name, value);
        return this;
    }

2).传递Bundle

    public Intent putExtra(String name, Bundle value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putBundle(name, value);
        return this;
    }

3).传递Parcelable对象

    public Intent putExtra(String name, Parcelable value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putParcelable(name, value);
        return this;
    }

4).传递Serializable对象

    public Intent putExtra(String name, Serializable value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putSerializable(name, value);
        return this;
    }

5).传递Intent对象

    public Intent putExtras(Intent src) {
        if (src.mExtras != null) {
            if (mExtras == null) {
                mExtras = new Bundle(src.mExtras);
            } else {
                mExtras.putAll(src.mExtras);
            }
        }
        return this;
    }
6).传递IBinder对象
    @Deprecated
    public Intent putExtra(String name, IBinder value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putIBinder(name, value);
        return this;
    }
归根结底都是通过new Bundle()来实现数据封装,而Bundle则是通过map来存储数据的
    public Bundle() {
        mMap = new ArrayMap<String, Object>();
        mClassLoader = getClass().getClassLoader();
    }

3.PendingIntent
从字面意思上来看,是一个延时Intent。

A description of an Intent and target action to perform with it. Instances of this class are created with getActivity(Context, int, Intent, int), getActivities(Context, int, Intent[], int), getBroadcast(Context, int, Intent, int), and getService(Context, int, Intent, int); the returned object can be handed to other applications so that they can perform the action you described on your behalf at a later time.
获得一个PendingIntent实例,通过getActivity、getBroadcast、getService、getActivities等方法。要得到实例,参数必须必须传入的是Intent和context。

PendingIntent是一种特殊的Intent。主要区别在于Intent的执行是立刻的,而PendingIntent则不是。PendingIntent执行的操作实质上是参数Intent传递进来的操作,正由于PendingIntent中保存有当前app的context,使它赋予了外部app的能力,使得外部app可以如同当前app一样的执行pengdingIntent里面的intent,就算在执行时当前app已经不存在了,也能同pendingintent里面的context来执行。另外还可以处理intent执行后的操作。主要用在Notification、SmsManager和AlarmManager等。

PendingIntent和Intent的区别:(1).Intent是立即使用的,而PendingIntent可以的呢感到时间发生后触发,PendingIntent可以cancel 。(2).Intent在程序结束后即终止,而PendingIntent在程序结束后仍然有效 。(3).PendingIntent自带context,而Intent需要在某个Context中运行 。(4).Intent在原task中执行,PendingIntent在新的task中执行。

PendingIntnet的匹配规则:如果两个PendingIntent它们内部的Intent相同并且requesstCode也相同,那么这两个PendingIntent就是相同的。

Intent的匹配规则:如果两个IntentcomponentNameintent-filter都相同,那么这两个Intent就是相同的,即使它们的Extras不同。


参考博客:http://blog.csdn.net/zeng622peng/article/details/6180190             http://blog.csdn.net/yuzhiboyi/article/details/8484771

                    http://lovelydog.iteye.com/blog/1666340


4.IntentFilter

顾名思义,IntentFilter对象负责过滤掉无法响应和处理的intent,只将自己关心的intent接受进来进行处理,intentFilter实行白名单管理,但IntentFilter只会过滤掉隐式的intent,显式的intent会直接传送到目标组件。android组件可以有一个或者多个intentfilter,每个之间都互相独立,只需要其中一个验证通过即可。除了用于过滤广播的intentfilter可以在代码中创建外,其他的intentfilter都必须在Androidmanifest.xml文件中进行声明。

http://blog.csdn.net/today520/article/details/7000048


Action是要求Intent中必须有一个action且必须能够和过来规则中的某个action相同,而category要求Intent可以没有category,但是如果你一旦有category,不管有几个,每个都要能够和过来规则中的任何一个category相同。

为什么不设置category也能匹配?原因是系统在调用startActivitystartActivityForResult的时候会默认为Intent加上“android.intent.category.DEFAULT”这个category。所以category就可以匹配前面的过滤规则中的第三个category。同时,为了我们的activity能够接受到隐式调用,就必须在intent-filter中指定“android.intent.category.DEFAULT”这个category


参考博客:http://blog.csdn.net/annkie/article/details/8483253


Hanlder

主线程初始化的时候,默认创建了looperHandler在使用之前必须有looper的存在,否则会报错。如果在非主线成使用handler,必须创建looper

ActivityThread中的main()方法中,系统已经自动调用了Looper.prepare()

不是的,有以下两种:
一,如果不带参数的实例化:Handler handler = new Handler();那么这个会默认用当前线程的looper
二,一般而言如果Handler是要来刷新操作UI的,那么就需要在主线程下跑。那么在主线程 Handler handler = new Handler()
三,如果在其他线程,也要满足这个功能的话,要Handler handler = new Handler(Looper.getMainLooper());不用刷新ui,只是处理消息。 
四,当前线程如果是主线程的话,Handler handler = new Handler();不是主线程的话,Looper.prepare(); Handler handler = new Handler();Looper.loop();或者Handler handler = new Handler(Looper.getMainLooper())

消息循环和消息队列都是属于THread,而Handler本身并不具有LooperMessageQueue;但是消息系统的建立和交互,是ThreadLooperMessageQueue交给某个Handler维护建立消息模型,所以消息系统模型的核心就是Looper。消息循环和消息队列都是由Looper建立的,而建立Handler的关键就是这个Looper

一个Thread同时可以对应多个Handler,一个Handler同时只能属于一个ThreadHandler属于那个Thread取决于Handler在哪个Thread中建立。在一个ThreadLooper也是唯一的,一个Thread对应一个Looper,建立HandlerLooper来自哪个ThreadHandler就属于哪个Thread。故建立Thread消息系统,就是将ThreadLooper交给Handler去打理,实现消息系统模型,完成消息的异步处理。

在线程中建立Handler

1).获得ThreadLooper交给Handler的成员变量引用维护;

2).通过Looper获取MessageQueue交个Hanler的成员变量引用维护。

消息发送和派发接口:

1).post(Runnable)消息,Runnable是消息回调,经过消息循环引发消息回调函数执行;

2).sendMessage(Message)消息,经过消息循环派发消息处理函数中处理消息;

3).diapatchMessage派发消息,若是post或带有回调函数则执行回调函数,否则执行消息处理函数HandlerhandleMessage(通常派生类重写)。

简单的几个方法使用:

package com.example.handlertestdemo;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

	public int a = 0;

	Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case 0:
				a++;
				System.out.println("a = " + a);
				sendEmptyMessageDelayed(0, 1000);
				if (a == 5) {
					if (hasMessages(0)) {
						removeMessages(0); // 终止消息的执行
					}
				}
				break;
			case 1:
				System.out.println("msg.what = 1 ---> msg0 is exits = "
						+ hasMessages(0));
				sendEmptyMessageDelayed(1, 7000);
				removeCallbacks(runnable); // 终止线程的执行
				break;
			default:
				break;
			}
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button button1 = (Button) findViewById(R.id.button1);
		button1.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				handler.sendEmptyMessageDelayed(0, 3000);
				handler.sendEmptyMessage(1);
			}
		});
	}

	Runnable runnable = new Runnable() {

		@Override
		public void run() {
			// 执行内容
		}
	};

}
简单的说,Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message,再有Handler进行Message的分发和处理.
Message Queue(消息队列):用来存放通过Handler发布的消息,通常附属于某一个创建它的线程,可以通过Looper.myQueue()得到当前线程的消息队列.
Handler:可以发布或者处理一个消息或者操作一个Runnable,通过Handler发布消息,消息将只会发送到与它关联的消息队列,然也只能处理该消息队列中的消息.
Looper:是Handler和消息队列之间通讯桥梁,程序组件首先通过Handler把消息传递给Looper,Looper把消息放入队列。Looper也把消息队列里的消息广播给所有的Handler:Handler接受到消息后调用handleMessage进行处理.
Message:消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理,在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列),线程间可以通过该Message Queue并结合Handler和Looper组件进行信息交换。下面将对它们进行分别介绍:
1. Message
Message消息,理解为线程间交流的信息,处理数据后台线程需要更新UI,则发送Message内含一些数据给UI线程。
2. Handler
Handler处理者,是Message的主要处理者,负责Message的发送,Message内容的执行处理。后台线程就是通过传进来的 Handler对象引用来sendMessage(Message)。而使用Handler,需要implement 该类的 handleMessage(Message)方法,它是处理这些Message的操作内容,例如Update UI。通常需要子类化Handler来实现handleMessage方法。
3. Message Queue
Message Queue消息队列,用来存放通过Handler发布的消息,按照先进先出执行。
每个message queue都会有一个对应的Handler。Handler会向message queue通过两种方法发送消息:sendMessage或post。这两种消息都会插在message queue队尾并按先进先出执行。但通过这两种方法发送的消息执行的方式略有不同:通过sendMessage发送的是一个message对象,会被 Handler的handleMessage()函数处理;而通过post方法发送的是一个runnable对象,则会自己执行。
4. Looper
Looper是每条线程里的Message Queue的管家。Android没有Global的Message Queue,而Android会自动替主线程(UI线程)建立Message Queue,但在子线程里并没有建立Message Queue。所以调用Looper.getMainLooper()得到的主线程的Looper不为NULL,但调用Looper.myLooper() 得到当前线程的Looper就有可能为NULL。对于子线程使用Looper,API Doc提供了正确的使用方法:这个Message机制的大概流程:
1. 在Looper.loop()方法运行开始后,循环地按照接收顺序取出Message Queue里面的非NULL的Message。
2. 一开始Message Queue里面的Message都是NULL的。当Handler.sendMessage(Message)到Message Queue,该函数里面设置了那个Message对象的target属性是当前的Handler对象。随后Looper取出了那个Message,则调用该Message的target指向的Hander的dispatchMessage函数对Message进行处理。在dispatchMessage方法里,如何处理Message则由用户指定,三个判断,优先级从高到低:
1) Message里面的Callback,一个实现了Runnable接口的对象,其中run函数做处理工作;
2) Handler里面的mCallback指向的一个实现了Callback接口的对象,由其handleMessage进行处理;
3) 处理消息Handler对象对应的类继承并实现了其中handleMessage函数,通过这个实现的handleMessage函数处理消息。
由此可见,我们实现的handleMessage方法是优先级最低的!
3. Handler处理完该Message (update UI) 后,Looper则设置该Message为NULL,以便回收!
在网上有很多文章讲述主线程和其他子线程如何交互,传送信息,最终谁来执行处理信息之类的,个人理解是最简单的方法——判断Handler对象里面的Looper对象是属于哪条线程的,则由该线程来执行!
1. 当Handler对象的构造函数的参数为空,则为当前所在线程的Looper;
2. Looper.getMainLooper()得到的是主线程的Looper对象,Looper.myLooper()得到的是当前线程的Looper对象。

为什么要使用Handler

有时候需要在子线程中进行耗时的I/O操作,可能是读取文件或者访问网络等,当耗时操作完成以后可能需要在UI上做一些改变,由于Adnroid开发规范的限制,我们并不能在子线程中访问UI控件,否则会出发程序异常,这个时候通过Handler就可以将更新UI的操作切换到主线程中执行。因此,本质上来说,Handler并不是专门用于更新UI的,只是被常常用来更新UI

MessageQueue虽然叫做消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表的。

MessageQueue只是一个消息的存储单元,它并不能去处理消息,Looper会以无限循环的形式去查询是否有新消息,如果有的话就处理消息,否则一直等待。

线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper。我们经常提到的主线程,也叫做UI线程,它就是ActivityThreadActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Looper的原因。

系统之所以提供Handler,主要原因就是为了解决在子线程中无法访问UI的矛盾。

系统为什么不允许在子线程中访问UI呢?

因为AndoridUI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。

为什么不对UI控件的访问加上锁机制?

缺点有两个:首先加上锁机制会让UI访问逻辑变得复杂;其次锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。所以最高效的方法就是采用单线程模型来处理UI操作。

Handler创建完毕,这个时候其内部的Looper以及MessageQueue就可以和Handler一起工作了,然后通过Handlerpost方法将一个Runable投递到Handler内部的Looper中去处理,也可以通过Handlersend方法来发送一个消息,这个消息同样会在Looper中去处理。其实post方法最终也是通过send方法处理的。

Send方法的工作过程:

Handlersend方法被调用是,它会调用MessageQueueenqueueMessage方法将这个消息放入消息队列中,然后Looper发现有新的消息到来是,就会出来这个消息,最终消息中的Runnable或者HandlerhandleMessage方法会被调用。

Thread是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据。

当某些数据以线程为作用域并且不同线程具有不同的数据副本的时候,就可以考虑采用ThreadLocal

对于Handler来说,它需要获取当前线程的Looper,很显然Looper的作用域就是线程并且不同线程具有不同的Looper


线程是一次性消费品,如果多次创建和销毁线程是很耗费资源的,所以就构建了一个循环线程Looper Thread,当有耗时任务投放到该循环线程中的时候,线程执行耗时任务,当执行完毕了,循环线程处于等待状态,等待下一个耗时任务被投放进来。

1).HandlerThread适用于构建循环线程

2).在创建Handler作为HandlerThread线程消息执行者的时候必须调用start方法之后,因为创建Handler需要的Looper参数是从HandlerThread类中获得的,而Looper对象的赋值又是在HandlerThreadrun方法中创建。


1.Cancelling a Task
我们可以在任何时刻来取消我们的异步任务的执行,通过调用 cancel(boolean)方法,调用完这个方法后系统会随后调用 isCancelled() 方法并且返回true。如果调用了这个方法,那么在 doInBackgroud() 方法执行完之后,就不会调用 onPostExecute() 方法了,取而代之的是调用 onCancelled() 方法。为了确保Task已经被取消了,我们需要经常调用 isCancelled() 方法来判断,如果有必要的话。
2.在使用AsyncTask做异步任务的时候必须要遵循的原则:
AsyncTask类必须在UI Thread当中加载,在Android Jelly_Bean版本后这些都是自动完成的
AsyncTask的对象必须在UI Thread当中实例化
execute方法必须在UI Thread当中调用
不要手动的去调用AsyncTaskonPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute方法,这些都是由Android系统自动调用的
AsyncTask任务只能被执行一次


参考博客:

http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html

http://www.androidchina.net/313.html



ListView

1.ListView的工作原理:  
Adapter的作用就是ListView界面与数据之间的桥梁,当列表里的每一项显示到页面时,都会调用Adapter的getView()方法返回一个view。这样,ListView中有多少项,就应该调用多少才gerView()方法去绘制每一项的界面。如果项数较少,这没有问题,但是如果10万或百万项,那么就会极大的占用系统内存,所以必须采用性能优化方法。
ListView在工作时,会针对每个Item,要求Adapter对象"返回一个View(getView()方法)",也就是说,ListView在开始绘制的时候,系统首先调用getCount(),根据其返回值得到ListView的长度,然后根据这个长度,调用getView()一行一行的绘制ListView的每一项。如果getCount()返回值是0的话,列表一行都不会显示,如果返回1,就只显示一行,返回几就显示几行。

2.ListView的缓存机制:
     (1) 如果有几千几万甚至更多的项时,其中只有可见的项存于内存中,其余的都在Recycler中,Recycler是 Android中专门用来处理缓存的组件。
     (2) ListView先通过getView()方法请求一个View,然后请求其他可见的View。convertView在getView中是空的。
     (3) 当列表第一项滚出屏幕,并且一个新的项从屏幕低端上来时,LIstView会在请求一个View,这是convertView已经不是空值了,它的值是滚出屏幕的第一项,之后只需设定新的数据,然后返回convertView就行,而不必重新创建一个View。
     通过缓存机制,大大地减少了创建新的VIew的次数,从而提升了LIstVIew的性能。   
public View getView( int position, View convertView, ViewGroup parent) {
           View view = convertView;
           ViewHolder holder;

            if (view == null) {
                LayoutInflater inflater = (LayoutInflater) mContext
                          .getSystemService(Context. LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout. list_item, null);

                holder = new ViewH older();
                 // 每项的视图布局是一样的
                holder. image = (ImageView) view.findViewById(R.id.image );
                holder. tv_title = (TextView) view.findViewById(R.id.tv_title );
                holder. tv_info = (TextView) view.findViewById(R.id.tv_info );
                view.setTag(holder);
           } else {
                holder = (ViewHolder) view.getTag();
           }

            // 每项的数据都不一样
          holder. image.setImageDrawable( dataList.get(position). image);
          holder. tv_title.setText( dataList.get(position). title);
           holder. tv_info.setText( dataList.get(position). info);

            return view;
     }

     private static class ViewHolder {
            public ImageView image;
            public TextView tv_title;
            public TextView tv_info;
     }
首先我们给出一个没有任何优化的 Listview Adapter 类,我们这里都继承自 BaseAdapter ,这里我们使用一个包含 100 个字符串的 List 集合来作为 ListView 的项目所要显示的内容,每一个条目都是一个自定义的组件,这个组件中只包含一个 textview

package com.alexchen.listviewoptimize;
 
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
 
public class MainActivity extends Activity {
 
      private ListView lv_demo;
      private List<String> list;
 
      @Override
      protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
             setContentView(R.layout.activity_main);
             lv_demo = (ListView) findViewById(R.id.lv_demo);
             //list为要加载的条目文本的集合,这里总共是100条
             list = new ArrayList<String>();
             for (int i = 0; i < 100; i++) {
                   list.add("条目" + i);
             }
 
             lv_demo.setAdapter(new MyAdapter());
       }
 
       private class MyAdapter extends BaseAdapter {
 
             @Override
             public int getCount() {
                   return list.size();
             }
 
             @Override
             public View getView(int position, View convertView, ViewGroup parent) {
                  //listview_item里只有一个textview
                  View view = View.inflate(MainActivity.this, R.layout.listview_item, null);
                  //使用每一次都findviewById的方法来获得listview_item内部的组件
                  TextView tv_item = (TextView) view.findViewById(R.id.tv_item);
                  tv_item.setText(list.get(position));
                  return view;
             }
 
             @Override
             public Object getItem(int position) {
                   return null;
             }
 
             @Override
             public long getItemId(int position) {
                   return 0;
             }
 
       }
}

优化一:

也是最普通的优化,就在MyAdapter类中的getView方法中,我们注意到,上面的写法每次需要一个View对象时,都是去重新inflate一个View出来返回去,没有实现View对象的复用,而实际上对于ListView而言,只需要保留能够显示的最大个数的view即可,其他新的view可以通过复用的方式使用消失的条目的view,而getView方法里也提供了一个参数:convertView,这个就代表着可以复用的view对象,当然这个对象也可能为空,当它为空的时候,表示该条目view第一次创建,所以我们需要inflate一个view出来,所以在这里,我们使用下面这种方式来重写getView方法:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
      View view;
      // 判断convertView的状态,来达到复用效果     
      if (null == convertView) {
          //如果convertView为空,则表示第一次显示该条目,需要创建一个view       
          view = View.inflate(MainActivity.this, R.layout.listview_item, null);
      } else {
          //否则表示可以复用convertView       
          view = convertView;
      }
      // listview_item里只有一个textview     
      TextView tv_item = (TextView) view.findViewById(R.id.tv_item);
      tv_item.setText(list.get(position));
      return view;
    }

优化二:

上面是对 view 对象的复用做的优化,我们经过上面的优化之后,我们不需要每一个 view 都重新生成了。下面我们来解决下一个每一次都需要做的工作,那就是 view 中组件的查找:
TextView tv_item = (TextView) view.findViewById(R.id.tv_item);
实际上, findViewById 是到 xml 文件中去查找对应的 id ,可以想象如果组件多的话也是挺费事的,如果我们可以让 view 内的组件也随着 view 的复用而复用,那该是多美好的一件事啊。。实际上谷歌也推荐了一种优化方法来做应对,那就是重新建一个内部静态类,里面的成员变量跟 view 中所包含的组件个数类型相同,我们这里的 view 只包含了一个 TextView ,所以我们的这个静态类如下:
private static class ViewHolder {
      private TextView tvHolder;
}
那么这个viewHolder类我们要如何使用才可以达到复用效果呢?基本思路就是在convertViewnull的时候,我们不仅重新inflate出来一个view,并且还需要进行findviewbyId的查找工作,但是同时我们还需要获取一个ViewHolder类的对象,并将findviewById的结果赋值给ViewHolder中对应的成员变量。最后将holder对象与该view对象在一块。
convertView不为null时,我们让view=converView,同时取出这个view对应的holder对象,就获得了这个view对象中的TextView组件,它就是holder中的成员变量,这样在复用的时候,我们就不需要再去findViewById了,只需要在最开始的时候进行数次查找工作就可以了。这里的关键在于如何将viewholder对象进行绑定,那么就需要用到两个方法:setTaggetTag方法了:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
      View view;
      ViewHolder holder;
      // 判断convertView的状态,来达到复用效果
      if (null == convertView) {
            // 如果convertView为空,则表示第一次显示该条目,需要创建一个view
            view = View.inflate(MainActivity.this, R.layout.listview_item, null);
            //新建一个viewholder对象
            holder = new ViewHolder();
            //将findviewbyID的结果赋值给holder对应的成员变量
            holder.tvHolder = (TextView) view.findViewById(R.id.tv_item);
            // 将holder与view进行绑定
            view.setTag(holder);
      } else {
            // 否则表示可以复用convertView
            view = convertView;
            holder = (ViewHolder) view.getTag();
      }
      // 直接操作holder中的成员变量即可,不需要每次都findViewById
      holder.tvHolder.setText(list.get(position));
      return view;
}

经过上面的做法,可能大家感觉不太到优化的效果,根据Google的文档,实际优化效果在百分之5左右。

优化三:

上面的两个例子中ListView都是显示的本地的List集合中的内容,List的长度也只有100个,我们可以毫不费力一次性加载完这100个数据;但是实际应用中,我们往往会需要使用Listview来显示网络上的内容,比如说我们拿使用ListView显示新闻为例:

其一:假如网络情况很好,我们使用的手机也许能够一下子加载完所有新闻数据,然后显示在ListView中,用户可能感觉还好,假如说在网络不太顺畅的情况下,用户加载完所有网络的数据,可能这个list1000条新闻,那么用户可能需要面对一个空白的Activity好几分钟,这个显然是不合适的

其二:我们知道Android虚拟机给每个应用分配的运行时内存是一定的,一般性能不太好的机器只有16M,好一点的可能也就是64M的样子,假如说我们现在要浏览的新闻总数为一万条,即便是网络很好的情况下,我们可以很快的加载完毕,但是多数情况下也会出现内存溢出从而导致应用崩溃的情况。

那么为了解决上面的两个问题,我们需要进行分批加载,比如说1000条新闻的List集合,我们一次加载20条,等到用户翻页到底部的时候,我们再添加下面的20条到List中,再使用Adapter刷新ListView,这样用户一次只需要等待20条数据的传输时间,不需要一次等待好几分钟把数据都加载完再在ListView上显示。其次这样也可以缓解很多条新闻一次加载进行产生OOM应用崩溃的情况。

实际上,分批加载也不能完全解决问题,因为虽然我们在分批中一次只增加20条数据到List集合中,然后再刷新到ListView中去,假如有10万条数据,如果我们顺利读到最后这个List集合中还是会累积海量条数的数据,还是可能会造成OOM的情况,这时候我们就需要用到分页,比如说我们将这10万条数据分为1000页,每一页100条数据,每一页加载时都覆盖掉上一页中List集合中的内容,然后每一页内再使用分批加载,这样用户的体验就会相对好一些。

Android的线程和线程池

AsyncTask来说,它的底层用到了线程池,对于IntentServiceHandlerThread来说,它的底层则直接就是线程。

 

AsyncTask封装了线程池和Handler,它主要是为了方便开发者在子线程中更新UI

HandelrThread是一种具有消息循环的线程,在它的内部可以使用Handler

IntentService是一个服务,系统对其进行了封装使它可以更方便的执行后台任务,IntentService内部采用了HandlerThread的来执行任务,当任务执行完毕后IntentService会自动退出。

IntentService的优点:从任务执行的角度看,IntentService的作用很像一个后台线程,但是IntentService是一种服务,他不容易被系统杀死从未可以尽量保证任务的进行,而如果一个后台线程,由于这个时候进程中没有活动的四大组件,那么这个进程的优先级就会变得很低,很容易被系统杀死。

 

主线程(main Thread)是指进程所拥有的线程。

 

主线程的作用是运行四大组件以及处理它们和用户的交互,而子线程的作用则是执行耗时任务,比如网络请求、I/O操作等。

 

AsyncTask

一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报错。

 

HandlerThread继承了Thread,它是一种可以使用HandlerThread。在run方法中通过Looper.prepaer()来创建消息队列,并通过Looper.loop()来开启消息循环,这样在实际的使用中就允许在HandlerThread中创建Handler了。

普通Thread要用于在run方法中执行一个耗时任务,而HandlerThread在内部创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。

由于HandlerThreadrun方法是一个无限循环,因此当明确不需要再使用HandlerThread时,可以通过它的quitquitSafely方法来终止线程的执行。

 

IntentService是一种特殊的Service,它继承了Service并且他是一个抽象类,因此必须创建它的子类才能使用IntentService。由于IntentService是服务的原因,它的优先级别单纯的线程高很多,所以IntentService比较适合执行一些高优先级的后台任务,不易被杀死。


动画

Android的动画可以分为三种:View动画、帧动画、属性动画。

 

View动画包括:旋转、平移、缩放、透明度。分别对应的是RotationAnimationTranslateAnimationScaleAnimationAlphaAnimation

<set>标签表示动画集合,对应AnimationSet类,它可以包含若干个动画,并且它的内部也是可以嵌套其他动画集合的。

自定义动画:说它简单,是因为派生一种新动画只需要继承Animation这个抽象类,然后重写它的initializeapplyTransformation方法,在initialize方法中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换的过程。说它复杂,是因为自定义View动画的过程主要是矩阵变换的过程,而矩阵变换是数学上的概念。

View动画可以在ViewGroup中控制子元素的出场效果,在Activity中实现不同Activity之间的切换效果。

LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会具有同样的动画效果。这种效果常常被用在ListView上,例如每个item都以一定的动画形式出现。

Activity有默认的切换效果,但是可以自定义,主要用到了overridePendingTransition(int enterAnim, int exitAnim ); 这个方法,这个方法必须在startActivity(Intent)或者finish()之后调用才能生效。

 



当系统配置发生改变后,Activity会被销毁,其onPauseonStoponDestory均会被调用,同时由于Activity是在异常情况下终止的,系统会调用onSaveInstanceState来保存当前Activity的状态。这个方法的调用时机是在onStop之前,这个方法只出现在Activity被异常终止的情况下,正常情况下系统不会回调这个方法的。当Activity被重新创建以后,系统会调用onRestoreInstanceState,并且把Activity销毁时onSaveInstanceState方法所保存的Bundle对象作为参数传递给onRestoreInstanceStateonCreate方法,因此,我们可以通过onRestoreInstanceStateonCreate方法来判断Activity是否被重建了,如果被重建了,那么我们就可以取出之前保存的数据并恢复,从时序上来说,onRestoreInstanceState的调用实际在onStart之后。

 

关于保存和恢复View层次结构,系统的工作流程就是这样的:首先Activity被意外终止时,Activity会调用onSaveInstanceState去保存数据,然后Activity委托Window去保存数据,接着Window在委托它上面的顶级容器去保存数据,顶层容器是一个ViewGroup,一般来说他可能是DecorView。最后顶层容器再去一一通知它的子元素来保存数据,这样整个数据保存过程就完成了。可以发现,这是一种典型的委托思想,上层委托下层、父容器委托子元素去处理一件事情。这种思想还用在了例如View的绘制过程、事件分发等。

接受的未知可以选择onRestoreInstanceState或者onCreate。二者的区别是:onRestoreInstanceState一旦被调用,其参数Bundle saveInstenceState一定是有值的,我们不用额外判断是否为空;但是onCreate就不行,onCreate如果正常启动的话,其参数Bundle saveInstanceStatenull,所以必须额外判断。官方建议onRestoreInstanceState去恢复数据。

 

如果进程中没有四大组件在执行,那么这个进程将很快被系统杀死。



View

自定义View

http://blog.csdn.net/u012975370/article/details/50033553#t4

DecorView为整个Window界面的最顶层View

DecorView只有一个子元素为LinearLayout,代表整个window界面,包括通知栏、标题栏、内容显示栏三块区域。

一个视图实际上是具有两对宽高的,一对是测量宽高,一对是绘制宽高。

绘制VIew本身的内容,通过调用View.onDraw(canvas)函数实现
绘制自己的孩子通过dispatchDrawcanvas)实现
  View组件的绘制会调用draw(Canvas canvas)方法,draw过程中主要是先画Drawable背景,对 drawable调用setBounds()然后是draw(Canvas c)方法.有点注意的是背景drawable的实际大小会影响view组件的大小,drawable的实际大小通过getIntrinsicWidth()getIntrinsicHeight()获取,当背景比较大时view组件大小等于背景drawable的大小。画完背景后,draw过程会调用onDraw(Canvas canvas)方法,然后就是dispatchDraw(Canvas canvas)方法, dispatchDraw()主要是分发给子组件进行绘制,我们通常定制组件的时候重写的是onDraw()方法。值得注意的是ViewGroup容器组件的绘制,当它没有背景时直接调用的是dispatchDraw()方法而绕过了draw()方法,当它有背景的时候就调用draw()方法,而draw()方法里包含了dispatchDraw()方法的调用。因此要在ViewGroup上绘制东西的时候往往重写的是dispatchDraw()方法而不是onDraw()方法,或者自定制一个Drawable,重写它的draw(Canvas c)和 getIntrinsicWidth(),getIntrinsicHeight()方法,然后设为背景。

EXACYTLY  一般是设置了明确的值或者是MATH_PARENT

AT_MOST  表示子布局限制在一个最大值内,一般为WARP_CONTENT

UNSPECIFIED  表示子布局想要多大就多大,很少使用

  • 每个Activity,都至少有一个Window,这个Window实际类型为PhoneWindow,当Activity中有子窗口,比如Dialog的时候,就会出现多个Window。Activity的Window是我们控制的,状态栏和导航栏的Window由系统控制。
  • 在DecorView的里面,一定有一个id为content的FraneLayout的布局容器,咱们自己定义的xml布局都放在这里面。
  • Activity的Window里面有一个DecorView,它使继承自FrameLayout的一个自定义控件,作为整个View层的容器,及View树的根节点。
  • Window是虚拟的概念,DecorView才是看得见,摸得着的东西,Activity.setContentView()实际调用的是PhoneWindow.setContentView(),在这里面实现了DecorView的初始化和id为content的FraneLayout的布局容器的初始化,并且会根据主题等配置,选择不同的xml文件。而且在Activity.setContentView()之后,Window的一些特征位将被锁定。
  • Activity.findViewById()实际上调用的是DecorView的findviewById(),这个方法在View中定义,但是是final的,实际起作用的是在ViewGroup中被重写的findViewTraversal()方法。
  • Activity的mWindow成员变量是在attach()的时候被初始化的,attach()是Activity被通过反射手段实例化之后调用的第一个方法,在这之后生命周期方法才会依次调用
  • 在onResume()刚执行之后,界面还是不可见的,只有执行完Activity.makeVisible(),DecorView才对用户可见
  • ViewManager这个接口里面就三个接口,添加、移除和更新,实现这个接口的有WindowManager和ViewGroup,但是他们两个面向的对象是不一样的,WindowManager实现的是对Window的操作,而ViewGroup则是对View的增、删、更新操作。
  • WindowManagerImpl是WindowManager的实现类,但是他就是一个代理类,代理的是WindowManagerGlobal,WindowManagerGlobal一个App里面就有一个,因为它是单例的,它里面管理了App中所有打开的DecorView,ContentView和PhoneWindow的布局参数WindowManager.LayoutParams,而且WindowManagerGlobal这个类是和WMS通信用的,是通过IWindowSession对象完成这个工作的,而IWindowSession一个App只有一个,但是每个ViewRootImpl都持有对IWindowSession的引用,所以ViewRootImpl可以和WMS喊话,但是WMS怎么和ViewRootImpl喊话呢?是通过ViewRootImpl::W这个内部类实现的,而且源码中很多地方采用了这种将接口隐藏为内部类的方式,这样可以实现六大设计原则之一——接口最小原则,这样ViewRootImpl和WMS就互相持有对方的代理,就可以互相交流了
  • ViewRootImpl这个类每个Activity都有一个,它负责和WMS通信,同时相应WMS的指挥,还负责View界面的测量、布局和绘制工作,所以当你调用View.invalidate()和View.requestLayout()的时候,都会把事件传递到ViewRootImpl,然后ViewRootImpl计算出需要重绘的区域,告诉WMS,WMS再通知其他服务完成绘制和动画等效果,当然,这是后话,咱们以后再说。
  • Window分为三种,子窗口,应用窗口和系统窗口,子窗口必须依附于一个上下文,就是Activity,因为它需要Activity的appToken,子窗口和Activity的WindowManager是一个的,都是根据appToken获取的,描述一个Window属于哪种类型,是根据LayoutParam.type决定的,不同类型有不同的取值范围,系统类的的Window需要特殊权限,当然Toast比较特殊,不需要权限
  • PopupWindow使用的时候,如果想触发按键和触摸事件,需要添加一个背景,代码中会根据是否设置背景进行不同的逻辑判断
  • Dialog在Activity不可见的时候,要主动dismiss掉,否则会因为appToken为空crash
  • Toast属于系统窗口,由系统服务NotificationManagerService统一调度,NotificationManagerService中维持着一个集合ArrayList,最多存放50个Toast,但是NotificationManagerService只负责管理Toast,具体的显示工作由Toast::TN来实现
  • View中的performClick()的出发条件:
    public boolean performClick() {
            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
            if (mOnClickListener != null) {
                playSoundEffect(SoundEffectConstants.CLICK);
                mOnClickListener.onClick(this);
                return true;
            }
            return false;
        }
    由源码可以看出,只要是使用了view.setOnClickListener()方法设置监听器,就会自动触发view.performClick()。需要注意的是,如果同时使用了view.setOnTouchListener()方法,则有可能存在拦截view.performClick()的响应事件,因为当view.OnTouchEvent()在event.getAction() == MotionEvent.ACTION_DOWN时返回false,系统会认为view不需要处理Touch事件,则后续的Touch事件(move、up、click)就不会被传进来,所以也不会触发view.performClick(),而view.setOnTouchListener()相当于是重写了view.OnTouchEvent(),所以在写view的TouchListener处理时,需要留意view是否存在点击事件监听,如果有,则在适当的位置使用view.performClick()触发点击事件。



 
 

子线程更新ui的三种方式

1.通用的就是handler

2.通过runOnUiThread方法

方法内部实现如下:
    public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
使用方法如下:
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 此处执行耗时操作,结束后,执行runOnUiThread将线程切换到主线程去更新ui
                
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        // 更新ui操作
                    }
                });
            }
        }).start();
如果在非上下文中环境中,可以通过一下方法来实现:
        final Activity activity = (Activity) mTextView.getContext();
        new Thread(new Runnable() {
            @Override
            public void run() {
                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        
                    }
                });
            }
        }).start();
在其他地方,则需要传递Activity对象,因为runOnUiThread方法是Activity的方法。

3.通过view.post(runnable)来实现

        mTextView.post(new Runnable() {
            @Override
            public void run() {

            }
        });
以上不管哪种方法,原理都是将更新ui的消息从子线程中传递到主线程中,因为,更新view只能在主线程,这个是无法改变的。

4.子线程直接更新ui的极端情况

android的UI访问是没有加锁的,这样会导致多个线程访问ui会不安全,那么既然这样,为什么不加锁呢,因为加上锁机制会让ui访问的逻辑变得复杂,其次锁机制会降低ui访问的效率,锁机制会阻塞某些线程的执行。所以android规定,只能在主线程更新ui。

子线程真的无法更新ui吗,答案是no,因为有些极端情况,还是可以更新ui的。

我们在onCreate中直接执行以下代码:

        Log.i("niejianjian", " -> onCreate -> " + Thread.currentThread().getId());
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.i("niejianjian", " -> Thread -> " + Thread.currentThread().getId());
                mTextView.setText("fjsdlj l");
            }
        }).start();
发现更新ui是可以成功的,我们可以打印当前的线程,发现确实是在子线程中,因为主线程的id是1。但是我们在将Thread睡眠200ms,
        Log.i("niejianjian", " -> onCreate -> " + Thread.currentThread().getId());
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i("niejianjian", " -> Thread -> " + Thread.currentThread().getId());
                mTextView.setText("fjsdlj l");
            }
        }).start();
结果就报错了,log如下:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
	at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6257)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:868)
ViewRootImpl是ViewRoot的实现类,异常主要是checkThread抛出的,因为里面对线程做了判断:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

因为view的绘制过程中,都会执行ViewRootImpl的checkThread来检查是否是主线程更新,所以onCreate中的子线程可以更新ui,主要是因为ViewRootImpl还没有创建,所以无法进行检查。

ViewRootImpl的创建在onResume方法回调之后,而我们一开篇是在onCreate方法中创建了子线程并访问UI,在那个时刻,ViewRootImpl是没有创建的,无法检测当前线程是否是UI线程,所以程序没有崩溃一样能跑起来,而之后修改了程序,让线程休眠了200毫秒后,程序就崩了。很明显200毫秒后ViewRootImpl已经创建了,可以执行checkThread方法检查当前线程。

参考: http://www.cnblogs.com/xuyinhuan/p/5930287.html


Bitmap的加载和Cache

Android对单个应用施加了内存限制,16MB,这导致在加载Bitmap的时候很容易出现内存溢出,异常信息为:

java.lang.OutofMemoryErrorbitmap size exceeds VM budget

比较常使用的缓存策略为LruCacheDiskLruCache。其中LruCache常备用做内存缓存,额DiskLruCache常被用作存储缓存。

LruLeast Recnetly Used的缩写,也就是最近最少使用算法,其核心思想为:当缓存快满时,会淘汰掉近期最少使用的缓存目标。

如何加载一个Bitmap

BitmapFactory提供了四类方法:decodeFiledecodeResourcedecodeSreamdecodeByteArray,分别支持从文件系统、资源、输入流以及字节数组中加载出一个Bitmap,其中decodeFiledecodeResource又简介调用了decodeStream方法。

如何高效的加载Bitmap

采用BitmapFactory.Options来加载所需尺寸的图片。一般情况下ImageView都没有原始图片那么大,如果将图片整个加载,在设给imageView的话,显然没必要,因为并没有办法显示原始的图片。通过BitmapFactory.Options就可以按一定的采样率来加载缩小后的图片了,这样就可以降低内存占用从而在一定程度上避免了OOM

通过BitmapFactory.Options来缩放图片,主要用到了参数inSampleSize参数,即采样率。

当inSampleSize1时,采样后的图片大小为原始大小;数值必须是大于一的整数,当小于1时,相当于是1。比如insample2,那么采样后的图片的宽高均为原图的1/2,而像素是原图的1/4,所以内存大小也为原图的1/4inSample的取值应该总是2的指数,如果不是2的指数,系统会自动向下取整(并不完全成立)。

如何获取采样率?

1).BitmapFactory.OptionsinJustDecodeBounds参数设为true并加载图片

2).BitmapFactory.Options中取出图片的原始宽高信息,它们对应于outWidthoutHeight参数

3).根据采样率的规则并结合目标View的所需大小,计算出采样率inSampleSize

4).BitmapFactory.OptionsinJustDecodeBounds参数设为false,然后重新加载图片。 

优化列表的卡顿现象:

1).不要在getView中执行耗时操作。必须通过异步操作来实现,比如ImageLoader

2).控制异步任务的刷新频率

3).开启硬件加速。设置android:hardwareAccelerated=true即可

针对控制刷新频率的详解:(为什么要在列表滑动的时候停止加载图片)

在getView方法中会通过ImageLoaderbindBitmap方法来异步加载图片,但是如果用户可以频繁的上下滑动,这个会在一瞬间产生上百个异步任务,这些异步任务会造成线程池的拥堵并随即带来大量的Ui更新操作,这是没有意义的。由于一瞬间存在了大量的UI更新操作,这些UI操作是运行在主线程,所以造成了一定程度的卡顿。

具体实现时,可以给ListViewGridView设置setScrollListener,并在OnScrollListeneronScrollStateChanged方法中判断是否滑动,如果是就停止加载图片。


JNI

Java JNI的本意是Java Native Interface,它是为了方便Java调用CC++等本地代码所封装的一层接口。

NDKAndroid所提哦功能的一个工具集合,通过NDK可以在Android中更方便地通过JNI来访问本地代码,比如C或者C++

NDK的好处:

1).提高代码的安全性。由于so库反编译比较困难,因此NDK提高了Android程序的安全性。

2).可以很方便地使用目前已有的C/C++开源库。

3).便于平台间的移植。通过C/C++实现的动态库可以很方便的在其他平台上使用。

4).提高程序在某些特定情形下的执行效率,但是并不能明显提升Android程序的性能。

JNI开发流程

1.在java中声明native方法

2.编译java源文件得到class文件,然后通过javah命令导出JNI的头文件

3.实现JNI方法

4.编译so库并在java中调用


Toast

为什么Toast需要由系统统一控制,在子线程中为什么不能显示Toast?

首先Toast也属于窗口系统,但是并不是属于App的,是由系统同一控制的。 
关于这一块不想说太多,具体实现机制请参考后面的文章。

为了看下面的内容,你需要知道以下几件事情:

  1. Toast的显示是由系统Toast服务控制的,与系统之间的通信方式是Binder
  2. 整个Toast系统会维持最多50个Toast的队列,依次显示
  3. 负责显示工作的是Toast的内部类TN,它负责最终的显示与隐藏操作
  4. 负责给系统Toast服务发送内容的是INotificationManager的实现类,它负责在Toast.show()里面把TN对象传递给系统消息服务,service.enqueueToast(pkg, tn, mDuration);这样Toast服务就持有客户端的代理,可以通过TN来控制每个Toast的显示与隐藏。
所以说,TN初始化的线程必须为主线程,在子线程中使用Handler,由于没有消息队列,就会造成这个问题。

HTTP

1.HTTP协议可以通过传输层的TCP协议在额客户端和服务器之间传输数据。

2.HTTP协议采用了请求/响应的工作方式。基于HTTP1.0协议的客户端每次向服务器发出请求后,服务器就会向客户端返回响应消息(包括请求是否正确以及锁请求的数据),在确认客户端已经收到响应消息后,服务器就会关闭网络连接,并不保存任何历史信息和状态信息。HTTP协议也被认为是无状态协议。

3.通讯过程:

 通过客户端Socket建立连接;向服务器发送请求;服务器回送响应;服务器关闭连接。

4.HTTP1.1协议增加了持久连接支持。也就是服务器将关闭客户端连接的主动权还给了客户端,客户端向服务器发送一个请求并接受到一个响应后,只要不调用Socket类的close方法关闭网络,就可以继续想服务器发送HTTP请求。

5.Android6.0API23)删除了http.client的一些类,导致HttpPost类完全找不到了。


Android性能优化

1.布局优化

减少布局文件的层级,这样就减少了Android绘制工作。

如果可以使用LinearLayout也可以使用RelativeLayout时,就选择LinearLayout。这是因为RelativeLayout的功能比较复杂,它的布局过程需要花费更多的CPU时间。LinearLayoutFrameLayout一样是一种简单高校的ViewGroup。如果使用LinearLayout布局需要嵌套才能完成,此时建议使用RelativeLayout

在不影响层级深度的情况下,使用LinearLayout而不是Relativelayout。因为Relative会让子view执行两次onMeasure,LinearLayout在有weight的时候,才会让子view执行两次onMeasure。Measure耗时越长,绘制效率越低。

布局优化另一种手段是采用<include>标签、<merge>标签和ViewStub<include>标签只要用于布局重用,<merge>标签一般和<include>配合使用,它可以降低减少布局的层级,而ViewStub则提供了按需加载的功能,当需要时才会将ViewStub中布局加载到内存,这就提高了程序的初始化效率。

1).<include>标签只支持以android:layout_开头的属性,比如android:layout_widthandroid:layout_height,其他属性不支持,比如android:background

2).android:id是特例,如果<include>和被包含的布局文件的根源素都指定了id属性,那么以<include>指定的id属性为准。

3).如果<include>标签指定了android:layout_*这种属性,那么要求android:layout_widthandroid:layout_height必须存在,否则其他android:layout_*形式的属性都无效。

4).<merge>标签一般和<include>标签一起使用从而减少布局的层级。如果父布局是LinearLayout,而且orientation=vertical,<include>标签中包含的布局中包含两个按钮,也是LinearLayout布局,方向也是orientaion=vertical,此时,再被包含的布局中再写LinearLayout就是多要点,通过merge标签就额可以去掉多余的那一层LinearLayout

5).ViewStub继承了View,它非常轻量级且宽高都是。因此它本身不参与任何的布局和绘制过程。ViewStub的意义在于按需加载所需的布局文件,在实际开发中,有很多布局文件在正常情况下不会显示,比如网络异常时的界面,这个时候就没有必要在整个界面初始化的时候将它加载进来,在需要的时候在加载。

<ViewStub
        android:id="@+id/stub_import"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:inflatedId="@+id/panel_import"
        android:layout="@layout/activity_test" />

其中stub_importViewStubid,而panel_importlayout/activity_test这个布局的根元素的id。在需要加载ViewStub中的布局的时候,可以按照如下两种方式加载:

((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);

或者

View importpPanel = ((ViewStub)findViewById(R.id.stub_import)).inflate();
当ViewStub 通过 setVisibility 或者 inflate 方法加载后, ViewStub 就会被它内部的布局替换掉,这个时候 ViewStub 就不再是整个布局结构中的一部分了。 ViewStub 不支持 <merge>

干货连接: http://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650821434&idx=1&sn=dd404347eb5f953f7a5737a31ae864e8&chksm=80b787a4b7c00eb297a81316483f

2.绘制化

绘制优化是指ViewonDraw方法要避免执行大量的操作,主要体现在两方面。

1).onDraw中不要创建新的布局对象,这是因为onDraw方法可能会被频繁调用,这样就会在一瞬间产生大量的临时对象,这不仅占用了过多的内存而且还会导致系统更加频繁的gc,降低了程序的执行效率。

2).另一方面,onDraw方法中不要做耗时的任务,也不能执行成千上万次的循环操作,尽管每次循环都很轻量级,但是大量的循环仍然十分抢占CPU的时间片,这会造成View的绘制过程不流畅。按照Google官方给出的性能优化典范中的标准,View的绘制帧率保证60fps是最佳的,这就是要求每帧的绘制时间不超过16ms(1000/16),虽然程序很难保证这个时间,但是尽量降低onDraw方法复杂度总是切实有效的。

3.内存泄漏优化

内存泄漏的优化分为两方面,一方面是开发过程中避免写出有内存泄漏的代码,另一方面是通过一些分析工具比如MAT来找出潜在的内存泄漏进行解决。

1).静态变量导致的内存泄漏

这面的代码将导致内存泄漏,Activity不能正常销毁。

public class MainActivity1 extends Activity {
	private static final String TAG = "MainActivity1";

	private static Context mContext;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mContext = this;
	}
}
上面的代码改造下, sView 是一个静态变量,它内部持有了当前 Activity ,所以 Activity 仍然无法释放。
public class MainActivity1 extends Activity {
	private static final String TAG = "MainActivity1";

	private static View mView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mView = new View(this);
	}
}

2).单例模式导致的内存泄漏

首先提供一个单例模式TestMangerTestManager可以接受外部的注册并将外部的监听器存储起来。

public class TestManager {
	private List<onDataArrivedListener> mOnDataArrivedListeners = new ArrayList<onDataArrivedListener>();

	private static class SingletonHolder {
		public static final TestManager INSTANCE = new TestManager();
	}

	private TestManager() {
	}

	public static TestManager getInstance() {
		return SingletonHolder.INSTANCE;
	}

	public synchronized void registerListener(onDataArrivedListener listener) {
		if (!mOnDataArrivedListeners.contains(listener)) {
			mOnDataArrivedListeners.add(listener);
		}
	}

	public synchronized void unregisterListener(onDataArrivedListener listener) {
		mOnDataArrivedListeners.remove(listener);
	}

	public interface onDataArrivedListener {
		public void onDataArrived(Object data);
	}
}
接着再让 Activity 实现 OnDataArrivedListener 接口并向 TestManager 注册监听,如下所示,下面的代码由于缺少解注册的操作所以会引起内存泄漏,泄漏的原因是 Activity 的对象被单例模式的 TestManager 所持有的,而单例模式的特点就是其生命周期和 Application 保持一致,因此 Activity 对象无法被及时释放。
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		TestManager.getInstance().registerListener(this);
	}

3).属性动画导致的内存泄漏

Android3.0开始,Google提供了属性动画,属性动画中有一类无限循环的动画,如果在Activity中播放此类动画且没有在onDestory中停止动画,那么动画会一直被播放下去,尽管已经无法在界面上看到动画效果了,并且这个时候ActivityView动画会被动画持有,而View又持有了Activity,最终Activity无法释放。下面的动画是无限动画,会泄漏当前Activity,解决方法就是在ActivityonDestory中调用animator.cancel()来停止动画。

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		Button mButton = (Button) findViewById(R.id.button1);
		ObjectAnimator animator = ObjectAnimator.ofFloat(mButton, "rotation",
				0, 360).setDuration(2000);
		animator.setRepeatCount(ValueAnimator.INFINITE);
		animator.start();
	}

1.你可以提醒系统定期回收,system.gc(),对于那些你不用的东西,你可以设置为null或者0,系统就会回收,还有其他的东西。
2.可以自己手动关闭,比如bitmap,就有bitmap.recycle();方法,还有cursor.close();用完记得关闭。
3.如果是特殊的一些自己写的东西,你可以写一个回收的方法或者类,来回收,比如动画。

onPause中不用去释放,onStop里才要释放那些你的代码自己用的东西

Android内存泄漏就这样产生了:http://blog.csdn.net/sunchaoenter/article/details/7224926

如何避免内存泄漏:http://blog.csdn.net/sunchaoenter/article/details/7209635

4.响应速度优化

响应速度优化的核心思想就是避免在主线程中做耗时操作。

如果Activity5秒钟无法响应屏幕触摸事件或者键盘输入事件就会出现ANR,而BroadcastReceiver如果10秒之内还未完成操作,也会出现ANR

5.LIstView优化

首先要采用ViewHolder并避免在getView中执行耗时操作;

要根据列表的滑动状态来控制任务的执行频率,比如当列表快速滑动时显示是不太合适开启大量的异步任务的;

最后可以尝试开启硬件加速来使ListView的滑动更加流畅。

ListView的优化策略完全适用于GridView

6.线程优化

线程优化的思想就是采用线程池,避免程序中存在大量的Thread。线程池可以重用内部的线程,从而避免了线程的创建和销毁所带来的性能开销,同时线程池还能有效地控制线程池的最大并发数,避免大量的线程因互相抢占系统资源从而导致阻塞线程的发生。

7.其他建议

避免创建过多的对象;

不要过多使用枚举,枚举占用的内存空间要比整型大;

常量请使用static fianl来修饰;

使用一些Android特有的数据结构,比如SpareArrayPair等,它们都具有更有的性能;

适当使用软引用和弱引用;

采用内存缓存和磁盘缓存;

尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏。


综合技术

1.用户使用中,遇到crash,如何获得crash信息。

可以通过CrashHandler来监视应用的crash信息,给程序设置一个CrashHandler,这样当程序crash时就会调用CrashHandleruncaughtException方法。在方法中获得crash信息,然后上传服务器。

2.Android有一个限制,就是整个应用的方法数不能超过65536,否则会编译错误,并且无法安装到手机上。Google提供了multidex方案解决这个问题,通过将一个dex文件拆分为多个dex文件来避免单个dex文件方法数越界的问题。(dex文件:Andorid平台上可执行文件类型)。

方法越界的另一种解决方案是动态加载。动态加载可以直接加载一个dex形式的文件,将部分代码打包到一个单独的dex文件中(也就是dex格式的jar或者apk),并在程序运行时根据需要去动态加载dex中的类,这种方式既可以解决环节方法数越界额问题,也可以为程序提供按需加载的特性,同时这还为应用按模块更新提供了可能性。

3.反编译:

Android中反编译主要通过dex2Jar以及apktool来完成。dex2Jar可以将apk转换成一个jar包,这个jar包在通过反编译工具jd-gui来打开就可以查看到反编译后的Java代码了。Apktool主要用于应用的解包和二次打包。








1. Inent大多是应用层的功能整合,Binder则大多用在系统层的整合

2. 接口能不能继承接口:可以,因为手动创建一个aidl文件,会自动在gen目录下生成同名的 java文件,这个java文件本身就是接口,而且它还继承了android.os.IInterface,所以我认为接口可以继承接口。所以在Binder中传输的接口都需要继承IInterface接口。

3. 尽管Book类和IBookManager在相同的包下,但是在IBookManager中仍需导入Book类,这是AIDL的特殊之处。

4. java synchronized详解:http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html

5. 在android中的主线程也就是UI线程,UI线程中才可以操作界面元素。在主线程中执行耗时操作,会造成UI阻塞,导致ANR;在非主线程中更新UI,会直接报错。

案例:SerrviceActivity发送广播,更新UI :http://blog.csdn.net/u012975370/article/details/50033553#t0

6. android对单个应用所使用的最大内存做了限制,可以利用多进程模式增大一个应用可使用的内存。

7. android中使用多进程的方法:

1).给四大组件在AndroidManifest.xml中指定androidprocess属性。

2).非常规方法:通过JNInative层去fork一个新的进程。

8. 使用多进程会造成如下的几个方面的问题:

    1) 静态成员和单例模式完全失效

    2) 线程同步机制完全失效  

    3) SharedPreferences的可靠性下降

    4) Application会多次创建

9. Thread的运行是独立于Acitivty的,当Accitivtyfinish之后,如果你没有主动停止Thread或者Thread中的run方法没有执行完毕的话,Thread也会一直执行。

13. Android的动画类型:View Animation , Drawable Animation , Property Animation 

View Animation 只是支持简单的缩放、平移、旋转、透明等基本属性,View动画对View的影响做操作,它并不能改变View的位置参数以及宽高。如果想要保留动画后的状态需要属性fillAfter,设置为true,否则动画完成后自动消失。 Property Animation解决了View Animation不能真正改变View属性的问题。

14. getX/getY返回的是相对于当前View左上角的xy坐标,

getRawX/getRawY返回的是相对于手机屏幕左上角的xy坐标。

15.

scrollTo()相对于初始位置滚动某段距离 

scrollBy()相对于当前位置滚动某段距离

 

16.使用Toast时,建议定义一个全局的Toast对象,这样可以避免连续显示Toast时不能取消上一次Toast消息的情况(如果你有连续弹出Toast的情况,避免使用Toast.makeText)

17.使用Adapter的时候,如果你使用了ViewHolder做缓存,在getView的方法中无论这项的每个视图是否需要设置属性(比如TextView设置的属性可能为null,item的某一个按钮的背景为透明、某一项的颜色为透明等),都需要为每一项的所有视图设置属性(textview的属性为空也需要设置setText(“”),背景透明也需要设置),否则在滑动的过程中会出现内容的显示错乱。

18.不同API版本的AsyncTask实现不一样,有的是可以同时执行多个任务,有的API中只能同时执行一个线程,所以在程序中同时执行多个AsyncTask时有可能遇到一个AsyncTask的excute方法后很久都没有执行。调用AsyncTask的excute方法不能立即执行程序的原因分析及改善方案

19.系统上安装了很多浏览器,如何指定某个浏览器访问固定的网页

 	Intent intent =newIntent();        
         intent.setAction("android.intent.action.VIEW");    
         Uri content_url =Uri.parse("http://www.163.com");   
         intent.setData(content_url);           
         intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");   
         startActivity(intent);
只要修改以intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");
中相应的应用程序packagename 和要启动的activity即可启动其他浏览器来
uc浏览器":"com.uc.browser", "com.uc.browser.ActivityUpdate“
opera浏览器:"com.opera.mini.android", "com.opera.mini.android.Browser"
qq浏览器:"com.tencent.mtt", "com.tencent.mtt.MainActivity"

20.在Activity中弹出对话框,并不会执行onPause,所以对话框消失,也不会执行onResume方法。因为我们创建dialog的时候,需要传入一个Acitivity的上下文,既然使用者Activity的上下文,就认为Acitivity是在交互状态中所以,每一失去焦点,就不会执行onPause方法。



Java基础:

1.面向对象

面向过程:强调的是功能行为

面向对象:将功能行为进行封装,强调的是具备功能行为的对象。

例如:将大象装进冰箱

这个过程包括,打开冰箱->存储->关闭冰箱。

这三个行为的对象是冰箱

如果我们面向过程,就会执行打开、存储、关闭这三个操作。

但是我们面向对象,我们只需要记住冰箱,让冰箱去执行打开、存储、关闭的过程即可。

而且,我们从执行者变成了指挥者。

原来我们需要自己执行打开、存储、关闭操作。

现在我们只需要指挥冰箱去打开、存储、关闭。至于执行过程,怎么执行,我们不需要关心。


2.Java多线程

多线程尽可能利用CPU的资源,提高效率

只有运行状态的线程才会有机会执行代码,主线程的终止不会影响其他的正在运行中的线程,主线程终止也就是main()方法退出了,只有进程中的所有线程都终止了,进程(JVM进程)才会退出,只要有线程没有终止,进程就不会退出。

线程编程的两种方式:

1.Thread

   线程(Thread)包含一个可以运行的过程:run()方法。

2.创建一个具体线程的步骤如下:

  第一,继承Thread类。

  第二,覆盖run方法,就是更新运行过程,实现用户自己的过程。

  第三,创建线程实例,就是创建一个线程。

  第四,使用线程实例的start()方法启动线程,启动以后线程会尽快的去并发执行run()

方法一:继承Thread

步骤:

 1).继承Thread类,覆盖run()方法,提供并发运行的过程。

 2).创建这个类的实例

 3).使用start()方法启动线程。

方法二:实现Runnable接口

步骤:

  1).实现Runnbale接口,实现run()方法,提供并发运程的过程

  2).创建这个类的实例,用这个实例作为Thread构造器参数,创建爱你Thread

  3).使用start()方法启动线程

class MyRunnable implements Runnable {
	@Override
	public void run() {
	}
}
public class TestThread {
	public static void main(String[] args) {
		Runnable myRunnable = new MyRunnable();
		Thread thread = new Thread(myRunnable);
		thread.start();
	}
}

线程的常用方法和属性

1.线程的优先级

   t.setPriority(Thread.MAX_PRIORITY);

   默认有10级优先级,优先级高的线程获得running状态的机会多,机会的多少不能代码干预。默认的优先级是5.

2.后台线程

  t.setDaemon(true);

  Java进程的结束:当前所以前台线程都结束时,java进程结束。

  当前台线程全都结束时,不管后台线程有没有执行结束,都要被停止。

3.获得线程的名字

  geName()

4.获得当前线程

  Thread main = Thread.currentThread()

Thread的状态

New新建状态 -> Runnable可运行(就绪)状态 -> Running运行状态 -> Block阻塞(挂起)状态  -> Dead死亡状态

Sleep状态的打断唤醒

1.Thread.sleep(times)  Running->Block ->Runnable

2.Interrupt()方法打断/中断。

  使用该方法可以让一个线程提前唤醒另外一个sleep Block的线程。

3.被唤醒线程会出现中断异常。

Sleep()wait()的区别

SleepThread类中定义的方法,表示线程休眠,会自动唤醒

WaitObject中定义的方法,需要手工调用notify()或者notifyAll()方法

Sleep就是正在执行的线程主动让出了cpucpu去执行其他的线程,在sleep指定的时间过后,cpu才会回到这个线程上继续执行,如果当前线程进入同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。Wait是指在一个已经进入同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到暂时的可以得到同步锁并运行。只有其他线程调用notify方法,(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了, 但不是马上得到锁,因为锁还在别人的手里,别人还没有释放。如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在notify方法后增加一个等待和一些代码,看看效果),调用wait方法的线程就会接触wait状态和程序可以再次得到锁后继续向下运行。

public class TestThread {
	public static void main(String[] args) {
		new Thread(new Thread1()).start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		new Thread(new Thread2()).start();
	}

	private static class Thread1 implements Runnable {

		@Override
		public void run() {
			synchronized (TestThread.class) {
				System.out.println("enter thread1...");
				System.out.println("thread1 is waiting");
				try {
					/*
					 * 释放锁有两种方式,第一种方式是程序自然离开监视器的范围,也就是离开了synchronized关键字关下的代码范围,
					 * 另一种方式就是synchronized关键字关下的代码内部调用监视器对象的wait方法
					 * ,这里,使用wait方法释放锁。
					 */
					TestThread.class.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("thread1 is going on...");
				System.out.println("thread1 is being over !");
			}
		}
	}

	private static class Thread2 implements Runnable {

		@Override
		public void run() {
			System.out.println("enter thread2...");
			System.out
					.println("thread2 notify other thread can release wait status");
			/*
			 * 由于notify方法并不释放锁,即使thread2调用下面的sleep方法休息10毫秒,但thread1仍然不会执行,
			 * 因为thread2没有释放锁,所以thread1无法得锁
			 */
			TestThread.class.notify();
			System.out.println("thread2 is sleeping ren millisecond");
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("thread2 is going on...");
			System.out.println("thread2 is being over !");
		}
	}
}
Runnable和Thread的区别:

在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成的。但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有,查看Thread元那么会发现,有一个构造函数public Thread(Runnable target);此构造方法接受Runnable的子类实例。

由于java类有单继承多接口的特点,所以使用Runnable可以避免点继承的局限性;适用于资源共享。其实,Thread也是Runnable的子类。

http://mars914.iteye.com/blog/1508429

http://blog.csdn.net/wwww1988600/article/details/7309070

线程安全:

线程安全问题都是由全局变量及静态变量引起的。

若每个线程中对全局变量、静态变量只有读操作,没有写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则就可能影响线程安全。

线程池:

Java线程池是通过hashmap获取当前的线程,保持线程同步。

子线程更新UI

AndroidUI是单线程的,为了避免拖住GUI,一些较费时的对象应该交给独立的线程去执行,如果幕后的线程来执行UI对象,Android就会发出错误讯息CalledFromWrongThreadException


堆主要是用来存放对象的,栈主要是用来执行程序的。

Thread类中的start()run()方法的区别:

Start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,这和直接调用run()方法的效果是不一样的。当你调用run()方法的时候,只会在原来的线程中调用,没有新的线程启动,start()才会启动新线程。

Java内存模型对一个线程所做的变动能被其他线程可见提供了保证,它们之间是线程发生关系,这个关系定义了一些规则让程序员在并发线程时思路更清晰,比如,线程发生关系确保了:

1).线程内的代码能够按先后顺序执行,这被成为程序次序规则;

2).对于同一个锁,一个解锁操作一定要发生在时间上后发生的另一个锁定操作之前,也叫做管理锁定规则;

3).前一个对volatile的写操作在后一个volatile的读操作之前,也叫做volatile变量规则;

4).一个线程内的任何操作都必须在这个线程start()调用之后,也叫做线程启动规则;

5).一个线程的所以操作都会在线程终止之前,线程终止规则;

6).一个对象的终结才做必须在这个对象构造完成之前,也叫对象终结规则;

7).可传递性。


50java多线程的面试题:http://www.androidchina.net/589.html



Java集合:

Collection

List

    LinkedList

    ArrayList

    Vector

        Stack

Set

Map

      Hashtable

      HashMap

      WeakHashMap

Map:提供keyvalue的映射

List:有序,可重复的集合

ArrayList:实质就是一个会自动增长的数组。

1).查询效率比较高,增删效率比较低,适用于查询比较频繁,增删动作比较少的元素管理的集合。

2).加载大批量的数据时,先进行手动扩容(调用ensureCapacity(int minCapacity)

LinkedList:底层是用双向循环链表来实现的。

查询效率低,但是增删效率很高,适用于增删动作的比较频繁,查询次数较少的元素管理的集合。

Set:无序的,不允许有重复的元素集合。

HashSet

      Object类中的hashCode()的方法是所有类都会继承的方法,这个方法会算出一个Hash码值返回,HashSet会用Hash码值去和数组长度取模,对象的模值(这个模值就是对象要存放在数组中的位置,和数组的下标相同)相同时才会判断数组中的元素和要加入的对象的内容是否相同,如果不同才会再找位置添加进去,相同则不允许添加。

      如果数组中的元素和要加入的对象的hashCode()返回了相同的Hash码值,才会用equals()方法来判断两个对象的内容是否相同。

      注意:要存入HashSet的集合对象中的自定义类必须hashCode()euqals()两个方法,才能保证集合中元素不重复。

Map:存放key-value(有关系的两个对象,一个叫做key,一个叫做value,同时存入)

HashMap:基于哈希表的Map接口的实现,此实现提供所有可选的映射操作,并允许使用null值和null键。

Hashtable:同HashMap,一般不使用

HashMapHashtable的区别:

HashMap:非线程安全,不支持并发控制,允许空的键值对。

Hashtable:是线程安全,支持并发控制,不允许有空的键值对。

Key一般是8中基本类型的封装类或者是String类,拿自己自定义的类作为key没有意义。

 

线性表List

List表示有先后次序的对象集合,比如歌曲列表。

1).ArrayList = Object[] + 线性表操作(增删改差)

2).StringBuilder = char[] + 操作(增删改查)

ArrayList和 StringBuilder相比,基本操作都差不多,但是ArrayList除了可以添加String类型,还可以添加其他对象。

StringBuilder打印出来是一个字符串,ArrayList打印出来,是一个类似于数组,用中括号包裹起来,中间用逗号隔开的一组内容。

 

对于Vector&ArrayListHashtable&HashMap,要记住线程安全的问题,记住VectorHashtable是旧的,是java一旦生就是提供的,它们是线程安全的,ArrayListHashMapjava2时才提供的,它们是线程不安全的。所以,一般单线程的时候,用HashMapArrayList,因为不需要考虑线程安全,但是多线程时就需要用VectorHashtable

VectorArrayList都有一个初始的容量大小,当存储进它们里面的元素超过容量时,就会自动增加大小,Vector默认增加为原来的两倍,ArrayList默认增加为原来的1.5倍。ArrayList都可以设置初始的空间大小,Vector可以设置增长的空间大小,而ArrayList则不可以设置。

 

LinkedListArrayList

ArrayList是使用变长数组算法实现的,ArrayList继承自List()

1).ArrayListVector的比较:Vector线程安全的,效率较低,也是使用可变长数组算法实现的,继承自List接口。ArrayList,线程不安全的,效率高速度快,现在常用。

2).LinkedLIstArrayList的比较:LinkedList是采用双向循环链表实现的ListArrayList是采用变长数组算法实现的List

3).区别:ArrayList采用的数组形式保存对象,LinkedList基于链表的数据结构。对于随机访问getset方法,ArrayList优于LinkedList,因为LinkedList要移动指针;对于新增add和删除remove操作,LinkedList比较占优势,因为ArrayList要移动数据。

 

Vector的放过是线程同步的,是线程安全的,而ArrayList的方式不是,由于线程的同步必然影响性能,因此ArrayList的性能比Vector好;当VectorArrayList中的元素超过它的初始化大小时,Vector会将它的容量翻倍,而ArrayList只会增加50%,所以,ArrayList有利于节约内存空间。















你可能感兴趣的:([置顶] android开发经验笔记总计)