您的位置:首页 > 移动开发 > Android开发

android核心技术与最佳实践笔记(二)

2016-12-14 14:35 507 查看
第四章 深入解析Android数据存储与管理

4.1 xml文件管理

xml文件更多的是用来维护应用或系统的配置文件。SharedPreferences这个轻量级的类作为xml文件存储的上层接口。本质为<key, value>

根据配置信息是否对外开放,SharedPreferences提供了 MODE_PRIVATE, MODE_WORLD_READABLE两种权限,SharedPreferences的操作分为获取配置信息和存储配置信息两种。下面是获取配置信息方法:

SharedPreferences
settings = getSharedPreferences( PREFS_NAME, MODE_PRIVATE );


boolean silent = settings.getBoolean( "silentMode" , false );

存储配置信息方法:

SharedPreferences
settings = getSharedPreferences( PREFS_NAME, MODE_PRIVATE );


SharedPreferences.Editor editor = settings.edit();

editor.putBoolean( "silentMode" , mSilentMode );

editor.commit();

若希望配置信息对其他应用程序开发,在设置权限时,可使用 MODE_WORLD_READABLE 。在其他应用获得相应的配置信息时,必须先获得相应的上下文,方法如下:

context = createPackageContext( "com.miaozl.text" , Context.CONTENXT_IGNORE_SECURITY );

if(context != null){

SharedPreferences settings = context.getSharedPreferences(PREFS_NAME, Context.MODE_WORLD_READABLE);

mTest = settings.getString( "test", null );

}

上述PreferenceActivity中内置了对SharedPreferences的支持。对于上层代码中的接入,实例如下;

<ListPreference

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_EMAIL, "KSK@163.com" );

it.putExtra( Intent.EXTRA_TEXT, "EMAIL——内容" );

it.setType( "text/plain" );

startActivity( Intent.createChooser( it , "选择一个Email客户端" ) );

发送的时候有抄送人,方法如下;

it.putExtra( Intent.EXTRA_CC, "ASDJF@163.com" );//抄送人

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<CatCmdMessage> CREATOR = new

Parcelable.Creator<CatCmdMessage>(){

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<Intent , Void, Void> {

protected void doInBackground( Intent ... intents ){

Intent intent = intents[ 0 ];

}

public void onReceive(){ ... }

}

AsyncTask中最基本的两个方法doInBackground() 和 onReceive(), 前者执行真正的后台计算,后者向UI主线程反馈计算结果。为了使用AsyncTask,必须继承它。

其他重要方法:

public final AsyncTask<Params, Progress, Result> 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主线程的方法处理操作执行的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: