深入Android線程的相關(guān)問題解惑
在默認(rèn)情況下,更確切的講一個(gè)進(jìn)程中只有一個(gè)線程,這跟其他語(yǔ)言,比如C/C++,Java等是一致。也就是說在Android應(yīng)用程序里面一個(gè)進(jìn)程只有一個(gè)線程,所有組件都運(yùn)行在一個(gè)線程里面!
當(dāng)應(yīng)用程序啟動(dòng)時(shí),系統(tǒng)會(huì)為其創(chuàng)建一個(gè)進(jìn)程,也會(huì)創(chuàng)建一個(gè)線程名字叫做main,所有其所屬組件的創(chuàng)建,系統(tǒng)事件的處理,系統(tǒng)的回調(diào)等一切應(yīng)用相關(guān)的事情都運(yùn)行在此名叫main的線程中。此線程即為常說的主線程(main thread)。俗稱的UI線程(UI thread)也是它,因?yàn)橹挥兄骶€程可以操作UI相關(guān)的事情,所以有人把主線程也稱作UI線程,但這并不是正確的說法,因?yàn)镾ervice所屬的線程也可以操作Toast,但是Service并沒有UI。為什么非主線程不能操作UI呢?因?yàn)閷?duì)UI操作常常會(huì)引發(fā)系統(tǒng)的回調(diào),所以如果允許第三線程來操作可能會(huì)引發(fā)系統(tǒng)回調(diào)的紊亂,進(jìn)而會(huì)打亂整個(gè)框架的時(shí)序!
這里要特別注意的就是同一個(gè)進(jìn)程中的所有組件運(yùn)行在同一個(gè)線程中,Activiy,Service,BoradcastReceiver和ContentProvider都運(yùn)行在主線程中。
最容易引起誤解的就是Service,文檔和常識(shí)都會(huì)認(rèn)為Service是放在后臺(tái)用于操作費(fèi)時(shí)運(yùn)算的,但是實(shí)則不然,如果你在Service中做費(fèi)時(shí)操作,同樣會(huì)引發(fā)臭名昭著的ANR(Application Not Responding)。所以如果想把Service當(dāng)做一個(gè)Server,必須在Service用HandlerThread或Thread創(chuàng)建一個(gè)Worker線程!
Activity也是一樣的,你startActivity()后,開啟了一個(gè)新的Activity,但它們都運(yùn)行在同一個(gè)線程中,所以你還是不能在原Activity中做費(fèi)時(shí)操作!也即在調(diào)用startActivity()開啟了一個(gè)新的Activity后,或者在onPause(), onStop(), onDestroy()中做費(fèi)時(shí)操作會(huì)引發(fā)ANR。
對(duì)于ContentProvider也是一樣的,如果跟其他組件在同一進(jìn)程內(nèi),那么調(diào)用ContentResolver的方法會(huì)相當(dāng)于直接調(diào)用ContentProvider的方法。如果是在另外一個(gè)進(jìn)程中,雖是通過IPC,但也是同步的,因?yàn)镮Binder的同步的,也即調(diào)用ContentResolver時(shí)會(huì)把調(diào)用者的進(jìn)程掛起,等待ContentProvider的進(jìn)程操作結(jié)束,再把結(jié)果傳給調(diào)用者進(jìn)程!所以,如果ContentProvider中有費(fèi)時(shí)操作,或者會(huì)同步鎖數(shù)據(jù)庫(kù)等,也一定要注意ANR的發(fā)生!
所以一定要記住:一個(gè)進(jìn)程只有一個(gè)線程,所有組件都運(yùn)行在主線程中。
因此,如果有費(fèi)時(shí)操作,必須要?jiǎng)?chuàng)建Worker線程!
下面有一個(gè)小實(shí)例,一個(gè)應(yīng)用中有五個(gè)組件:2個(gè)Activity,一個(gè)Service,一個(gè)ContentProvider和一個(gè)BroadcastReceiver。在每個(gè)組件的方法中都有打印所屬線程信息,另外對(duì)于Activity,Service和ContentProvider如果做費(fèi)時(shí)操作會(huì)引發(fā)ANR,對(duì)于BroadcastReceiver更是如此,這個(gè)大家都懂得的!
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());
}
}
- android開發(fā)教程之子線程中更新界面
- android開發(fā)教程之handle實(shí)現(xiàn)多線程和異步處理
- android使用handler ui線程和子線程通訊更新ui示例
- android使用handlerthread創(chuàng)建線程示例
- android使用多線程更新ui示例分享
- Handler與Android多線程詳解
- 淺析android中的線程封裝
- android中多線程下載實(shí)例
- Android多線程及異步處理問題詳細(xì)探討
- Android多線程處理機(jī)制中的Handler使用介紹
- Android中加載網(wǎng)絡(luò)資源時(shí)的優(yōu)化可使用(線程+緩存)解決
- Android開發(fā)筆記之:如何安全中止一個(gè)自定義線程Thread的方法
- Android 在其他線程中更新UI線程的解決方法
- 深入Android Handler與線程間通信ITC的詳解
- Android開發(fā)筆記之:深入理解多線程AsyncTask
- Android Handler主線程和一般線程通信的應(yīng)用分析
- android 多線程技術(shù)應(yīng)用
- android開發(fā)教程之使用線程實(shí)現(xiàn)視圖平滑滾動(dòng)示例
相關(guān)文章
使用RecyclerView實(shí)現(xiàn)瀑布流高度自適應(yīng)
這篇文章主要為大家詳細(xì)介紹了使用RecyclerView實(shí)現(xiàn)瀑布流高度自適應(yīng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Android Studio與SVN版本控制程序的協(xié)作使用指南
這篇文章主要介紹了Android Studio與SVN版本控制程序的協(xié)作使用指南,使用Gradle插件自動(dòng)填寫SVN號(hào)并發(fā)布到指定目錄的方法,需要的朋友可以參考下2016-03-03Android 三種實(shí)現(xiàn)定時(shí)器詳解及實(shí)現(xiàn)方法
本文主要介紹 Android 定時(shí)器的知識(shí)資料,這里整理了三種方法來實(shí)現(xiàn)定時(shí)器的方法,有需要的小伙伴可以參考下2016-09-09Android App支付系列(二):支付寶SDK接入詳細(xì)指南(附官方支付demo)
本篇文章介紹了Android App支付系列(二):支付寶SDK接入詳細(xì)指南(附官方支付demo) ,有興趣的同學(xué)可以了解一下。2016-11-11Android使用第三方服務(wù)器Bmob實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼
這篇文章主要介紹了Android使用第三方服務(wù)器Bmob實(shí)現(xiàn)發(fā)送短信驗(yàn)證碼的思路詳解,需要的朋友可以參考下2016-09-09Android Path繪制貝塞爾曲線實(shí)現(xiàn)QQ拖拽泡泡
本文主要介紹Android Path繪制貝塞爾曲線,這里整理相關(guān)資料并運(yùn)用貝塞爾曲線實(shí)現(xiàn)QQ拖拽泡泡的示例,有興趣的小伙伴可以參考下2016-09-09Android中RecyclerView實(shí)現(xiàn)橫向滑動(dòng)代碼
這篇文章主要介紹了Android中RecyclerView實(shí)現(xiàn)橫向滑動(dòng)代碼的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07Android Insets相關(guān)知識(shí)總結(jié)
這篇文章主要介紹了Android Insets相關(guān)知識(shí)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Android,感興趣的朋友可以了解下2021-03-03Android WebView使用方法詳解 附j(luò)s交互調(diào)用方法
這篇文章主要為大家詳細(xì)介紹了Android WebView使用方法詳解,文中附j(luò)s交互調(diào)用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05Android實(shí)現(xiàn)抽獎(jiǎng)轉(zhuǎn)盤實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)抽獎(jiǎng)轉(zhuǎn)盤實(shí)例代碼,可以應(yīng)用于Android游戲開發(fā)中的一個(gè)應(yīng)用,需要的朋友可以參考下2014-07-07