安卓四大组件Activities, Services, Broadcast receivers, Content providers是程序的核心模块。
源码在appfundamentals 目录里面。
1.Activities
Activities代表和用户交互的一个界面,比如一个邮箱程序会有一个展示列表的activity,会有一个建立新邮件的activity,还有一个读邮件的activity。虽然这些activities构成用户连贯的动作,但是每个activity之间是独立的,允许其它程序独立启动他们。比如一个相机应用可能会启动一个写邮件的activity来发送照片。
你必须实现Activity这个类,更多的介绍参考后面的文章。
2.Services
一个服务是运行在后台用户不可见的,它可以运行一个长时间的耗时操作,服务不会提供用户界面。比如启动一个服务去播放音乐,用户可以操作其它的应用,或者服务正在进行网络下载。其它部件,比如activity可以启动或者绑定一个服务。启动服务就是告诉系统,要保持这个服务一直运行,直到任务完成。
绑定服务的目的是允许其它部件调用服务的API接口进行通信,比如如果进程A绑定进程B,系统就会为A保持B的运行。因为这种灵活性,服务可以做很多系统层级的事情,比如动态壁纸,消息监听,输入法,辅助服务。
服务需要实现Service类,如果你是5.0以上的系统,可以使用JobScheduler类来分发服务。
举个IntentService的例子,activity通过
startService(new Intent(this, HelloIntentService.class));
启动服务,服务启动之后,弹出一个消息提示,过五秒钟之后服务停止,关闭消息提示。
@Override
protected void onHandleIntent(@Nullable Intent intent) {
showNotification();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
mNM.cancel(R.string.notification_title);
}
private void showNotification() {
Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_safari)
.setContentTitle(getString(R.string.notification_title));
mNM.notify(R.string.notification_title, builder.build());
}
3.Broadcast receivers
广播接收器允许程序响应系统的事件,即使程序现在没有处于运行状态。比如,一个程序定了一个闹钟在某个时间通知他要处理的事情,通过广播告知就不用程序一直运行。很多的广播都是来自于系统,比如屏幕关闭,电池电量低,获取到一张图片。程序也可以建立广播,比如一个程序可能通过广播告诉其它程序任务已经下载完成。
广播不会显示界面,但是可以创建一个状态栏消息告诉用户广播已经发送,通常来说,广播只需要处理很少量的工作,如果需要做大量的工作,可以使用JobScheduler来进行任务调度。
广播的实现很简单
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, intent.getStringExtra("data"), Toast.LENGTH_LONG).show();
}
}
并且在xml文件中注册
<receiver android:name=".appfundamentals.MyBroadcastReceiver">
<intent-filter>
<action android:name="com.android.guide.broadcast.MY_BROADCAST" />
</intent-filter>
</receiver>
就可以接收一个自定义的广播,然后发送一条消息提示。
4.Content providers
Content providers管理程序存储的数据,可以是手机的文件系统,可以是SQLite数据库,也可以是网络,或者其它的存储位置。通过providers其它应用在获得许可的条件下可以改变存储的内容。比如系统有管理联系人的provider, 其它程序在获取到权限之后就可以访问ContactsContract.Data数据。
可以将content provider想成数据库的一层抽象,但是对于系统来说,系统通过URI scheme来访问程序的数据。Content provider也可以用来保存程序的私有数据。
可以通过一个字典查询的例子来加深理解,在搜索框中输入字母,就会有相关的单词提示。
首先是建立数据库,需要继承
private static class DictionaryOpenHelper extends SQLiteOpenHelper {
}
,并且实现其中的onCreate, onUpgrade方法。
然后提供相关的查询语句,
public Cursor getWordMatches(String query, String[] columns) {
String selection = KEY_WORD + " MATCH ?";
String[] selectionArgs = new String[] {query+"*"};
return query(selection, selectionArgs, columns);
/* This builds a query that looks like:
* SELECT <columns> FROM <table> WHERE <KEY_WORD> MATCH 'query*'
* which is an FTS3 search for the query text (plus a wildcard) inside the word column.
*
* - "rowid" is the unique id for all rows but we need this value for the "_id" column in
* order for the Adapters to work, so the columns need to make "_id" an alias for "rowid"
* - "rowid" also needs to be used by the SUGGEST_COLUMN_INTENT_DATA alias in order
* for suggestions to carry the proper intent data.
* These aliases are defined in the DictionaryProvider when queries are made.
* - This can be revised to also search the definition text with FTS3 by changing
* the selection clause to use FTS_VIRTUAL_TABLE instead of KEY_WORD (to search across
* the entire table, but sorting the relevance could be difficult.
*/
}
通过文字过滤获取现在匹配的单词,完成数据库的底层实现。
其次写ContentProvider的实现,
* <p>The primary methods that need to be implemented are:
* <ul>
* <li>{@link #onCreate} which is called to initialize the provider</li>
* <li>{@link #query} which returns data to the caller</li>
* <li>{@link #insert} which inserts new data into the content provider</li>
* <li>{@link #update} which updates existing data in the content provider</li>
* <li>{@link #delete} which deletes data from the content provider</li>
* <li>{@link #getType} which returns the MIME type of data in the content provider</li>
* </ul></p>
需要实现以上的接口,供UI界面使用。
最后UI界面就会把相关的查询结果显示在手机上。
private void handleIntent(Intent intent) {
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
// handles a click on a search suggestion; launches activity to show word
Uri uri = intent.getData();
Cursor cursor = managedQuery(uri, null, null, null, null);
if (cursor == null) {
finish();
} else {
cursor.moveToFirst();
TextView word = (TextView) findViewById(R.id.word);
TextView definition = (TextView) findViewById(R.id.definition);
int wIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_WORD);
int dIndex = cursor.getColumnIndexOrThrow(DictionaryDatabase.KEY_DEFINITION);
word.setText(cursor.getString(wIndex));
definition.setText(cursor.getString(dIndex));
}
} else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
}
}
5.启动部件
三个部件可以通过一个异步的意图 (Intent)启动,意图在运行的时候会绑定独立的部件,你可以把意图想成一个需要其它部件响应的消息,可以发送精确的意图,指定哪个部件相应,也可以发送想要做某类操作的意图。
对于activities和services来说,意图可能携带一个URI信息,需要activity显示相关的图片,或者打开一个网页。有些情况会把意图当做返回结果,比如用户想要获取一个联系人,选择完联系人之后就会返回一个指定的联系人URI给程序。
对于广播来说,意图定义了广播的内容,比如一个告知设备电量低的意图可能就包含一个简单的字符串信息显示现在电量低。
Content providers和其它三者不同,不能通过意图激活,它是通过ContentResolver的请求来工作的。所以想要和content providers交互的部件可以通过ContentResolver完成交互。
Activity通过startActivity()或者startActivityForResult()启动。
对于5.0以上的系统,你可以通过JobScheduler启动,或者通过startService()和bindService()启动。
可以通过sendBroadcast(), sendOrderedBroadcast(), sendStickyBroadcast()的方式启动广播。
通过ContentResolver的query()函数访问数据库。