2013年5月1日 星期三

筆記,android的廣播註冊和監聽方式;使用CursorLoader監聽其狀態


這篇文章可能會有點多,但是因為這兩個禮拜可能會很忙,
所以先留著接口(interface?哈哈)之後補。

首先這是廣播註冊、送出與監聽的方法,專案檔下載在這裡
貼上主要的程式碼:

package com.example.broadcastTest;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

/** 測試broadcast */
public class MainActivity extends Activity {
    final String TAG = getClass().getSimpleName();
    Button button;
    TextView textView;
    // 這裡用static 是為了thread用途,建議如果夠大的話,用Service包起來
    static boolean threadIsStop;
    final String ACTION_BROAD_CAST = "susanTestYAHU";
    final String KEY_INTENT_SIMPLE = "intent用的key值";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.text_show);
        button = (Button) findViewById(R.id.button1);
        // 一個開關的設置

        if (threadIsStop) {
            button.setText(R.string.lb_switch_turnOn);
        } else {
            button.setText(R.string.lb_switch_turnOff);
        }
        button.setTag(threadIsStop);
        button.setOnClickListener(mOnClickListener);

    }

    OnClickListener mOnClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {

            boolean onThread = (Boolean) button.getTag();
            if (onThread) {
                // 關閉程序
                threadIsStop = true;
                button.setTag(false);
                button.setText(R.string.lb_switch_turnOn);
            } else {
                // 開啟程序
                button.setText(R.string.lb_switch_turnOff);
                threadIsStop = false;
                startThread();
                // FIXME 這個demo可能會有thread不同步的問題,不過不是主要的目的所以不管
                button.setTag(true);
            }// end if
        }// end onClick
    };

    @Override
    protected void onResume() {
        // 註冊監聽器
        IntentFilter mIntentFilter = new IntentFilter();
        mIntentFilter.addAction(ACTION_BROAD_CAST);
        registerReceiver(mBroadcastReceiver, mIntentFilter);
        super.onResume();
    }

    @Override
    protected void onPause() {
        /*
         * 如果使用broadcast receiver來改變UI內容,則必須要註冊在onResume和onPuase中
         * 如果需要註冊全域,可以寫在Manifest中, 可參考:http://yangguangfu.iteye.com/blog/1063732
         * 裡面的"系統註冊"。其中也有更多的詳細介紹。
         */
        unregisterReceiver(mBroadcastReceiver);
        super.onPause();
    }

    void startThread() {

        new Thread(new Runnable() {

            @Override
            public void run() {
                Log.d(TAG, "thread啟動");
                long count = 0;
                while (!threadIsStop) {
                    // 開啟程序,定時送出一個broadcast
                    Intent intent = new Intent();
                    /* 注意送出的Action要和接收的一樣 */
                    intent.setAction(ACTION_BROAD_CAST);
                    // 填入值
                    intent.putExtra(KEY_INTENT_SIMPLE, count);
                    // 送出廣播
                    sendBroadcast(intent);
                    Log.d(TAG, "sendBroadcast count: " + count);
                    count++;

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }// end try
                }// end while
            }// end run
        }).start();

    };

    /** 廣播的監聽器 */
    BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(ACTION_BROAD_CAST)) {
                /*
                 * 查Action,意味著這邊可以同時接收許多Action,
                 * 但是在register的時候也必須要填入filiter之內,要不根本進不來
                 */

                long value = intent.getLongExtra(KEY_INTENT_SIMPLE, 0);
                textView.setText("value notify: " + value);

            }
        }

    };

    @Override
    protected void onDestroy() {
        /*
         * 這裡設定當activity結束時thread也應該結束,如果是包在Service裡面寫就可以追蹤
         * 參考:http://androidsusan.blogspot.tw/2013/03/service.html
         */
        threadIsStop = true;
        super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        // getMenuInflater().inflate(R.menu.activity_main, menu);
        return false;
    }

}


其實這沒什麼好講的,只是要注意在註冊broadcast receiver的方法也可以在manifest中做掉,就是差別在全域的註冊和區域的註冊哩。
動態和靜態的註冊可以參考這裡,不過這篇有點難讀,請慢慢服用。

------------我是不想要換篇新文章的分隔線------------

再來就是CursorLoader監聽其狀態的作法,專案下載在這裡
專案實作了獲取手機聯絡人相關訊息,如果要實作必須要注意它有權限必須登入。

主要是使用了LoaderCallbacks<Cursor>物件,這裡必須使用getLoaderManager()獲得LoaderManager物件,呼叫initLoader()初始化LoaderCallbacks物件,然後就可以在其中實作。

注意這是3.0以後才有的東西,話說我看了一下很久以前上架的程式,3.x的裝置幾乎沒有(沒有顯示?)超過2.3.x版本的裝置大概都超過了七成,可能樣本數不是很足夠,不過現在開發似乎都不是很想開發3.0以下的版本了呀...

關於LoaderManager的部分,這裡有一些介紹,
如果你看英文會比比看中文還要快樂,可以略為參考這裡

主要程式碼:

package com.example.cursorloader;

import android.app.Activity;
import android.app.LoaderManager;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.ProgressDialog;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.Contacts.Photo;
import android.util.Log;
import android.view.Menu;
import android.widget.TextView;

/** {@link CursorLoader}監聽的方法 */
public class MainActivity extends Activity {
    final String TAG = getClass().getSimpleName();
    TextView textView, tx_info;

    ProgressDialog mProgressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.textView);

        tx_info = (TextView) findViewById(R.id.TextView_info);
        tx_info.setText("還沒開始!");
        // 設定轉轉轉
        mProgressDialog = new ProgressDialog(MainActivity.this);
        mProgressDialog.setMessage("Please Wait..");
        mProgressDialog.setCancelable(false);
    }

    LoaderCallbacks<Cursor> mLoaderCallbacks = new LoaderCallbacks<Cursor>() {
        // 我們可以實作一個新的callback來實作更完整的呼叫方法
        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            Log.d(TAG, "獲取cursor " + id);
            CursorLoader mCursorLoader;
            // 設定Cursor搜尋條件,這邊可以在建構子時操作
            mCursorLoader = new CursorLoader(MainActivity.this);
            mCursorLoader.setUri(Phone.CONTENT_URI);
            String[] PHONES_PROJECTION = new String[] { Phone.DISPLAY_NAME,
                    Phone.NUMBER, Photo.PHOTO_ID, Phone.CONTACT_ID };
            mCursorLoader.setProjection(PHONES_PROJECTION);
            // 創立就馬上執行
            mCursorLoader.loadInBackground();
            return mCursorLoader;
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor mCursor) {
            // 這個介面的意思是如果在onCreateLoader()中創立的loader結束時在這裡就會有反應。
            if (mProgressDialog.isShowing()) {
                mProgressDialog.dismiss();
            }
            tx_info.setText("Complete!!");

            // 結束後的動作
            Log.d(TAG, "獲取cursor" + mCursor);
            if (mCursor != null) {
                Log.d(TAG, "cursor count: " + mCursor.getCount());
                if (mCursor.getCount() > 0) {
                    StringBuffer mBuffer = new StringBuffer();
                    while (mCursor.moveToNext()) {
                        // 獲得名字
                        mBuffer.append(mCursor.getString(mCursor
                                .getColumnIndex(Phone.DISPLAY_NAME)));
                        mBuffer.append("\n");
                        mBuffer.append(mCursor.getString(mCursor
                                .getColumnIndex(Phone.NUMBER)));
                        mBuffer.append("\n");
                        mBuffer.append("\n");
                    }// end while
                    textView.setText(mBuffer.toString());

                }// end if(mCursor.getCount()>0)
                mCursor.close();
            }// end if(mCursor!=null)
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
            // TODO 這是reset的方法,沒有用到

        }

    };

    @Override
    protected void onResume() {
        mProgressDialog.show();
        LoaderManager mLoaderManager = getLoaderManager();
        // 利用觀察者模式來設計監控loader的方法,其中前兩個參數是判定方法可以由控制項操作LoaderCallbacks的內容
        mLoaderManager.initLoader(1, null, mLoaderCallbacks);
        super.onResume();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        // getMenuInflater().inflate(R.menu.activity_main, menu);
        return false;
    }

}



有空來補,最近在開始學ios,最麻煩的是兩個鍵盤不一樣,操作mac習慣以後換回來windows系統就整個卡卡的。可是mac的eclipse快速鍵又不太熟悉,突然覺得生命太長了可能要睡少一點才能在知識上面有所收穫才是...















沒有留言:

張貼留言

你好,我是小書,如果文章內容有錯誤,或是看到有建議以及任何感想時,歡迎提出分享,我們一起學習一起努力。

追蹤者