編輯:關於Android編程
通信 ,顧名思義,指的就是信息的傳遞或者交換
按目錄索引,你可以學習到
1. 組件間的通信,Activity,fragment,Service, Provider,Receiver
2. 進程間的通信,AIDL
3. 線程間的通信,Handler,AnsycTask,IntentService
4. 多個App間的通信
5. 使用大型開源框架完成組件通信,EventBus,otto
6. 網絡通信基礎篇:Google 課程–AnsycTask+HttpClient
7. 網絡通信提高篇:開源框架Ansyc-Httpclient,okttp,Retrofit
1. 研究對象:Activity,fragment等組件
2. 信息存在形式:Intent,Bundle,靜態變量,全局變量,還是點擊事件,觸摸事件的回調監聽,或者文件形式(Sharepreference,SQLite,File , NetStream) ,本質就是信息源
3. 信息傳遞的形式:網路,回調監聽,線程,Intent,全局Application
4. 相同形式的思路,不會出現第二次,請讀者舉一反三
5. 最後強調研究對象是單一的
Activity 和 Activity
1. 常規方式:Intent Bundle
通過Intent 啟動另一個Activity時,有兩種重載方式:
startActivity(new Intent(),new Bundle()); startActivityForResult(new Intent(),FLAG,new Bundle());從參數列表就可以總結出來,有Intent,和Bundle,可以傳遞8種基本數據類型和可序列化的數據類型,比如字符串和字節數組。提到可序列化,就引發 Intent和Bundle 的局限性了:
Intent Bundle 無法傳遞“不可序列化”的數據,比如Bitmap,InputStream,解決辦法有很多種,最簡單的就是將“不可序列化”的對象,轉換成字節數組,這裡因為主要是講解通信,所以不展開講了。 Intent Bundle 能傳遞的數據大小在40K以內 。PS : 很多人不理解為什麼把Intent和Bundle放在一起談,因為Intent 底層存儲信息的原理也是通過Bundle存儲!
2. 公有靜態變量
比如 public static String flag=“中國”;
使用方式 比如 在其他Activity當中 FirstActivity.flag=“china”; 修改 靜態變量的值
3. 基於物理形式:
比如 File,SQLite,Sharepreference 物理形式
4. 全局變量:
比如Application:Application是與Activity,Service齊名的組件,非常強大,它的特點是全局組件共用,單例形式存在,在其他組件中,我們只需要Context.getApplication()獲得該對象的引用即可
Activity 和Fragment,Service,BrodcastReceiver
,首先都遵循,如何啟動它們,就如何傳遞信息的原則:
1. Activity與Fragment
1. 通過構造函數傳遞 2.獲取Fragment的實例對象
//CustFragment 是自定義的fragment,參數列表也可以自己定義咯,
getSupportFragmentManager().beginTransaction()
.add(new CustFragment(自定義的的參數列表),new String("參數"))
//------------------method two-----------------------
getSupportFragmentManager().findFragmentById(R.id.headlines_fragment);
//------------------method three----------------------
getSupportFragmentManager().findFragmentByTag("HeadLines");
聰明的讀者可能會問Fragment如何與Activity通信類似的問題,這是個好問題,請注意我們的研究的原則是單一目標原則,在這節我研究的是Activity,你的疑惑在後面都會一一解答
2. Activity與Service
Activity啟動Service的兩種方式:
//CustomService 是自定義Service,完成一些後台操作
startService(new Intent(FirstActivity.this,CustomService.class));
bindService(new Intent(FirstActivity.this,CustomService.class)), new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//當前啟動的service 一些數據就會回調回這裡,我們在Activity中操作這些數據即可
get
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
},flags);
從啟動方式就可以看出,通過Bundle對象的形式存儲,通過Intent傳輸,來完成Activity向Service傳遞數據的操作
3. Activity與BroadcastReceiver
啟動廣播的形式也有兩種:
//method one !!!-----------------------------------------------
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
}
},new IntentFilter(),"",new Handler());
//method two !!!-----------------------------------------------
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
}
},new IntentFilter());
關於method one 的第三個參數Handler很多人會很費解
參照registerReceiver中源碼關於該Handler參數的解釋:
Handler identifying the thread that will receive the Intent. If null, the main thread of the process will be used.
定義了一個用於接收Intent的子線程,如果不填或者默認為null,那麼就會在主線程中完成接收Intent的操作
很明顯,Activity與BroadcastReceiver通信時,用的也是Intent傳遞,Bundle存儲
4. 通訊時的同步問題
這裡的同步通訊問題,為下文Fragment通訊作鋪墊,不是這個問題不重要,不值得引起你注意,只是我想把問題放在它最應該出現的位置。
以上只是基礎的傳遞數據的形式,大部分都是靜態的,現在有一種需求,用戶操作Activity,發出了某些指令,比如按下,滑動,觸摸等操作,如何完成這些信息傳遞呢?這就要求同步了。
同步傳遞消息也很簡單,就是調用系統寫好的回調接口
首先我們要知道,用戶 點擊,觸摸 這些行為 也屬於 通信的范疇—點擊和觸摸屬於 信息源;
比如用戶行為進行點擊,那就實現 :
new Button(mCotext).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new ImageView(mCotext).invalidate();
}
});
通過此招提示指定的ImageView:嘿!老兄,你該刷新了
又或者 當用戶 進行觸摸操作,我們需要實現放大縮小平移指定的區域:
new RelativeLayout(mCotext).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
//縮放
v.setScaleX(1f);
v.setScaleY(1f);
//平移
v.setTranslationX(1f);
v.setTranslationY(1f);
v.setTranslationY(1f);
//旋轉
v.setRotation(2f);
v.setRotationX(2f);
v.setRotationY(2f);
v.invalidate();
return true;
}
});
嘿,你看,當用戶進行觸摸操作,我們可以通過回調onTouchListenter來完成“觸摸”這一操作
關於View重繪機制以及優化刷新UI的細節,不屬於本文討論范圍。
1. Fragment 與Activity通信
通過實例對象傳遞
同樣的,在Fragment中 getActivity()可以獲取到它相關聯的 Activity實例,就可以輕松獲取並且修改Activity的數據
2. Fragment 與 多個Fragment通信
首先,兩個Fragment之間不可能直接通信(非正規因素除外),Google官方提出的解決辦法是 通過相關聯的Activity來完成兩個Fragment的通信
只需要記住三步:
1. 定義一個接口:
在讓Fragment關聯Activity之前,可以在Fragment中定義一個接口,然後讓宿主Activity來實現這個接口。接著,在Fragment中捕獲這個接口,並且在onAttach()中 捕獲Activity實例
//只需關注接口是如何定義的,以及onAttack中的實現
public class HeadlinesFragment extends ListFragment {
//定義的接口引用
OnHeadlineSelectedListener mCallback;
// 自定義回調接口,宿主Activity必須要實現它
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// 在這裡只是為了確保Activity實現了我們定義的接口,如果沒有實現,則拋出異常
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
一旦Activity通過OnHeadlineSelectedListener 的實例mCallBack回調 onArticleSelected(),Fragment就可以傳遞信息 給Activity了
例如 下面是 ListFragment的一個回調方法,當用戶點擊了list 中的item,這個Fragment就會通過回調接口向宿主Activity傳遞事件
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// 向Activity傳遞事件信息
mCallback.onArticleSelected(position);
}
2. 在宿主Activity實現這個接口
怎麼實現?很簡單,參考下面代碼:
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// 用戶從從 HeadlinesFragment選中了一個標題
//響應用戶的操作,做一些業務邏輯
}
}
3. 向其他Fragment傳遞信息 (完成通信)
宿主Activity可以通過findFragmentById()向指定的Fragment傳遞信息,宿主Activity可以直接獲取Fragment實例,回調Fragment的公有方法
例如:
宿主Activity 包含了一個Listfragment用來展示條目信息,當每個條目被點擊的時候,我們希望ListFragment向另外一個DetailsFragment傳遞一個信息用來 展示不同的細節
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// 用戶在 HeadlinesFragment中選中了一個item
//在activity中添加新的fragment
ArticleFragment articleFrag = (ArticleFragment)
getSupportFragmentManager().findFragmentById(R.id.article_fragment);
if (articleFrag != null) {
// If article 對象 可以復用, 我們就不需要創建兩遍了
// 回調articleFrag 更新
articleFrag.updateArticleView(position);
} else {
// 創建 Fragment 並為其添加一個參數,用來指定應顯示的文章
ArticleFragment newFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 將 fragment_container View 時中的內容替換為此 Fragment ,
// 然後將該事務添加到返回堆棧,以便用戶可以向後回滾
transaction.replace(R.id.fragment_container, newFragment);
int setTransition=TRANSIT_FRAGMENT_OPEN;
transaction.setTransition(setTransition);
transaction.addToBackStack(null);
// 執行事務
transaction.commit();
}
}
}
下面我寫了一個實例來供大家理解:
各個類的聯系圖:
效果如下:
Fragment通信demo實例
Service 與Activity通信
主要是如何獲得Service實例的問題
總結來說兩步:
public class LocalService extends Service {
// 傳遞給客戶端的Binder
private final IBinder mBinder = new LocalBinder();
//構造Random對象
private final Random mGenerator = new Random();
/**
* 這個類提供給客戶端 ,因為Service總是運行在同一個進程中的
*/
public class LocalBinder extends Binder {
LocalService getService() {
// 當客戶端回調的時候,返回LoacalService實例
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
/**交給客戶端回調的方法 */
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// 綁定 LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// 解綁 service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/**button已經通過 android:onClick (attribute) 設置此方法響應用戶click*/
public void onButtonClick(View v) {
if (mBound) {
// 回調 LocalService的方法.
//因為在主線程中刷新UI,可能會造成線程阻塞,這裡只是為了測試
int num = mService.getRandomNumber();
Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
/**定義通過bindService 回調的Binder */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
//先通過Binder獲得Service的內部類 LoacalBinder
LocalBinder binder = (LocalBinder) service;
// 現在可以獲得service對象了
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
除了這種回調的方式外
還有一種方式 是在Service中 發送廣播,
比如 在Service中 開啟了一個子線程執行任務,就在子線程的run()方法中去sendBroadcast(intent);
數據用Intent封裝,傳遞形式用廣播
關於進程和線程的細節改天詳細說明,我們首先了解一下進程和線程的概念:
當某個應用組件啟動且該應用沒有運行其他任何組件時,Android 系統會使用單個執行線程為應用啟動新的 Linux
進程。默認情況下,同一應用的所有組件在相同的進程和線程(稱為“主”線程)中運行。
如果某個應用組件啟動且該應用已存在進程(因為存在該應用的其他組件),則該組件會在此進程內啟動並使用相同的執行線程。
但是,我們也可以安排應用中的其他組件在單獨的進程中運行,並為任何進程創建額外的線程。各類組件元素的清單文件條目—:activity,servicer,eceiver 和 provider均支持 android:process 屬性,此屬性可以指定該組件應在哪個進程運行。我們可以設置此屬性,使每個組件均在各自的進程中運行,或者使一些組件共享一個進程,而其他組件則不共享。 此外,我們還可以設置 android:process,使不同應用的組件在相同的進程中運行
以及了解一下 進程間通信的概念
Android 利用遠程過程調用 (RPC) 提供了一種進程間通信 (IPC) 機制,通過這種機制,由 Activity
或其他應用組件調用的方法將(在其他進程中)遠程執行,而所有結果將返回給調用方。這就要求把方法調用及其數據分解至操作系統可以識別的程度,並將其從本地進程和地址空間傳輸至遠程進程和地址空間,然後在遠程進程中重新組裝並執行該調用。
然後,返回值將沿相反方向傳輸回來。 Android 提供了執行這些 IPC 事務所需的全部代碼,因此我們只需集中精力定義和實現 RPC
編程接口即可。要執行 IPC,必須使用 bindService() 將應用綁定到服務上。
具體實現 可以 參考這個實例 和文末給出的官方文檔
Handler 和AsyncTask都是用來完成子線程和主線程即UI線程通信的
都可以解決主線程 處理耗時操作,造成界面卡頓或者程序無響應ANR異常 這一類問題
Handler 是 一種機制【Handler+Message+Looper】,所有的數據通過Message攜帶,,所有的執行順序按照隊列的形式執行,Looper用來輪詢判斷消息隊列,Handler用來接收和發送Message
AsyncTask 是一個單獨的類,設計之初的目的只是為了 異步方式完成耗時操作的,順便可以通知主線程刷新Ui,AsyncTask的內部機制則是維護了一個線程池,提升性能。
在這裡提供另一種優雅的做法完成線程間的通信:
擴展 IntentService 類
由於大多數啟動服務都不必同時處理多個請求(實際上,這種多線程情況可能很危險),因此使用 IntentService 類實現服務值得一試。
但如需同時處理多個啟動請求,則更適合使用該基類Service。
IntentService 執行以下操作:
創建默認的工作線程,用於在應用的主線程外執行傳遞給 onStartCommand() 的所有 Intent。 創建工作隊列,用於將一個 Intent 逐一傳遞給 onHandleIntent() 實現,這樣我們就永遠不必擔心多線程問題。 在處理完所有啟動請求後停止服務,因此我們不必調用 stopSelf()。 提供 onBind() 的默認實現(返回 null)。 提供 onStartCommand() 的默認實現,可將 Intent 依次發送到工作隊列和 onHandleIntent() 實現。以下是 IntentService 的實現示例:
public class HelloIntentService extends IntentService {
/**
* 必須有構造函數 必須調用父 IntentService(String)帶有name的構造函數來執行工作線程
*/
public HelloIntentService() {
super("HelloIntentService");
}
/**
* IntentService 調用默認的工作線程啟動服務
* 當此方法結束,, IntentService 服務結束
*/
@Override
protected void onHandleIntent(Intent intent) {
// 通常在這裡會執行一些操作,比如下載文件
//在這裡只是sleep 5 s
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
}
}
看吧,我們只需要一個構造函數和一個 onHandleIntent() 實現即可。
對於Service 當然也有基礎一點的做法,來完成多線程的操作,只不過代碼量更多了:
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
// Handler 接收來自主線程的Message
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//執行任務,比如下載什麼的,這裡只是 讓線程sleep
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// 手動停止服務,來處理下一個線程
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
//啟動線程. 注意我們在主線程中創建了一些子線程, 這些線程都沒有加鎖同步. 這些現場都是後台線程,所以不會阻塞UI線程
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Handler開始輪詢遍歷了
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// 每一次請求,都會通過handler發送Message
// startID只是為了讓我們知道正在進行的是哪一個線程,以便於我們停止服務
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
// 不提供 binding, 所以返回空
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
首先我們要知道以下兩點:
Android 應用一般具有若干個Activity。每個Activity顯示一個用戶界面,用戶可通過該界面執行特定任務(比如,查看地圖或拍照)。要將用戶從一個Activity轉至另一Activity,應用必須使用 Intent 定義做某事的“意向”。 當我們使用諸如 startActivity() 的方法將 Intent 傳遞至系統時,系統會使用 Intent 識別和啟動相應的應用組件。使用意向甚至可以讓我們的應用開始另一個應用中包含的Activity。
Intent 可以為 顯式 以便啟動特定組件(特定的 Activity 實例)或隱式 以便啟動處理意向操作(比如“拍攝照片”)的任何組件。
1.向另一個應用發送用戶
Android最重要的功能之一,是可以操作其他應用,比如在我們的應用中,需要使用地圖顯示公司地址,我們無序在地圖應用程序中構建Activity,而是直接創建Intent查看 地址的請求,Android系統之後啟動 可以在地圖上顯示 地址的應用。
1) 構建隱式的意圖
隱式意圖不用聲明要啟動的組件類名稱,而是聲明操作,比如查看,編輯,發送,或者獲取某項。
如果您我們的數據是Uri,可以這樣構建Intent:
//當我們的應用通過startActivity()調用此Intent時,電話應用會發起向指定電話號碼呼叫
Uri number = Uri.parse("tel:5551234");
Intent callIntent = new Intent(Intent.ACTION_DIAL, number);
這裡還有一些其他Intent的操作和Uri數據對:
查看地圖:
// 基於地址的地圖位置
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
// 基於經緯度的地圖位置
// Uri location = Uri.parse("geo:37.422219,-122.08364?z=14"); // z param is zoom level
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
查看網頁:
Uri webpage = Uri.parse("http://www.android.com");
Intent webIntent = new Intent(Intent.ACTION_VIEW, webpage);
有的同學會問了,我從哪裡可以知道,Intent可以傳遞的 Uri的類型,或者其他數據類型呢?
答:可以查閱Google Intent的Api
2) 確認是否存在 接收意向的應用
注意:如果調用了意向,但設備上沒有可用於處理意向的應用,我們的應用將崩潰。
要確認是否存在可響應意向的可用Activity,請調用 queryIntentActivities() 來獲取能夠處理ntent 的Activity列表。 如果返回的 List 不為空,則可以安全地使用該意向。例如:
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
如果 isIntentSafe 是 true,則至少有一個應用將響應該意向。 如果它是 false,則沒有任何應用處理該意向。
3) 啟動指定Activity
當我指定意圖後,通過startActivity(intent);就可以啟動指定Activity
此處有一個Google官方的示例:
// 構建Intent
Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
// 確定意圖可以被接收
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(mapIntent, 0);
boolean isIntentSafe = activities.size() > 0;
//啟動指定應用
if (isIntentSafe) {
startActivity(mapIntent);
}
顯
4) 顯示應用選擇器
比如我們要完成”分享操作“,用戶可以使用多個App完成分享,我們應明確顯示 選擇器對話框,如圖這裡寫鏈接內容
要顯示選擇器,需要使用Intent的createChooser()方法 創建Intent,並將其傳遞至startActivity()
Intent intent = new Intent(Intent.ACTION_SEND);
...
String title = getResources().getString(R.string.chooser_title);
// Create intent to show chooser
Intent chooser = Intent.createChooser(intent, title);
// Verify the intent will resolve to at least one activity
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
這將顯示一個對話框,其中有響應傳遞給 createChooser() 方法的意向的應用列表,並且將提供的文本用作 對話框標題
2. 接收其他Activity返回的結果
通過Intent.startActivityForResult()來完成。
首先在啟動另一個Activity時,我們需要指定request code以便返回結果時,我們可以正常處理它。
static final int PICK_CONTACT_REQUEST = 1; // The request code
...
private void pickContact() {
Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
pickContactIntent.setType(Phone.CONTENT_TYPE);
startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
}
當用戶完成操作後,返回數據,系統會調用Activity的 onActivityResult()方法,
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 檢查requestCode是否真確
if (requestCode == PICK_CONTACT_REQUEST) {
// 確保請求時成功的
if (resultCode == RESULT_OK) {
//完成我們的業務邏輯
}
}
}
為了成功處理結果,我們必須了解Intent的格式,比如聯系人返回的是帶內容的URI,照相機返回的是Bitmap
如何根據返回的URI來讀取數據,我們需要對ContentResolver 和 ContentProvider 有了解
下面就是一個三者結合的獲取聯系人的實例:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// 檢查requestCode
if (requestCode == PICK_CONTACT_REQUEST) {
// 確保請求成功
if (resultCode == RESULT_OK) {
//獲得選擇的聯系人的URI
Uri contactUri = data.getData();
// 我們只需要NUMBER這一列的信息,
String[] projection = {Phone.NUMBER};
// 顯示根據NUMBER查詢的結果
// We don't need a selection or sort order (there's only one result for the given URI)
// 在這裡我們並沒有對查詢的結果進行排序,因為在主線程中進行這種數據庫操作,有可能阻塞線程
//優化方案是異步完成排序的操作,這裡只是展示多個App間的通信
Cursor cursor = getContentResolver()
.query(contactUri, projection, null, null, null);
cursor.moveToFirst();
//從NUMBER那一列當中取回phone NUMBER
int column = cursor.getColumnIndex(Phone.NUMBER);
String number = cursor.getString(column);
//接下來就是要操作這些phone number了
}
}
}
3. 接收其他Activity返回的結果
要允許其他應用開始您的Activity,需要 在相應元素的宣示說明文件中添加一個 元素。
例如,此處有一個在數據類型為文本或圖像時處理 ACTION_SEND 意向的意向過濾器:
定義操作,通常是系統定義的值之一,比如ACTION_SEND 或 ACTION_VIEW。
定義與Intent關聯的數據,只需通過 android:mimeType 指定我們接收的數據類型,比如text/plain 或 image/jpeg。
所有的隱式Intent,都使用 CATEGORY_DEFAULT 進行定義
4. 處理Activity中的Intent
當Activity開始時,調用getIntent檢索開始Activity的Intent,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent = getIntent();
Uri data = intent.getData();
// 指出接收的數據類型
if (intent.getType().indexOf("image/") != -1) {
// 處理帶有圖片的Intent
} else if (intent.getType().equals("text/plain")) {
// 處理帶有文本的Intent
}
}
5. 向指定Activity中返回數據
只需調用setResult指定結果代碼和Intent
Intent result = new Intent("com.example.RESULT_ACTION", Uri.parse("content://result_uri");
setResult(Activity.RESULT_OK, result);
finish();
記住必須為結果指定結果碼,通常為 RESULT_OK 或 RESULT_CANCELED。
我們也可以在Intent中 用Bundle存儲額外的信息
細心的同學可能發現一個問題:
啟動Activity 有startActivity() 和startActivityForResult() 兩種啟動方式,返回結果的形式id偶有setResult()嗎?
如果開啟當前Activity的Intent可能需要結果,只需調用 setResult()。 如果原始Activity已調用 startActivityForResult(),則系統將向其傳遞您提供給 setResult() 的結果;否則,會忽略結果。
Github上非常火的兩大通信組件EventBus和otto:
1. EventBus
EventBus 是一個 Android 事件發布/訂閱框架,通過解耦發布者和訂閱者簡化 Android 事件傳遞,這裡的事件可以理解為消息,本文中統一稱為事件。事件傳遞既可用於 Android 四大組件間通訊,也可以用戶異步線程和主線程間通訊等等。
傳統的事件傳遞方式包括:Handler、BroadCastReceiver、Interface 回調,相比之下 EventBus 的優點是代碼簡潔,使用簡單,並將事件發布和訂閱充分解耦。
1)概念:
事件(Event):又可稱為消息,本文中統一用事件表示。其實就是一個對象,可以是網絡請求返回的字符串,也可以是某個開關狀態等等。事件類型(EventType)指事件所屬的 Class。
事件分為一般事件和 Sticky 事件,相對於一般事件,Sticky 事件不同之處在於,當事件發布後,再有訂閱者開始訂閱該類型事件,依然能收到該類型事件最近一個 Sticky 事件。
訂閱者(Subscriber):訂閱某種事件類型的對象。當有發布者發布這類事件後,EventBus 會執行訂閱者的 onEvent 函數,這個函數叫事件響應函數。訂閱者通過 register 接口訂閱某個事件類型,unregister 接口退訂。訂閱者存在優先級,優先級高的訂閱者可以取消事件繼續向優先級低的訂閱者分發,默認所有訂閱者優先級都為 0。
發布者(Publisher):發布某事件的對象,通過 post 接口發布事件。
本項目較為簡單,總體設計和流程圖:
使用方式:
build.gradle 中加入依賴
compile 'org.greenrobot:eventbus:3.0.0'
代碼中指需三步
定義事件:只需要是一個Java類public MessageEvent(String message) {
this.message = message;
}
}
//MessageEvent被Eventbus post提交的時候 將會回調這個方法
//這種方式 提示我們可以直接定義自己的事件
@Subscribe
public void onMessageEvent(MessageEvent event){
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// 當一些其他事件post提交的時候,回調這個方法
@Subscribe
public void handleSomethingElse(SomeOtherEvent event){
doSomethingWith(event);
在Activity或者Fragment中綁定訂閱者
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
發布事件:
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
android 的多線程實際上就是java的多線程。android的UI線程又稱為主線程。 首先是Thread 和 Runnable: Thread才是一個線程,而Run
過去的兩天,在項目中,拋棄了ListView, 想試一試RecyclerView, 在用的過程中,遇到了一些問題,比如:如何為RecyclerView添加Header和F
1,代理模式介紹代理模式(Proxy Pattern)也稱為委托模式,其實代理模式在我們生活中非常常見,對於我們這些程序員來說最常接觸的莫過於代理上網了,連上代理服務器地
JNI層方法命名規范在java層對應的native方法的聲明,test方法是一個本地方法,其參數是字符串類型,返回值是字符串類型。package com.kltz88.j