最容易引起误解的就是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()); } }