一个应用只有一个线程
在默认情况下,更确切的讲一个进程中只有一个线程,这跟其他语言,比如C/C++,Java等是一致。也就是说在Android应用程序里面一个进程只有一个线程,所有组件都运行在一个线程里面!
当应用程序启动时,系统会为其创建一个进程,也会创建一个线程名字叫做main,所有其所属组件的创建,系统事件的处理,系统的回调等一切应用相关的事情都运行在此名叫main的线程中。此线程即为常说的主线程(main thread)。俗称的UI线程(UI thread)也是它,因为只有主线程可以操作UI相关的事情,所以有人把主线程也称作UI线程,但这并不是正确的说法,因为Service所属的线程也可以操作Toast,但是Service并没有UI。为什么非主线程不能操作UI呢?因为对UI操作常常会引发系统的回调,所以如果允许第三线程来操作可能会引发系统回调的紊乱,进而会打乱整个框架的时序!
这里要特别注意的就是同一个进程中的所有组件运行在同一个线程中,Activiy,Service,BoradcastReceiver和ContentProvider都运行在主线程中。
最容易引起误解的就是Service,文档和常识都会认为Service是放在后台用于操作费时运算的,但是实则不然,如果你在Service中做费时操作,同样会引发臭名昭著的ANR(Application Not Responding)。所以如果想把Service当做一个Server,必须在Service用HandlerThread或Thread创建一个Worker线程!
Activity也是一样的,你startActivity()后,开启了一个新的Activity,但它们都运行在同一个线程中,所以你还是不能在原Activity中做费时操作!也即在调用startActivity()开启了一个新的Activity后,或者在onPause(), onStop(), onDestroy()中做费时操作会引发ANR。
对于ContentProvider也是一样的,如果跟其他组件在同一进程内,那么调用ContentResolver的方法会相当于直接调用ContentProvider的方法。如果是在另外一个进程中,虽是通过IPC,但也是同步的,因为IBinder的同步的,也即调用ContentResolver时会把调用者的进程挂起,等待ContentProvider的进程操作结束,再把结果传给调用者进程!所以,如果ContentProvider中有费时操作,或者会同步锁数据库等,也一定要注意ANR的发生!
所以一定要记住:一个进程只有一个线程,所有组件都运行在主线程中。
因此,如果有费时操作,必须要创建Worker线程!
实例
下面有一个小实例,一个应用中有五个组件:2个Activity,一个Service,一个ContentProvider和一个BroadcastReceiver。在每个组件的方法中都有打印所属线程信息,另外对于Activity,Service和ContentProvider如果做费时操作会引发ANR,对于BroadcastReceiver更是如此,这个大家都懂得的!
public class ActivityDemo extends Activity {
private static final String TAG = "ActivityDemo";
private Handler mMainHandler = new Handler(new Handler.Callback() {
public boolean handleMessage(Message msg) {
dumpThreadInfo();
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
dumpThreadInfo();
super.onCreate(savedInstanceState);
// add four buttons
LinearLayout layout = new LinearLayout(getApplication());
layout.setOrientation(LinearLayout.VERTICAL);
Button startService = new Button(getApplication());
startService.setText("Start a Service");
startService.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(getApplication(), ServiceDemo.class);
startService(i);
}
});
layout.addView(startService);
Button startAnother = new Button(getApplication());
startAnother.setText("Start another Activity");
startAnother.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent(getApplication(), AnotherActivity.class);
startService(i);
}
});
layout.addView(startAnother);
Button startContentProvider = new Button(getApplication());
startContentProvider.setText("Start a ContentProvider");
startContentProvider.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
getContentResolver().query(ContentProviderDemo.CONTENT_URI, null, null, null, null);
}
});
layout.addView(startContentProvider);
Button startReceiver = new Button(getApplication());
startReceiver.setText("Start a BroadcastReceiver");
startReceiver.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent i = new Intent("android.action.start_broadcastreceiver_demo");
sendBroadcast(i);
}
});
layout.addView(startReceiver);
setContentView(layout);
mMainHandler.sendEmptyMessageDelayed(0, 500);
}
public void dumpThreadInfo() {
Thread.dumpStack();
Log.e(TAG, Thread.currentThread().toString());
Log.e(TAG, " " + getMainLooper());
}
}
public class AnotherActivity extends Activity {
private static final String TAG = "AnotherActivity";
private Handler mMainHandler = new Handler(getMainLooper(), new Handler.Callback() {
public boolean handleMessage(Message msg) {
// this will cause ANR
Log.e(TAG, "you know what this is very slow slow slow slow");
SystemClock.sleep(20 * 1000);
dumpThreadInfo();
return false;
}
});
@Override
protected void onCreate(Bundle savedInstanceState) {
dumpThreadInfo();
super.onCreate(savedInstanceState);
setTitle("this is another activity");
mMainHandler.sendEmptyMessageDelayed(0, 500);
}
@Override
protected void onDestroy() {
dumpThreadInfo();
super.onDestroy();
}
public void dumpThreadInfo() {
Thread.dumpStack();
Log.e(TAG, Thread.currentThread().toString());
Log.e(TAG, " " + getMainLooper());
}
}
public class ServiceDemo extends Service {
private Handler mMainHandler = new Handler(new Handler.Callback() {
public boolean handleMessage(Message msg) {
// this will cause ANR, too
Log.e(TAG, "this is very slow you know, slow slow");
SystemClock.sleep(20 * 1000);
dumpThreadInfo();
return false;
}
});
private static final String TAG = "ServiceDemo";
@Override
public IBinder onBind(Intent arg0) {
dumpThreadInfo();
return null;
}
@Override
public void onCreate() {
dumpThreadInfo();
super.onCreate();
mMainHandler.sendEmptyMessageDelayed(0, 500);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
dumpThreadInfo();
return super.onStartCommand(intent, flags, startId);
}
public void dumpThreadInfo() {
Thread.dumpStack();
Log.e(TAG, Thread.currentThread().toString());
Log.e(TAG, " " + getMainLooper());
}
}
public class ContentProviderDemo extends ContentProvider {
public static final Uri CONTENT_URI = Uri.parse("content://com.hilton.effectiveandroid.app/content");
private static final String TAG = "ContentProviderDemo";
@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
dumpThreadInfo();
return 0;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
dumpThreadInfo();
return null;
}
@Override
public boolean onCreate() {
dumpThreadInfo();
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
dumpThreadInfo();
// it will cause ANR of course
Log.e(TAG, "this is very slow, you know that");
SystemClock.sleep(20 * 1000);
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
dumpThreadInfo();
return 0;
}
public void dumpThreadInfo() {
Thread.dumpStack();
Log.e(TAG, Thread.currentThread().toString());
}
@Override
public String getType(Uri arg0) {
return null;
}
}
public class BroadcastReceiverDemo extends BroadcastReceiver {
private static final String TAG = "BroadcastReceiverDemo";
@Override
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "intent is " + intent);
dumpThreadInfo();
}
public void dumpThreadInfo() {
Thread.dumpStack();
Log.e(TAG, Thread.currentThread().toString());
}
}