上述PreferenceActivity中内置了对SharedPreferences的支持。对于上层代码中的接入,实例如下;
android:lkey="window_animations"
...... />
接入:
private final static String WINDOW_ANIMATIONS_PREF = "window_animations";
mWindowAnimationsPref = (ListPreference)prefSet.findPreference(
WINDOW_ANIMATIONS_PREF);
mWindowAnimationsPref.setOnPreferenceChangeListener(this);
public boolean onPreferenceChange( Preference preference , Object objValue ){
if( preference == mWindowAnimationsPref ){
writeAniamtionPreference( 0, objValue );
}
}
若希望ListPreference保存或查看当前的选择,可调用ListPreference的方法:
public void setValue(String value); //对应android:entries属性的值
public void setValueIndex(int index); //对应android:entryValues属性的值
4.2 内部文件管理
对于二进制数据,android提供内部存储的方式,可将数据存储在应用的私有空间中,避免其他程序访问,内部存储的数据会在应用卸载时删除。
内部存储权限包括 MODE_PRIVATE, MODE_APPEND, MODE_WORLD_READABLE, MODE_WORLD_WRITEABLE 等。
内存存储所在目录为: \data\data\com.company.packagename\files.
4.2.1 写入数据
写入字符串:
FileOutputStream out = context.openFileOutput( file, Context.MODE_WORLD_WRITEABLE );
out.write( captureArray.toString().getBytes() );
out.close();
写入结构体数据的方法:
DataOutputStream out = new DataOutputStream( context.openFileOutput( PREFERENCES, MODE_PRIVATE ) );
out.writeUTF( configuration.locale );
out.writeInt( configuration.mcc );
out.writeInt( configuration.mnc );
4.2.2 读取数据
使用函数 openFileInput()实现
对于应用携带的静态数据,可放置在应用的 assets 目录或res raw目录下
对于assets目录下的静态数据,存在单个文件最大仅支持1MB的局限,读取方式如下:
InputStream is = getAssets().open("read_asset.txt");
对于res raw目录下的静态数据,可通过如下方式读取:
InputStream inputStream = resources.openRawResource( R.raw.definitions );
4.3 外部文件管理
即存储在外置SD卡上,可通过getExternalFilesDir()来获得具体路径,该具体路径依赖于应用的包名,其SD卡上的私有目录如下:
\mnt\sdcard\Android\data\com.miaozl.hello\files\
获得图片路径的方法:
File path = getExternalFilesDir( Environment.DIRECTORY_PAICTURES );
若希望存储在SD卡上的公共目录,可铜鼓 getExternalStoragePublicDirectory() 获得。注意,公共目录的具体路径视需要存储的文件类型而定。下面是获取公共目录的方法:
String sdroot = Environment.getExternalStorageDirectory().getAbsolutePath();
String dwndir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS ).gerAbsolutePath()
;
注意使用SD卡目录前,先通过 Environment.getExternalStorageState()方法判断SD卡是否已挂载。
4.4 数据库管理
android提供两种SQLite的实现方法: 基于android封装接口来实现的; 基于原生 SQLite 的方法来实现。
4.4.1 android封装接口
google提供的封装类 SQLiteOpenHelper, 通过继承
SQLiteOpenHelper来设计操作数据库。注意封装会使android的性能降低。
在继承SQLiteOpenHelper时,必须实现 onCreate() 和 onUpgrade()方法。下面是通过封装android接口设计数据库的步骤:
(1)定义数据库名,数据库版本号,数据表名
(2)实现 onCreate() 和 onUpgrade() 方法
(3)实现插入,更新,删除和查询方法
实现过程:
定义数据库名,数据库版本号,数据表名:
private static final String DATABASE_NAME = "notepad.db";
private static final int DATABASE_VERSION = 2;
private static final String NOTES_TABLE_NAME = "notes";
实现 onCreate() 和 onUpgrade() 方法 :
public void onCreate( SQLiteDatabase db ){
db.execSQL( "CREATE TABLE" + NOTES_TABLE_NAME + "("
+ NoteColumns._ID + " INTEGER PRIMARY KEY,"
+ NoteColumns.TITLE + " TEXT, "
+ NoteColumns.NOTE + " TEXT, "
+ NoteColumns..CREATED_DATE + " INTERGER,"
+ NoteColumns.MODIFIED_DATA + " INTEGER"
+ ");");
}
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion ){
db.execSQL("DROP TABLE IF EXISTS notes");
onCreate(db);
}
当一次性需要修改多个数据时,建议通过SQLite的书屋处理进行批量处理,有利于提高执行效率,事务处理相关的方法:
public void beginTransaction();
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener);
public void endTransaction();
public boolean inTransaction();
public void setTransactionSuccessful();
4.4.3 原生方法处理
采用 raw 方法来操作SQLite数据库,但必须做好SQL语句异常处理:
SQLiteDatabase db;
String args[] = { id };
ContentValues cv = new ContentValues();
cv.put("miaozl", id);
Cursor c = db.rawQuery( "SELECT * FROM table WHERE miaozl = ?", args );
if( c.getCount() != 0 ){
ContentValues cv = new ContentValues();
cv.put(" miaozl ", "cwj");
db.insert("table", " miaozl ", cv);
String args[] = {id};
ContentValues cv2 = new ContentValues();
cv2.put("miaozl", id);
db.delete("table", "miaozl = ?", args);
}
4.5 数据处理
第五章 android通信机制
android,通信技术涉及多个层面,在UI层涉及多种事件(触控事件,按键事件,轨迹球事件等);框架层涉及intent,message等。
5.1 intent 通信
通常情况下,intent仅能传递基本的数据类型,对于复杂的数据类型,则要借助Serializable, Parcelable 两个接口进行。
5.1.1 intent的常见用法
针对网络,地图,电话,消息,电子邮件,多媒体,系统等几个方面介绍Intent用法:
1. 网络相关
网络功能实现,需要拥有 android.permission.INTERNET 权限,与网络相关的intent通信包括显示网页,google地图,搜索等。
在由HTTP定义的与服务器交互的方法中,发起请求的方式有两种,即GET 和 POST, 其中GET方式通过URL提交数据,数据在URL中可以看到,无法保证私密性,且提交的数据最多只能为1024字节; 而 POST方式将数据放置在HTML HEADER中提交,且在数据长度上没有限制,通常用于传输敏感数据。
在Intent中,目前只支持GET方式,为了打开网站,其采用的ACTION 为 ACTION_VIEW,方法如下:
Uri uri = Uri.parse("http://www.163.com");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivitity(it);
如果传递敏感数据,在WebView中,采用的方法如下:
public void postUrl( String url, byte[] postData )
在网络中搜索相关的信息,实现方法如下:
intent.setAction( Intent.ACTION_WEB_SEARCH );
intent.putExtra(SearchManager.QUERY, "android123");
startActivity(intent);
2. 消息相关
为了查看消息,需有android.permission.READ_SMS权限,方法如下:
Intent it = new intent( Intent.ACTION_VIEW );
it.setType("vnd.android-dir/mms-sms");
startActivity(it);
为了发送短信,需有android.permission.WRITE_SMS权限,方法如下:
Uri uri = Uri.parse( "smsto:10010" );
Intent it = new Intent( Intent.ACTION_SENDTO, uri );
it.putExtra( "sms_body", "hello" );
startActivity(it);
发送彩信,还要涉及附件,下面是一个发送实例:
Uri uri = Uri.parse("content://media/external/images/media/10");
Intent it = new Intent( Intent.ACTION_SEND );
it.putExtra( "sms_body", "hello" );
it.putExtra(Intent.EXTRA_STREAM, uri);
it.setType("image/png");
startActivity(it);
3. 电子邮件相关
直接发送文字的邮件:
Intent it = new Intent( Intent.ACTION_SEND );
it.putExtra( Intent.EXTRA_TEXT, "EMAIL——内容" );
it.setType( "text/plain" );
startActivity( Intent.createChooser( it , "选择一个Email客户端" ) )
;
发送的时候有抄送人,方法如下;
it.putExtra( Intent.EXTRA_SUBJECT, "标题" );
it.setType( "message/rfc822" ); //编码类型
startActivity( Intent..createChooser( it, "选择一个Email客户端" ) );
带附件:
it.putExtra( Intent.EXTRA_STREAM, "file:///sdcard/miaozj.mp3" );
sendIntent.setType( "audio/mp3" );
4. 系统相关
卸载应用:
Uri uri = Uri.fromParts( "package", strPackageName, null );
Intent it = new Intent( Intent.ACTION_DELETE, uri );
startActivity( it );
安装应用:
Uri installUri = Uri.fromParts( "package" , "xxx", null );
return it = new Intent( Intent.ACTION_PACKAGE_ADDED, installUri );
创建快捷方式:
Intent shortcut = new Intent( "com.android.launcher.action.INSTALL_SHORTCUT" );
shortcut.putExtra( Intent.EXTRA_SHORTCUT_NAME, "shortcutname" );
shortcut.putExtra( "duplicate", false ); //不允许重复创建
ShortcutIconResource iconRes = Intent.ShortcutIconResource.fromContext( this, R.drawable.background );
shortcut.putExtra( Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconRes ); //资源图标
String action = "com.android.action.START";
Intent respondIntent = new intent( this, this.getClass() );
respondIntent.setAction( action );
shortcut.putExtra( Intent.EXTRA_SHORTCUT_INTENT, respondIntent );
sendBroadcast( shortcut );
删除快捷方式:
Intent shortcut = new Intent( "com.android.launcher.action.UNINSTALL_SHORTCUT" );
shortcut.putExtra( Intent.EXTRA_SHORTCUT_NAME, "shortcutname" );
String action = "com.android.action.START";
String appClass = this.getPackageName() + " . " + this.getLocalClassName();
ComponentName comp = new ComponentName( this.getPackageName(), appClass );//应用标识符
shortcut.putExtra( Intent.EXTRA_SHORTCUT_INTENT, new Intent(action).setComponent(comp) );
sendBroadcast( shortcut );
创建和删除快捷键的权限: com.android.launcher.permission.INSTALL_SHORTCUT ......UNINSTALL_SHORTCUT
5.1.2 Serializable 接口
Serializable 接口。要实现某个类支持序列化,可是该类继承Serializable接口。Serializable接口通常在Intent中使用。
Serializable 接口本质上是基于 ObjectOutStream 和 ObjectInputStream 对象进行的。具备Serializable序列化的类的实现非常简单。实例如下:
public final class CodeSigner implements Serializable{
private static final long serialVersionUID = 4648464646846846L;
}
serialVersionUID用于运行时,判断类版本的一致性,其可由JDK中的 serialver 工具生成,也可以采用默认的方式来定义。
在Intent中,可通过Bundle来设置序列化数据:
putSerializable( String key, Serializable value );
5.1.3 Parcelable接口
Parcelable接口是android特定的序列化接口,比Serializable接口有效,常用于Binder 和 AIDL场景中。
Parcelable接口序列化的数据可存储在Parcel中,继承Parcelable接口的类必须具有一个CREATOR的静态变量。下面是一个Parcelable接口序列化的示例:
public class CatCmdMessage implements Parcelable{
public CatCmdMessage(Parcel in){
mCmdDet = in.readParcelable(null);
mTextMsg = in.readParcelable(null);
mMenu = in.readParcelable(null);
mInput = in.readParcelable(null);
}
public void writeToParcel( Parcel dest, int flags ){
dest.writeParcelable(mCmdDet, 0);
dest.writeParcelable(mTextMsg, 0);
dest.writeParcelable(mMenu, 0);
dest.writeParcelable(mInput, 0);
}
public int describeContents(){
return 0;
}
//CREATOR的静态变量
public static final Parcelable.Creator CREATOR = new
Parcelable.Creator(){
public CatCmdMessage createFromParcel(Parcel in){
return new CatCmdMessage(in);
}
public CatCmdMessage[] newArray(int size){
return new CatCmdMessage[size];
}
};
}
通过Intent传递Parcelable接口序列化数据的方法如下:
public Intent putExtra(String name , Parcelable value );
另外,Parcelable也可以通过Bundle在Intent 中传递。在Bundle中设置Parcelable的方法如下:
public void putParcelable( String key, Parcelable value ); //Parcelable 方法
public Intent putExtra( String name, Bundle value ); //Intent方法
5.2 UI事件处理
主要包括事件监听器相关事件,事件句柄相关事件,焦点相关事件触控事件,按键事件,轨迹球时间等。UI事件均由View来实现的。
第六章 android多线程编程
在android中,UI主线程并非线程安全的,所有UI相关的操作均需在UI主线程中完成。在默认情况下,开发者创建的Service, Activity, Broadcast均运行在UI主线程中,一些耗时的操作,如网络下载,大文件读取,加解密计算,数据库操作等,也放在UI线程中执行,往往会阻塞线程,造成ANR异常。
android中,实现多线程的方式有,通过原生java线程,或android对java线程的封装及AsyncTask来实现多线程的效果。
6.1 java线程实现
1. 基于Thread的实现
基于Thread实现新线程和在Runnable构造器中实现新线程是传统java实现线程的两种方式,基于Thread的实现如下:
Static class AThread extends Thread{
ActivityManagerSerivce mService;
boolean mReady = false;
public AThread(){
super( "ActivityManager" );
}
public void run(){
Looper.prepare(); //初始化事件循环之后应调用loop()
android.os.Process.setThreadPriority( android.os.Process.THREAD_PRIORITY_FOREGROUND ); //设置线程优先级
android.os.Process.setCanSelfBackground( false );
ActivityManagerService m = new ActivityManagerService();
synchronized( this ){
mService = m;
notifyAll();
}
synchronized( this ){
while( !mReady ){
try{ wait();
}catch (InterruptedException e){ ........ }
}
}
Looper.loop();
}
}
在Thread声明周期中,存在NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED等多个状态。线程的生命周期和状态迁移过程如下:
2. 基于Runnable的实现
private Runnable mForceCheckBoxRunnable = new Runnable(){
public void run(){
if( mCheckBoxPreference != null ){
mCheckBoxPreference.setChecked( !mCheckBoxPreference.isChecked() );
}
mHandler.postDelayed( this, 1000 );
}
}
通过Handler的 removeCallbacks()方法可以移除待运行的线程,通过postAtTime()方法可以在特定的时间将线程放入消息队列。
3. 线程优先级
基于linux设置,用数字表示,范围-20~~19,其中-20为最高优先级,19为最低优先级。优先级的设置通常用于处理并发线程产生的阻塞。设置优先级的方法:
Process.setThreadPriority( priority );
6.2 android线程封装
对java线程进行了封装,最主要的工作包括 AsyncTask封装 和UI线程。
1. AsyncTask封装
AsyncTask通常用于后台线程和UI线程的交互。虽然AsyncTask本质上还是Thread加 Handler的一个封装,一个实例如下:
private class ReceivePushTask
extends AsyncTask
{
protected void doInBackground( Intent ... intents ){
Intent intent = intents[ 0 ];
}
public void onReceive(){ ... }
}
AsyncTask中最基本的两个方法doInBackground() 和 onReceive(), 前者执行真正的后台计算,后者向UI主线程反馈计算结果。为了使用AsyncTask,必须继承它。
其他重要方法:
public final AsyncTask
execute( Params...... params ); //执行AsyncTask
protected void
onPostExecute( Result result ); //在doInBackground()方法执行后执行,在UI线程中执行
protected void
onPreExecute(); //在执行doInBackground()方法前执行
protected final void
publishProgress( Progress... values ); //向UI线程反馈计算进度
AsyncTask可以支持多个输入参数甚至可变参数,其参数在execute()方法中传入。实际开发中,通常存在中断线程执行的情况,方法如下:
public final boolean
cancel
( boolean mayInterruptIfRunning );
此方法可能因为进程已经执行结束,已经被中断或其他原因而执行失败,判断AsyncTask是否已经被中断方法:
public final boolean
isCancelled();
在中断执行时,系统会调用AsyncTask的onCancelled()方法,开发者可在该方法中进行一些收尾工作,但要注意,必须显式判断线程是否中断。
2. 接入UI线程
在多线程编程中,执行后台计算的线程通常需要和UI线程进行交互,将计算结果反馈给UI线程,呈现给用户,传统的方法一般需要借助于Handler。 android,从activity,view和UI主线程3 个层次,提供了4中方式接入Ui线程。
(1)Activity的runOnUiThread(Runnable)方法
这个方法本质上是基于Handler的 post(Runnable r) 方法实现的。实例如下:
mActivity.runOnUiThread( new Runnable(){
public void run(){
......
}
} );
(2)View的 post(Runable)方法
View的 post(Runnable) 方法从控件层面对多线程提供了支持,实现原理与(1)类似,实现实例:
public void onClick( View v ){
new Thread( new Runnable(){
public void run(){
final Bitmap bitmap = loadImageFromNetwork( "http://example.com/image.png" );
mImageView.post( new Runnable()
{
public void run(){
mImageView.setImageBitmap( bitmap );
}
} );
}
} ).start();
}
(3)View 的 postDelayed(Runnable, long) 方法
View 的
postDelayed(Runnable, long)方法提供了延时处理的能力,实例如下:
final static int PAUSE_DELAY = 100;
Runnable uRunnable = new Runnable(){
public void run(){
if( mToggle ){
mCount++;
mView.onResume();
}else{
mView.onPause();
}
mToggle = !mToggle;
mView.postDelayed( mRunnable, PAUSE_DELAY );
}
}
(4)Handler 方法
利用Message消息向UI主线程发送后台计算的信息,及时将计算结果反馈给用户。当UI线程接收到Message消息后,会调用其Handler进行消息处理。Handler处理消息的过程如下:
Message message = mHandler.obtainMessage( ORIENTATION_CHANGED );
mHandler.sendMessage( message );
......
Handler mHandler = new Handler(){
public void handleMessage(Message msg){
switch( msg.what ){
case ORIENTATION_CHANGED:
......
break;
}
}
};
6.3 线程间的消息通信
相比Thread加Handler的封装,AsyncTask更为可靠,更易于维护。AsyncTask的缺点是,一旦线程开启即dobackground方法执行后无法向线程发送消息,仅能通过预先设置好的标记来控制逻辑,当然可以通过挂起线程并等待标志位的改变来进行通信。
下面主要介绍消息队列以及消息的分发和接收
6.3.1 消息队列
消息队列的维护是在线程中进行的,但默认除UI主线程外,其他线程并不拥有消息线程。为了维护一个消息队列,需要在线程中调用 Looper 的prepare() 方法进行初始化。为了使线程可以接收发送过来的消息,通常需要借助Handler来接收消息。一个维护消息队列的实例如下:
private class QueryRunner extends Thread{
public Handler mHandle;
public void run(){
Looper.prepare();//loop()方法应随后调用,实现维护消息队列的初始化
mHandler = new Handler(){
public void handleMessage( Message msg ){
//处理得到的消息
}
};
Looper.loop();//提供对消息的监听
}
}
通过Looper还可以判断当前线程是否为UI主线程,方法如下:
private void assertUiThread(){
if( !Looper.getMainLooper().equals( Looper.myLooper() ) ){
throw new RuntimeException( "not on the UI thread" );
}
}
通过查看Message的实现即可发现, Message继承了Parcelable,在消息的传递过程中,实际上传递的是序列化数据。Message的实现如下:
public final class Message implements Parcelable{
public int whatl; //消息标识符
public int arg1; //传递参数
public int arg2; //传递对象
public Object obj; //传递的对象
public Messenger replyTo;
long when;
Bundle data;
Handler target; //目标Handler
Runnable callback;
Message next;
private static final int MAX_POOL_SIZE = 10; .......
}
源码显示,一个线程会自动维护一个消息池,该消息池的大小为10 。 在需要生成消息时,首先从消息池中获取信息,只有当消息池中的消息均被使用时,才会重新创建新消息,当消息使用结束时,消息池会回收消息,实现过程如下:
private static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0; // 消息池当前的大小
private static final int MAX_POOL_SIZE = 10; //消息池极限
从消息池中获取消息:
public static Message obtain(){
Synchronized( sPoolSync ){
if( sPool != null ){
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message
}
从上可见,在发送消息时,通过obtain()方法获得Message比创建Message的效率更高,发送消息的实例如下:
pubic void progress( boolean progress ){
android.os.Message msg = android.os.Message.obtain();
msg.what = MSG_PROGRESS;
msg.arg1 = progress ? 1 : 0;
sendMessage( msg );
}
向消息池中回收消息的实现过程:
public void recycle(){
synchronized( sPoolSync ){
if( sPoolSize < MAX_POOL_SIZE ){
clearForRecycle();
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
6.3.2 消息分发
消息的分发和接收均与Handler密切相关,对于消息的分发,android目前提供了两种消息类型,一种是 post 消息,一种是 send 消息。 其中 post 消息会在未来某一时间加入消息队列,而 send 消息则会立即加入到消息队列。
1. send 消息
分发一个简单的 send 消息的方法如下:
Message msg = mHandler.obtainMessage( KILL_APPLICATION_MSG );
msg.arg1 = uid;
msg.arg2 = 0;
msg.obj = pkg;
mHandler.sendMessage( msg );
如果没有消息需要传递,那么可以发送仅携带消息标示符的空消息,方法:
mHandler.sendEmptyMessage( CANCEL_HEAVY_NOTIFICATION_MSG );
2. post 消息
通过 post 消息可以实现类似循环计算的功能,方法如下:
mTicker = new Runnable(){
public void run(){
if( mTickerStopped ) return;
mCalender.setTimeInMillis( System.currentTimeMillis() );
setText( DateFormat.format( mFormat, mCalendar ) );
invalidate();
long now = SystemClock.uptimeMillis();
long next = now + (1000 - now%1000);
mHandler.postAtTime(mTicker, next); //间隔一段时间后,再调用线程
}
};
mTicker.run();
6.3.3 消息接收
消息接收相对简单,如果存在重要问题,应注意数据的同步,Handler中消息接收的过程:
public void dispatchMessage( Message msg ){
if( msg.callback != null ){
handleCallback( msg ); //通过Message自身回调进行处理
}else{
if( mCallback != null ){ //通过当前线程回调进行处理
if( mCallback.handleMessage(msg) ){
return;
}
}
handleMessage(msg);//Handler自己处理
}
}
对于消息队列而言,在Looper 的loop()方法中执行对消息的监听。 loop()方法的实现如下:
public static final void loop(){
Looper me = myLooper();
MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();
final long ident = Bindler.clearCallingIdentity();
if( msg != null ){
if(msg.target == null){ return; }
msg.target.dispatchMessage(msg);//调用Handler进行消息处理
msg.recycle();//消息回收
}
}
6.4 线程安全处理
线程安全多是由多线程对共享资源的访问引起的。在线程安全层面,android更多采用java的实现,除了java的join(), wait(), sleep(), notify()等方法和synchronized关键字外,还有java的并发库。
6.4.1 synchronized同步
在android应用层,线程的并发很多是靠 synchronized 关键字来实现的,通过 synchronized 关键字,可以实现方法和语句块的同步。同步的本质是对特定对象的加锁,他们可以是来自调用方法的对象,也可以是开发这自己的对象。
另外,synchronized 关键字并不能继承,对于父类的同步方法,在子类中必须再次显式声明才能成为同步方法。
synchronized 关键字的局限在与 在试图获得锁时,无法设定超时和中断,每个所只有一个条件,在某些场景下可能不够用。
synchronized 关键字可以是实现方法同步和语句块同步。
1. 方法同步
方法同步分一般方法同步和静态方法同步。一般方法同步的本质在于将synchronized关键字过用于对象引用,作用域仅限类的单个对象。实例:
public synchronized void setMax(int max){
super.setMax(max); ......
}
等同于:
public void setMax(int max){
synchronized(this){
super.setMax(max); ......
}
}
静态方法同步的本质是将类本身作为锁,其作用域是该类的所有对象。下面是BitmaoManager中利用synchronized实现单子模式的过程:
private static BitmapManager sManager = null;
public static synchronized BitmapManager instance(){
if( sManager == null ){
sManager = new BitmapManager();
}
return sManager;
}
2. 语句块的同步
当需要同步的范围不是很大时,可以采用语句块同步,下面是一个实例:
private void setOrientation( int orientation ){
synchronized(this){ ...... }
}
将对象本身作为锁,显然影响并发效率,更灵巧的是设计自定义锁。 自定义锁可以是任何类型的对象,但通常将其类型设计为Object。实例如下:
public abstract class AbstractPreferences extends Preferences
{
protected final Object lock;
protected
AbstractPreferences getChild(String name) throws BackingStoreException{
synchronized(lock){ ...... }
}
}
6.4.2 RPC通信
6.4.3 SQLite调用
对于SQLite的调用,可能会存在多处同时执行读写操作的场景,这种场景也需要考虑线程的安全性。android提供了类似AysncTask的 AsyncQueryHandler 方法来解决这一问题。将耗时的查询等操作放置非UI主线程中,操作结束后,通过Handler 调用响应的UI主线程的方法处理操作执行的结果。