Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 詳解 Android 通信

詳解 Android 通信

編輯:關於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 和 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的細節,不屬於本文討論范圍。

Fragment

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

Service 與Activity通信

主要是如何獲得Service實例的問題
總結來說兩步:

在Service定義內部類,繼承Binder,封裝Service作為內部類的屬性,並且在onBind方法中返回內部類的實例對象 在Activity中實現ServiceConnection ,獲取到Binder對象,再通過Binder獲取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封裝,傳遞形式用廣播

AIDL完成進程間通信


關於進程和線程的細節改天詳細說明,我們首先了解一下進程和線程的概念:

當某個應用組件啟動且該應用沒有運行其他任何組件時,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() 實現。
綜上所述,您只需實現 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();
  }
}

多個App間的通信

首先我們要知道以下兩點:

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 class MessageEvent {
public final String message;

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!"));

很遺憾未完成的部分

網絡通信基礎篇:Google 課程–AnsycTask+HttpClient 網絡通信提高篇:開源框架Ansyc-Httpclient,okttp,Retrofit

本文參考並翻譯

Google 課程 Communicating with Other Fragments Google 解釋 AIDL進程間通信 Google 解釋 Handler Google 解釋 AsyncTask Google BroadcastReceiver API Google 課程 Interacting with Other Apps Google 解釋 contentprovider Google BroadcastReceiver 課程 Google Service 課程 Google 解釋 進程和線程 EventBus官方文檔
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved