源码分析Mms--ConversationList短信主界面会话列表

ConversationList是短信很重要的一个Activity,单布局也很简单,就是一个ListActivity。

layout.conversation_list_screen

<RelativeLayout  xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"  >

    <ListView android:id="@android:id/list"
        style="?android:attr/listViewWhiteStyle"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/banner_sms_promo"
        android:drawSelectorOnTop="false"
        android:scrollbarStyle="insideOverlay"
        android:background="@android:color/white"
        android:cacheColorHint="@android:color/white"
        android:fadingEdgeLength="16dip" />

    <TextView android:id="@+id/empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="@string/loading_conversations"
        android:textAppearance="?android:attr/textAppearanceMedium" />

     <include layout="@layout/banner_sms_promo" />
</RelativeLayout>

layout.banner_sms_promp,貌似是给运营商做的需求,原生代码不确定有没有,当不是默认短信时,显示此View,点击可以设置为默认短信,这么一个功能,显示在ListView的上面。按照常规的写法,在RelativeLayout中,banner_sms_promo应该写在ListView之前的。

此empty TextView当ListView没有数据时显示的,还有一个功能是当加载列表比较慢的情况下,显示正在加载,但这个会话多的时候会覆盖在内容列表上面,后来的项目有提这个bug,后来改了就不提示正在加载了。

/**
 * This activity provides a list view of existing conversations.
 */
public class ConversationList extends ListActivity implements DraftCache.OnDraftChangedListener {<span style="font-family: Arial, Helvetica, sans-serif;">...</span>
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.conversation_list_screen);

        mSmsPromoBannerView = findViewById(R.id.banner_sms_promo);

        mQueryHandler = new ThreadListQueryHandler(getContentResolver());//查询使用的Handler,经常使用

        ListView listView = getListView();
        listView.setOnCreateContextMenuListener(mConvListOnCreateContextMenuListener);//长按事件,弹出来的菜单,在ActionBar位置显示,比之前的,长按弹出菜单好用
        listView.setOnKeyListener(mThreadListKeyListener);
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        listView.setMultiChoiceModeListener(new ModeCallback());

        // Tell the list view which view to display when the list is empty
        listView.setEmptyView(findViewById(R.id.empty));

        initListAdapter();

        setupActionBar();

        setTitle(R.string.app_label);

        mHandler = new Handler();
        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        boolean checkedMessageLimits = mPrefs.getBoolean(CHECKED_MESSAGE_LIMITS, false);
        if (DEBUG) Log.v(TAG, "checkedMessageLimits: " + checkedMessageLimits);
        if (!checkedMessageLimits) {
            runOneTimeStorageLimitCheckForLegacyMessages();
        }

        if (savedInstanceState != null) {
            mSavedFirstVisiblePosition = savedInstanceState.getInt(LAST_LIST_POS,
                    AdapterView.INVALID_POSITION);
            mSavedFirstItemOffset = savedInstanceState.getInt(LAST_LIST_OFFSET, 0);
        } else {
            mSavedFirstVisiblePosition = AdapterView.INVALID_POSITION;
            mSavedFirstItemOffset = 0;
        }
    }
<pre name="code" class="java">private final class ThreadListQueryHandler extends ConversationQueryHandler {//重点掌握

    private final class ThreadListQueryHandler extends ConversationQueryHandler {
        public ThreadListQueryHandler(ContentResolver contentResolver) {
            super(contentResolver);
        }
        @Override
        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
            switch (token) {
            case THREAD_LIST_QUERY_TOKEN:
                mListAdapter.changeCursor(cursor);
case UNREAD_THREADS_QUERY_TOKEN:
...break;
case HAVE_LOCKED_MESSAGES_TOKEN:
...break;

        @Override
        protected void onDeleteComplete(int token, Object cookie, int result) {
            super.onDeleteComplete(token, cookie, result);
            switch (token) {
            case DELETE_CONVERSATION_TOKEN:
...break;
case DELETE_OBSOLETE_THREADS_TOKEN:
… break;
}

======================================================================
public static class ConversationQueryHandler extends AsyncQueryHandler {…}
===========================================================================
package android.content;
/**
 * A helper class to help make handling asynchronous {@link ContentResolver}
 * queries easier.
 */
public abstract class AsyncQueryHandler extends Handler {
    private static final int EVENT_ARG_QUERY = 1;
    private static final int EVENT_ARG_INSERT = 2;
    private static final int EVENT_ARG_UPDATE = 3;
    private static final int EVENT_ARG_DELETE = 4;

//使用弱引用避免对象一直被引用而无法释放,导致内存泄露,WeakReference GC可以自动回收
    /* package */ final WeakReference<ContentResolver> mResolver;

    private static Looper sLooper = null;

    private Handler mWorkerThreadHandler;

//查询等操作需要的参数
    protected static final class WorkerArgs {
        public Uri uri;
        public Handler handler;
        public String[] projection;
        public String selection;
        public String[] selectionArgs;
        public String orderBy;
        public Object result;
        public Object cookie;
        public ContentValues values;
}

//使用Handler去查询等耗时的操作
    protected class WorkerHandler extends Handler {
        public WorkerHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
//mResolver.get()使用WeakReference
            final ContentResolver resolver = mResolver.get();
//如果mResolver.get()为空,表示对象已经回收了,此短信例子中对应的ConversationList Activity
            if (resolver == null) return;

            WorkerArgs args = (WorkerArgs) msg.obj;

            int token = msg.what;
            int event = msg.arg1;

            switch (event) {
                case EVENT_ARG_QUERY:
                    Cursor cursor;
                    try {
                        cursor = resolver.query(args.uri, args.projection,
                                args.selection, args.selectionArgs,
                                args.orderBy);
                        // Calling getCount() causes the cursor window to be filled,
                        // which will make the first access on the main thread a lot faster.
                        if (cursor != null) {
                            cursor.getCount();
                        }
                    } catch (Exception e) {
                        Log.w(TAG, "Exception thrown during handling EVENT_ARG_QUERY", e);
                        cursor = null;
                    }

                    args.result = cursor;
                    break;

                case EVENT_ARG_INSERT:
                    args.result = resolver.insert(args.uri, args.values);
                    break;

                case EVENT_ARG_UPDATE:
                    args.result = resolver.update(args.uri, args.values, args.selection,
                            args.selectionArgs);
                    break;

                case EVENT_ARG_DELETE:
                    args.result = resolver.delete(args.uri, args.selection, args.selectionArgs);
                    break;
            }

            // passing the original token value back to the caller
            // on top of the event values in arg1.
            Message reply = args.handler.obtainMessage(token);
            reply.obj = args;
            reply.arg1 = msg.arg1;

            if (localLOGV) {
                Log.d(TAG, "WorkerHandler.handleMsg: msg.arg1=" + msg.arg1
                        + ", reply.what=" + reply.what);
            }

            reply.sendToTarget();//返回给自己AsyncQueryHandler处理,因为StartQuery等方法WorkerArgs.handler传的是this
        }
    }

//构造函数
    public AsyncQueryHandler(ContentResolver cr) {
        super();
        mResolver = new WeakReference<ContentResolver>(cr);
        synchronized (AsyncQueryHandler.class) {
            if (sLooper == null) {
                HandlerThread thread = new HandlerThread("AsyncQueryWorker");
                thread.start();
//使用HandlerThread的Looper而不使用主线程的Looper就不会阻塞主线程
                sLooper = thread.getLooper();
            }
        }
        mWorkerThreadHandler = createHandler(sLooper);
    }

//暴露给外部调用
    public void startQuery(int token, Object cookie, Uri uri,
            String[] projection, String selection, String[] selectionArgs,
            String orderBy) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_QUERY;

        WorkerArgs args = new WorkerArgs();
        args.handler = this;//最后WorkerHandler处理的结果还是给自己的HandleMessage()处理
        args.uri = uri;
        args.projection = projection;
        args.selection = selection;
        args.selectionArgs = selectionArgs;
        args.orderBy = orderBy;
        args.cookie = cookie;
        msg.obj = args;
//最终给WorkerHandler,用ContentResolver查询等操作。
//这里为什么不直接查询完再调用onQueryComplete()方法,反而又加多一个WorkerHandler那么麻烦呢?如果这样,必然会阻塞主线程,因为外部调用此StartQuery的handler(即该AsyncQueryHandler的子类实例)是在主线程中执行的,而WorkerHandler使用的HanderThread就不会阻塞主线程。
        mWorkerThreadHandler.sendMessage(msg);
    }

    public final void cancelOperation(int token) {
        mWorkerThreadHandler.removeMessages(token);
}

    public final void startInsert(int token, Object cookie, Uri uri,
            ContentValues initialValues) {
        // Use the token as what so cancelOperations works properly
        Message msg = mWorkerThreadHandler.obtainMessage(token);
        msg.arg1 = EVENT_ARG_INSERT;

        WorkerArgs args = new WorkerArgs();
...
        mWorkerThreadHandler.sendMessage(msg);
    }

    public final void startUpdate(int token, Object cookie, Uri uri,
            ContentValues values, String selection, String[] selectionArgs) {...}

    public final void startDelete(int token, Object cookie, Uri uri,
            String selection, String[] selectionArgs) {...}

    protected Handler createHandler(Looper looper) {
        return new WorkerHandler(looper);
    }

protected void onQueryComplete(int token, Object cookie, Cursor cursor) {// Empty}
protected void onInsertComplete(int token, Object cookie, Uri uri) {// Empty}
protected void onUpdateComplete(int token, Object cookie, int result) {// Empty}
protected void onDeleteComplete(int token, Object cookie, int result) {// Empty}
    @Override
    public void handleMessage(Message msg) {
        WorkerArgs args = (WorkerArgs) msg.obj;

        if (localLOGV) {
            Log.d(TAG, "AsyncQueryHandler.handleMessage: msg.what=" + msg.what
                    + ", msg.arg1=" + msg.arg1);
        }

        int token = msg.what;
        int event = msg.arg1;

        // pass token back to caller on each callback.
        switch (event) {
            case EVENT_ARG_QUERY:
//回调给子类处理,父类AsynQueryHandler的onQueryComplete是空方法,把结果传回给子类处理想要的逻辑
                onQueryComplete(token, args.cookie, (Cursor) args.result);
                break;

            case EVENT_ARG_INSERT:
                onInsertComplete(token, args.cookie, (Uri) args.result);
                break;

            case EVENT_ARG_UPDATE:
                onUpdateComplete(token, args.cookie, (Integer) args.result);
                break;

            case EVENT_ARG_DELETE:
                onDeleteComplete(token, args.cookie, (Integer) args.result);
                break;
        }
    }
}

======================================================================
package android.os;

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */
//HandlerThread 是一个Thread不是Handler
public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;//使用自己的Looper而不是主线程的

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }
    
    /**
     * Constructs a HandlerThread.
     * @param name
     * @param priority The priority to run the thread at. The value supplied must be from 
     * {@link android.os.Process} and not from java.lang.Thread.
     */
    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }
    
    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();//不能漏,Looper.loop ()前先调用一次,prepare有new Looper。
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    
    /**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason is isAlive() returns false, this method will return null. If this thread 
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }
    
    /**
     * Ask the currently running looper to quit.  If the thread has not
     * been started or has finished (that is if {@link #getLooper} returns
     * null), then false is returned.  Otherwise the looper is asked to
     * quit and true is returned.
     */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }
    
    /**
     * Returns the identifier of this thread. See Process.myTid().
     */
    public int getThreadId() {
        return mTid;
    }
}

android.os.Looper 略

}//ConversationList class end

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/djyjava/article/details/48698897
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞